diff --git a/.agents/.style.mdformat b/.agents/.style.mdformat new file mode 100644 index 00000000000..e69de29bb2d diff --git a/.agents/skills/gn-check-autofix/SKILL.md b/.agents/skills/gn-check-autofix/SKILL.md new file mode 100644 index 00000000000..67943ae1881 --- /dev/null +++ b/.agents/skills/gn-check-autofix/SKILL.md @@ -0,0 +1,72 @@ +--- +name: gn-check-autofix +description: Automatically fix GN check errors in WebRTC BUILD.gn files. Use when encountering "Include not allowed", "rtc_source_set shall not contain cc files", or when needing to clean up non-absolute dependencies. +--- + +# GN Check Autofix + +This skill provides instructions for using `tools_webrtc/gn_check_autofix.py` to +automatically resolve common GN build configuration errors in WebRTC. + +## Core Workflows + +### Fix Missing Dependencies + +When you see "Include not allowed" errors from `gn gen --check`, use this +workflow: + +1. Run the autofix tool on your output directory: + ```bash + tools_webrtc/gn_check_autofix.py -C + ``` +2. The tool will: + - Identify targets missing dependencies. + - Add the missing `deps` to the appropriate `BUILD.gn` files. + - Automatically run `gn format` on modified files. + +### Fix rtc_source_set Violations + +If you see "rtc_source_set shall not contain cc files", the tool will +automatically convert them to `rtc_library`: + +1. Run the tool: + ```bash + tools_webrtc/gn_check_autofix.py -C + ``` + +### Clean Up Dependencies + +To remove all non-absolute dependencies (those not starting with `//`) from +specific `BUILD.gn` files: + +```bash +tools_webrtc/gn_check_autofix.py -r path/to/BUILD.gn +``` + +*Note: This preserves absolute dependencies (starting with `//`) and targets +ending in `_test`, `_tests`, `_unittest`, or `_unittests`.* + +## Integration with Include Cleaner + +This tool is the recommended second step after running `webrtc-include-cleaner`. +While the include cleaner updates your C++ source files, `gn-check-autofix` +synchronizes your `BUILD.gn` files to match. + +## Parameters + +- `-C `: Path to a local build directory (e.g., `out/Default`). The tool + internally runs `gn gen --check --error-limit=20000`. +- `-r `: Remove all non-absolute dependencies from the specified files. +- `--error-limit`: Can be used to override the default error cap. + +## Post-Fix Steps + +After the tool runs, always verify the changes: + +1. **Deduplicate**: The tool may occasionally add a dependency that is already + present. Check the `deps` list for duplicates. +2. **Regenerate GN**: Run `gn gen ` to confirm errors are resolved. +3. **Format**: The tool runs `gn format`, but a final `git cl format` is + recommended for consistency. +4. **Review**: Check the diff to ensure dependencies were added to the correct + targets. diff --git a/.agents/skills/gtest-parallel/SKILL.md b/.agents/skills/gtest-parallel/SKILL.md new file mode 100644 index 00000000000..f1e8b32d997 --- /dev/null +++ b/.agents/skills/gtest-parallel/SKILL.md @@ -0,0 +1,60 @@ +--- +name: gtest-parallel +description: Run Google Test binaries in parallel using the gtest-parallel script. Use when needing to speed up test execution, run flaky tests with repeat, or filter specific tests. +--- + +# gtest-parallel + +`gtest-parallel` is a script that executes Google Test binaries in parallel, +providing speedup for single-threaded tests and tests that do not run at 100% +CPU. + +## Location + +The script is located at `third_party/gtest-parallel/gtest-parallel`. + +## Core Flags + +### Filtering Tests + +Use `--gtest_filter` to run a select set of tests. It supports the same syntax +as Google Test (including exclusion with `-`). + +```bash +third_party/gtest-parallel/gtest-parallel path/to/binary --gtest_filter=Foo.*:Bar.* +``` + +### Timeouts + +- `--timeout=TIMEOUT`: Interrupt all remaining processes after the given time + (in seconds). +- `--timeout_per_test=TIMEOUT_PER_TEST`: Interrupt single processes after the + given time (in seconds). + +### Output and Logging + +- `-d OUTPUT_DIR`, `--output_dir=OUTPUT_DIR`: Output directory for test logs. + Logs will be available under `gtest-parallel-logs/` inside the specified + directory. +- `--dump_json_test_results=DUMP_JSON_TEST_RESULTS`: Saves the results of the + tests as a JSON machine-readable file. + +## Advanced Usage + +### Repeating Tests (Flakiness Testing) + +Use `--repeat=N` to run tests multiple times. + +```bash +third_party/gtest-parallel/gtest-parallel path/to/binary --repeat=1000 +``` + +### Workers + +Use `-w WORKERS` or `--workers=WORKERS` to specify the number of parallel +workers (defaults to the number of cores). + +### Serializing Test Cases + +Use `--serialize_test_cases` to run tests within the same test case sequentially +(useful if they share resources). diff --git a/.agents/skills/include-cleaner/SKILL.md b/.agents/skills/include-cleaner/SKILL.md new file mode 100644 index 00000000000..d664a81e64c --- /dev/null +++ b/.agents/skills/include-cleaner/SKILL.md @@ -0,0 +1,67 @@ +--- +name: webrtc-include-cleaner +description: Runs the WebRTC include-cleaner tool (IWYU replacement) to fix headers in C++ files. Use when preparing a CL for upload, after modifying .cc or .h files, or when instructed to fix include regressions. +--- + +# WebRTC Include Cleaner + +This skill provides instructions for using +`tools_webrtc/iwyu/apply-include-cleaner`, a tool that automatically manages C++ +`#include` directives in the WebRTC codebase. It ensures that every header used +is explicitly included and that unused headers are removed. + +## When to Use + +- **Pre-upload**: Run this tool before uploading a CL to ensure clean includes. +- **After refactoring**: When moving code or changing dependencies, use this to + update `#include` blocks. +- **Fixing regressions**: Use this if a presubmit or bot identifies + include-related issues. + +## Basic Usage + +To run the include cleaner on specific files: + +```bash +tools_webrtc/iwyu/apply-include-cleaner path/to/file.cc path/to/file.h +``` + +To run it on all modified files relative to the upstream branch (ideal for CL +preparation): + +```bash +tools_webrtc/iwyu/apply-include-cleaner +``` + +Note: This is as expensive as a build for each file, so use it sparingly. + +## Options + +- `-p`, `--print`: Don't modify the files, just print the proposed changes. +- `-w WORK_DIR`, `--work-dir WORK_DIR`: Specify the GN work directory (default: + `out/Default`). + +## Post-Execution Steps + +After running the include cleaner, it is recommended to perform the following +steps to ensure build and style consistency: + +1. **Check for build errors**: The tool might occasionally make mistakes. Run a + build to verify. +1. **Fix GN dependencies**: Use `tools_webrtc/gn_check_autofix.py` to fix any + `deps` issues caused by include changes. + ```bash + tools_webrtc/gn_check_autofix.py -C out/Default + ``` +1. **Format code**: Run `git cl format` to fix any formatting issues in the + `#include` blocks. + ```bash + git cl format + ``` + +## Prerequisites + +- The tool automatically generates `compile_commands.json` in the output + directory if `out/Default` exists. +- `clangd` must be checked out in your `.gclient` file + (`"checkout_clangd": True`). diff --git a/.github/workflows/manual-platform-tests.yml b/.github/workflows/manual-platform-tests.yml index cbf8cae2de8..b44a214ff3b 100644 --- a/.github/workflows/manual-platform-tests.yml +++ b/.github/workflows/manual-platform-tests.yml @@ -312,6 +312,12 @@ jobs: - name: Build iOS SDK artifact working-directory: release-pipeline + # M148's build config adds the lld-only flag --ignore-auto-link-option + # when Xcode >= 26.4, but the pipeline links with Apple ld (use_lld=false + # for Mac Catalyst), which rejects it. Pin Xcode 26.3 until the Catalyst + # slices can link with lld again. + env: + DEVELOPER_DIR: /Applications/Xcode_26.3.app run: >- bundle exec fastlane ios build "root:${{ github.workspace }}/webrtc-tree/.output/src" @@ -443,12 +449,13 @@ jobs: while IFS= read -r framework; do command+=(-framework "${framework}") if [[ "${ENABLE_DEBUG}" == "true" ]]; then - mapfile -t dsyms < <(find "$(dirname "${framework}")" -name "$(basename "${framework}").dSYM" -type d | sort) - if [[ "${#dsyms[@]}" -eq 0 ]]; then + # macOS runners use bash 3.2, which lacks mapfile. + dsym="$(find "$(dirname "${framework}")" -name "$(basename "${framework}").dSYM" -type d | sort | head -n 1)" + if [[ -z "${dsym}" ]]; then echo "::warning::Missing dSYM for ${framework}" continue fi - command+=(-debug-symbols "${dsyms[0]}") + command+=(-debug-symbols "${dsym}") fi done < <(find apple-inputs -name '*.framework' -type d | sort) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d74465e6682..f9ed90c1eb8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -292,6 +292,12 @@ jobs: - name: Build iOS SDK artifact working-directory: release-pipeline + # M148's build config adds the lld-only flag --ignore-auto-link-option + # when Xcode >= 26.4, but the pipeline links with Apple ld (use_lld=false + # for Mac Catalyst), which rejects it. Pin Xcode 26.3 until the Catalyst + # slices can link with lld again. + env: + DEVELOPER_DIR: /Applications/Xcode_26.3.app run: >- bundle exec fastlane ios build "root:${{ github.workspace }}/webrtc-tree/.output/src" @@ -418,12 +424,13 @@ jobs: while IFS= read -r framework; do command+=(-framework "${framework}") if [[ "${ENABLE_DEBUG}" == "true" ]]; then - mapfile -t dsyms < <(find "$(dirname "${framework}")" -name "$(basename "${framework}").dSYM" -type d | sort) - if [[ "${#dsyms[@]}" -eq 0 ]]; then + # macOS runners use bash 3.2, which lacks mapfile. + dsym="$(find "$(dirname "${framework}")" -name "$(basename "${framework}").dSYM" -type d | sort | head -n 1)" + if [[ -z "${dsym}" ]]; then echo "::warning::Missing dSYM for ${framework}" continue fi - command+=(-debug-symbols "${dsyms[0]}") + command+=(-debug-symbols "${dsym}") fi done < <(find apple-inputs -name '*.framework' -type d | sort) diff --git a/.gitignore b/.gitignore index 9b7a2aa2255..8c83bf78e9e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,11 +34,13 @@ .cproject .gdb_history .gdbinit +.gemini .landmines .metadata .project .pydevproject .settings +.siso_failed_targets .sw? /Makefile /base diff --git a/.gn b/.gn index 3b92d0ad12f..4706638e3ea 100644 --- a/.gn +++ b/.gn @@ -12,8 +12,8 @@ import("//build/dotfile_settings.gni") buildconfig = "//build/config/BUILDCONFIG.gn" # The python interpreter to use by default. On Windows, this will look -# for vpython3.exe and vpython3.bat. -script_executable = "vpython3" +# for python3.exe and python3.bat. +script_executable = "python3" # The secondary source root is a parallel directory tree where # GN build files are placed when they can not be placed directly @@ -95,6 +95,8 @@ default_args = { # Use Siso instead of Ninja. use_siso = true + clang_unsafe_buffers_paths = "//unsafe_buffers_paths.txt" + # WebRTC must stay in C++20 for longer compared to Chromium. use_cxx23 = false } diff --git a/AUTHORS b/AUTHORS index 8b8798fe84b..e9f6f9b90cd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -168,6 +168,7 @@ Hailin Zhao Fizz Fang Sai Xu Shunyang Zhang +Suresh Jain # END individuals section. # BEGIN organizations section. @@ -188,6 +189,7 @@ Hopin Ltd. <*@hopin.to> HyperConnect Inc. <*@hpcnt.com> Igalia S.L. <*@igalia.com> Intel Corporation <*@intel.com> +Island Technology, Inc. <*@island.io> LG Electronics, Inc. <*@lge.com> Life On Air Inc. <*@lifeonair.com> LiveKit <*@livekit.io> @@ -223,4 +225,5 @@ Vonage Holdings Corp. <*@vonage.com> Wang Qing Wire Swiss GmbH <*@wire.com> &yet LLC <*@andyet.com> +Ahmad Yar # END organizations section. diff --git a/BUILD.gn b/BUILD.gn index 61fec180a6e..4d154f8e193 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -47,6 +47,7 @@ if (!build_with_chromium) { } if (rtc_include_tests) { deps += [ + ":audio_engine_tests", ":rtc_p2p_unittests", ":rtc_unittests", ":video_engine_tests", @@ -177,6 +178,10 @@ config("common_inherited_config") { defines += [ "RTC_ENABLE_WIN_WGC" ] } + if (deprecate_plan_b) { + defines += [ "WEBRTC_DEPRECATE_PLAN_B" ] + } + if (!rtc_use_perfetto) { # Some tests need to declare their own trace event handlers. If this define is # not set, the first time TRACE_EVENT_* is called it will store the return @@ -274,33 +279,6 @@ config("rtc_prod_config") { } } -group("tracing") { - all_dependent_configs = [ "//third_party/perfetto/gn:public_config" ] - if (rtc_use_perfetto) { - if (build_with_chromium) { - public_deps = # no-presubmit-check TODO(webrtc:8603) - [ "//third_party/perfetto:libperfetto" ] - } else { - public_deps = [ # no-presubmit-check TODO(webrtc:8603) - ":webrtc_libperfetto", - "//third_party/perfetto/include/perfetto/tracing", - ] - } - } else { - public_deps = # no-presubmit-check TODO(webrtc:8603) - [ "//third_party/perfetto/include/perfetto/tracing" ] - } -} - -if (rtc_use_perfetto) { - rtc_library("webrtc_libperfetto") { - deps = [ - "//third_party/perfetto/src/tracing:client_api_without_backends", - "//third_party/perfetto/src/tracing:platform_impl", - ] - } -} - config("common_config") { cflags = [] cflags_c = [] @@ -346,10 +324,6 @@ config("common_config") { defines += [ "WEBRTC_HAVE_SCTP" ] } - if (rtc_enable_external_auth) { - defines += [ "ENABLE_EXTERNAL_AUTH" ] - } - if (rtc_use_h264) { defines += [ "WEBRTC_USE_H264" ] } @@ -565,7 +539,6 @@ if (!build_with_chromium) { "modules/video_capture:video_capture_internal_impl", "pc:libjingle_peerconnection", "pc:rtc_pc", - "sdk", "video", ] @@ -590,6 +563,14 @@ if (!build_with_chromium) { if (rtc_include_internal_audio_device) { deps += [ "api/audio:create_audio_device_module" ] } + if (!build_with_chromium) { + if (is_android) { + deps += [ "sdk/android" ] + } + if (is_ios) { + deps += [ "sdk:framework_objc" ] + } + } } # TODO(bugs.webrtc.org/430260876): Compile webrtc lib with rust once toolchain @@ -646,6 +627,7 @@ if (rtc_include_tests && !build_with_chromium) { "api/task_queue:pending_task_safety_flag_unittests", "api/test/metrics:metrics_unittests", "api/test/network_emulation:network_queue_unittests", + "api/transport:ecn_marking_unittest", "api/transport:stun_unittest", "api/transport/rtp:corruption_detection_message_unittest", "api/video/test:rtc_api_video_unittests", @@ -712,19 +694,27 @@ if (rtc_include_tests && !build_with_chromium) { rtc_executable("benchmarks") { testonly = true deps = [ - "rtc_base:base64_benchmark_temp", + "rtc_base:base64_benchmark", "rtc_base/synchronization:mutex_benchmark", "test:benchmark_main", ] } } + rtc_test("audio_engine_tests") { + testonly = true + deps = [ "audio:audio_tests" ] + + if (is_android) { + use_default_launcher = false + deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java" ] + } + } + # TODO(pbos): Rename test suite, this is no longer "just" for video targets. rtc_test("video_engine_tests") { testonly = true deps = [ - "audio:audio_tests", - # TODO(eladalon): call_tests aren't actually video-specific, so we # should move them to a more appropriate test suite. "call:call_tests", diff --git a/DEPS b/DEPS index 83e5f8c59c8..56b4be5b957 100644 --- a/DEPS +++ b/DEPS @@ -10,7 +10,7 @@ vars = { # chromium waterfalls. More info at: crbug.com/570091. 'checkout_configuration': 'default', 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"', - 'chromium_revision': '86cd6c0b05db1dcde3a25d7a9f9a87003fc5bf73', + 'chromium_revision': 'a3f5fcb392f2902650ca2b71820e7e418787e18b', # Fetch the prebuilt binaries for llvm-cov and llvm-profdata. Needed to # process the raw profiles produced by instrumented targets (built with @@ -33,7 +33,7 @@ vars = { # By default, download the fuchsia sdk from the public sdk directory. 'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/core/', - 'fuchsia_version': 'version:30.20251218.4.1', + 'fuchsia_version': 'version:31.20260204.7.1', # By default, download the fuchsia images from the fuchsia GCS bucket. 'fuchsia_images_bucket': 'fuchsia', 'checkout_fuchsia': False, @@ -50,7 +50,7 @@ vars = { # reclient CIPD package version 'reclient_version': 're_client_version:0.185.0.db415f21-gomaip', # siso CIPD package version. - 'siso_version': 'git_revision:1624786919608fb2140226f6468cd8d0b52fe3b5', + 'siso_version': 'git_revision:87bad442ede1c60700dfabef5862c4a584621734', # ninja CIPD package. 'ninja_package': 'infra/3pp/tools/ninja/', @@ -69,28 +69,28 @@ vars = { deps = { 'src/build': - 'https://chromium.googlesource.com/chromium/src/build@75489df9cb57087efa5ef49f5ceb2f29ce302632', + 'https://chromium.googlesource.com/chromium/src/build@dd54dd5186566a13bda647123c22540666b12ace', 'src/buildtools': - 'https://chromium.googlesource.com/chromium/src/buildtools@4dc32b3f510b330137385e2b3a631ca8e13a8e22', + 'https://chromium.googlesource.com/chromium/src/buildtools@95ed44cf5f06dbb5861030b91c9db9ccb4316762', # 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@b4ba4e6585ff9c478d1f3276b096ca269dc47589', + 'url': 'https://chromium.googlesource.com/chromium/src/ios@b3b1914b7bd50a64ec13fabaf5e42edcf22e99b8', 'condition': 'checkout_ios', }, 'src/testing': - 'https://chromium.googlesource.com/chromium/src/testing@abe0cb4162303126aa88fb7e54d27198f6f04f2c', + 'https://chromium.googlesource.com/chromium/src/testing@629b7bb6055714e23d8125bf790cfc8d94a94159', 'src/third_party': - 'https://chromium.googlesource.com/chromium/src/third_party@ae7dba2f6ba96a03becead08e8ed53c88691dce1', + 'https://chromium.googlesource.com/chromium/src/third_party@f54c8ff58874b977b8ba20c146dd312d2102168e', 'src/buildtools/linux64': { 'packages': [ { 'package': 'gn/gn/linux-${{arch}}', - 'version': 'git_revision:5550ba0f4053c3cbb0bff3d60ded9d867b6fa371', + 'version': 'git_revision:b2ac0e7a9089039e62b84d246eca83f84c540f76', } ], 'dep_type': 'cipd', @@ -100,7 +100,7 @@ deps = { 'packages': [ { 'package': 'gn/gn/mac-${{arch}}', - 'version': 'git_revision:5550ba0f4053c3cbb0bff3d60ded9d867b6fa371', + 'version': 'git_revision:b2ac0e7a9089039e62b84d246eca83f84c540f76', } ], 'dep_type': 'cipd', @@ -110,7 +110,7 @@ deps = { 'packages': [ { 'package': 'gn/gn/windows-amd64', - 'version': 'git_revision:5550ba0f4053c3cbb0bff3d60ded9d867b6fa371', + 'version': 'git_revision:b2ac0e7a9089039e62b84d246eca83f84c540f76', } ], 'dep_type': 'cipd', @@ -136,157 +136,157 @@ deps = { 'objects': [ { # The Android libclang_rt.builtins libraries are currently only included in the Linux clang package. - 'object_name': 'Linux_x64/clang-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': 'a2d632dfbd997b1c545c4ab858c664e33b55bf6423b58793ed9eb42c8d2a8249', - 'size_bytes': 57165612, - 'generation': 1765411203931092, + 'object_name': 'Linux_x64/clang-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '750b331006635281d7d90696629f67db748ba62004c46675eccb8af144141847', + 'size_bytes': 58029996, + 'generation': 1772218390302503, 'condition': '(host_os == "linux" or checkout_android) and non_git_source', }, { - 'object_name': 'Linux_x64/clang-tidy-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': 'eccc0770ce912c2da813856b5f83b729e26a71cea99c63dc5ae63e92fc3cfd53', - 'size_bytes': 14313444, - 'generation': 1765411203943205, + 'object_name': 'Linux_x64/clang-tidy-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': 'd53439bb6ac13c8d2c30c20555ded434039802f70d4119c0138bd77d03552223', + 'size_bytes': 14392856, + 'generation': 1772218390323510, 'condition': 'host_os == "linux" and checkout_clang_tidy and non_git_source', }, { - 'object_name': 'Linux_x64/clangd-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': 'd5d2c507783f551eb8ce24f19610233df1af799a2c4ae7ff64a843d9d27104d4', - 'size_bytes': 14517932, - 'generation': 1765411203940105, + 'object_name': 'Linux_x64/clangd-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': 'a24613fb7afce42c076bb95d1b671ac028746b379e88070c126f0aab17a4c34e', + 'size_bytes': 14635272, + 'generation': 1772218390330947, 'condition': 'host_os == "linux" and checkout_clangd and non_git_source', }, { - 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': 'a29fc6b6e582df4ce0a2178bbc8225e01b6446d3788b89932765558523e4de4a', - 'size_bytes': 2307836, - 'generation': 1765411203990197, + 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '8dcd816a83361b7924093ccba92dfe6bd29af2cf8af58bf7ce785b38c5027a8b', + 'size_bytes': 2328908, + 'generation': 1772218390452408, 'condition': 'host_os == "linux" and checkout_clang_coverage_tools and non_git_source', }, { - 'object_name': 'Linux_x64/llvmobjdump-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '8b6b59b573731321a0320917d011b8f373d14d9556db63bad1a8a2449e275f05', - 'size_bytes': 5771312, - 'generation': 1765411203963068, + 'object_name': 'Linux_x64/llvmobjdump-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '0a15d6b8c2b774b0706618d2afa123b9c87af2ec12e74dc44346df4c4690b670', + 'size_bytes': 5780116, + 'generation': 1772218390340688, 'condition': '((checkout_linux or checkout_mac or checkout_android) and host_os == "linux") and non_git_source', }, { - 'object_name': 'Mac/clang-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '3443ffd7392237fe82cf2eb62f56315e090dc6030a1cadc98dd4e938a28d2b2a', - 'size_bytes': 54346192, - 'generation': 1765411205883988, + 'object_name': 'Mac/clang-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '2661847eb275079358ab186eaf7f85d6139d44c7413a731dfac7f5ed1ec34a01', + 'size_bytes': 54827776, + 'generation': 1772218392155773, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '350d349928e9075d9409c1d59c2fcba70e0b47a7cca8eef100a835e509bf4093', - 'size_bytes': 1009740, - 'generation': 1765411213098351, + 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '69918295c163ec5a20aede81d4100bbd41e01142d32e0555366bba05141f7bf2', + 'size_bytes': 1010608, + 'generation': 1772218399449599, 'condition': 'checkout_mac and not host_os == "mac"', }, { - 'object_name': 'Mac/clang-tidy-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '953077b4b49d9a92981c1d8a8e44a5564d551931e81a089a6741d7db5c8be72f', - 'size_bytes': 14338004, - 'generation': 1765411205893281, + 'object_name': 'Mac/clang-tidy-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': 'b8013fe5d2410db4f365ec8779972415d1d0a08042a3a43f823a0da712108cff', + 'size_bytes': 14280488, + 'generation': 1772218392176137, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_tidy', }, { - 'object_name': 'Mac/clangd-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '16fbcb0ff1e7eed822d007af549a2820c69ff32aa5a951518cecc8ea161b300f', - 'size_bytes': 16279576, - 'generation': 1765411205896181, + 'object_name': 'Mac/clangd-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '508098b26e74bd7f5cdcc40a2ed2db24e2bdde15e0f1c14ce94f685f991b3dd6', + 'size_bytes': 15455912, + 'generation': 1772218392186146, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clangd', }, { - 'object_name': 'Mac/llvm-code-coverage-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '9ff2acd949d45fea14553baf8e035e8d2cd731ad7b747b1d3f1d726d77102373', - 'size_bytes': 2330756, - 'generation': 1765411205937658, + 'object_name': 'Mac/llvm-code-coverage-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '46c33f13a68fc14005560c01a91215b5cab54c07e920a714264352e46af1350c', + 'size_bytes': 2376304, + 'generation': 1772218392292978, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac/llvmobjdump-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': 'cf0d536e8ee4b92426819b64ba2e9b080796f97beb85a68ebe01894783c82955', - 'size_bytes': 5621768, - 'generation': 1765411205900222, + 'object_name': 'Mac/llvmobjdump-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '6a92e3f21b3a035f406313d24688bb1b312a9a0ec423ff808752b6638104aff3', + 'size_bytes': 5699700, + 'generation': 1772218392189830, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/clang-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '82d056f890fd3f86f711d2153e365e240673c98f94feb09758ecca7b487431fc', - 'size_bytes': 45447500, - 'generation': 1765411214931139, + 'object_name': 'Mac_arm64/clang-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '909be0f896bcf140c710548ccda4673c0aea2480e28d10803c19b1689b36acd5', + 'size_bytes': 45847044, + 'generation': 1772218401088162, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Mac_arm64/clang-tidy-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '04c7feb0058499149468edef9ce7b6155831ef22b756ae9aa39e5a9504937701', - 'size_bytes': 12329844, - 'generation': 1765411214943784, + 'object_name': 'Mac_arm64/clang-tidy-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '83dc8d90529730ae503e684ea0047a0baec2b0c4a81941d1bb4196feea6ba264', + 'size_bytes': 12444972, + 'generation': 1772218401143017, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_tidy', }, { - 'object_name': 'Mac_arm64/clangd-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '8e41df7efa35f732f46561bbbe1967743f8270276c0adb3dfa77ccd65b377ee1', - 'size_bytes': 12730784, - 'generation': 1765411214956057, + 'object_name': 'Mac_arm64/clangd-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '3b7ff06ccd41b0a1fb165e182a35bcd74ae49172f1720cd276eb5feac0e3dd9f', + 'size_bytes': 12816980, + 'generation': 1772218401144631, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clangd', }, { - 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '64aad877a3a74b9ae3a000bea9f3025011c0f246e58c64eb7a3ff93db4fccf11', - 'size_bytes': 1968060, - 'generation': 1765411215019503, + 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '67148555d00427a3eaa8aeefb8c4c4e1271d585315bdbf0d28d20fd78957e309', + 'size_bytes': 1988008, + 'generation': 1772218401224240, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '07ae49a9d0bca1909f870cf2bc8af2eeb8da57ce2153385c5fdf5a009c946d6e', - 'size_bytes': 5373248, - 'generation': 1765411214948454, + 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': 'a31075e7f46ed77c62ecec424722bec8335ef306a4701660f19b713229c49afa', + 'size_bytes': 5421552, + 'generation': 1772218401116635, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/clang-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '7c584196707e592fb4e4bd14cf2bb1399be250e666030aa15ba3482ca5a1adab', - 'size_bytes': 48674988, - 'generation': 1765411224454962, + 'object_name': 'Win/clang-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': 'f2c9d2a8accf7ed2e3c19b3f67fb94e60365411a536fb9d71391dd2d4e7e14bb', + 'size_bytes': 49546756, + 'generation': 1772218410442709, 'condition': 'host_os == "win"', }, { - 'object_name': 'Win/clang-tidy-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': 'e848dddd208e626afac825d740b76fd9e91af627dc8fb6293bd3297e06cadc0f', - 'size_bytes': 14269616, - 'generation': 1765411224494029, + 'object_name': 'Win/clang-tidy-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '99e00bbb404557db32df4e7a183ac520c526fe0e143ca380dfb2d0c33a2025b5', + 'size_bytes': 14462056, + 'generation': 1772218410470169, 'condition': 'host_os == "win" and checkout_clang_tidy', }, { - 'object_name': 'Win/clang-win-runtime-library-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '073eaf43b1897500a1a826851b8cce43ac04cf395f1e05eb14f273102221b165', - 'size_bytes': 2526948, - 'generation': 1765411231670451, + 'object_name': 'Win/clang-win-runtime-library-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '62e9c022223e0fa6ff855c25dcee524818f04c570127ed7e74895b320a10100a', + 'size_bytes': 2597584, + 'generation': 1772218417651221, 'condition': 'checkout_win and not host_os == "win"', }, { - 'object_name': 'Win/clangd-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '6c944d6d1f3627b30661a977ebaf28dfdb28a80cab3a2b4c0297447e60360f58', - 'size_bytes': 14680960, - 'generation': 1765411224477368, + 'object_name': 'Win/clangd-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '6a3ab3afb8d2e7f4a04eecd8073993586665ede3929308a0fa0119d9382b1e2d', + 'size_bytes': 14887416, + 'generation': 1772218410483998, 'condition': 'host_os == "win" and checkout_clangd', }, { - 'object_name': 'Win/llvm-code-coverage-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '4552393e501da36109e4d7ea6c5a8582c361c6812d00a641d5f19c6f3db804f3', - 'size_bytes': 2398400, - 'generation': 1765411224579579, + 'object_name': 'Win/llvm-code-coverage-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '4bd610d2fbcc6e2bd8fd2df8d8c23a915373f8c987701d295314e8b33d457075', + 'size_bytes': 2479300, + 'generation': 1772218410570017, 'condition': 'host_os == "win" and checkout_clang_coverage_tools', }, { - 'object_name': 'Win/llvmobjdump-llvmorg-22-init-17020-gbd1bd178-2.tar.xz', - 'sha256sum': '0fee07e9de315cfeea0802a6a411e1111217fd57b44645394509e9a4ecfe361f', - 'size_bytes': 5749012, - 'generation': 1765411224494520, + 'object_name': 'Win/llvmobjdump-llvmorg-23-init-5669-g8a0be0bc-1.tar.xz', + 'sha256sum': '2ee77b6240b76353840439b38e7009d9f1fb8e97930dbbef3b1ff805ee981c5f', + 'size_bytes': 5846184, + 'generation': 1772218410487302, 'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "win"', }, ] @@ -298,31 +298,31 @@ deps = { 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/rust-toolchain-a4cfac7093a1c1c7fbdb6bc75d6b6dc4d385fc69-2-llvmorg-22-init-17020-gbd1bd178.tar.xz', - 'sha256sum': '5ca1ca6268ce2dcfe878c623f0f49e4eba983bb36e79ceddb9c745ef48efc933', - 'size_bytes': 140484296, - 'generation': 1765411196238822, + 'object_name': 'Linux_x64/rust-toolchain-6f54d591c3116ee7f8ce9321ddeca286810cc142-7-llvmorg-23-init-5669-g8a0be0bc.tar.xz', + 'sha256sum': 'afbb00d27b8f9f65e6a754fb21e80dff084993285cf7f3c0020dece59c5bd67a', + 'size_bytes': 271641712, + 'generation': 1773769777991797, 'condition': 'host_os == "linux" and non_git_source', }, { - 'object_name': 'Mac/rust-toolchain-a4cfac7093a1c1c7fbdb6bc75d6b6dc4d385fc69-2-llvmorg-22-init-17020-gbd1bd178.tar.xz', - 'sha256sum': '26f095b3217e9619d6172bdc4b7329e51ebe2fb7508a313b8c3a6fce21416170', - 'size_bytes': 135435424, - 'generation': 1765411198122573, + 'object_name': 'Mac/rust-toolchain-6f54d591c3116ee7f8ce9321ddeca286810cc142-7-llvmorg-23-init-5669-g8a0be0bc.tar.xz', + 'sha256sum': '70b86e82f1cb55777d40b5828ddcb80afea49510085290424b61251d22e9f959', + 'size_bytes': 259443552, + 'generation': 1773769780408342, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/rust-toolchain-a4cfac7093a1c1c7fbdb6bc75d6b6dc4d385fc69-2-llvmorg-22-init-17020-gbd1bd178.tar.xz', - 'sha256sum': '64d5fb112d809b1c4a047ef7bd99e88534de470b82d86ce6ad729b12c5611488', - 'size_bytes': 122796960, - 'generation': 1765411200047613, + 'object_name': 'Mac_arm64/rust-toolchain-6f54d591c3116ee7f8ce9321ddeca286810cc142-7-llvmorg-23-init-5669-g8a0be0bc.tar.xz', + 'sha256sum': 'e2e19684f31b653ce9238f6303aec22576085528c294757a7157d4ab5e1926dc', + 'size_bytes': 242768940, + 'generation': 1773769782590875, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/rust-toolchain-a4cfac7093a1c1c7fbdb6bc75d6b6dc4d385fc69-2-llvmorg-22-init-17020-gbd1bd178.tar.xz', - 'sha256sum': 'a3cf74c96f7959a8507786665c23a2fb8ac67f107279ef888a8d3da066c0bca5', - 'size_bytes': 198058716, - 'generation': 1765411201950690, + 'object_name': 'Win/rust-toolchain-6f54d591c3116ee7f8ce9321ddeca286810cc142-7-llvmorg-23-init-5669-g8a0be0bc.tar.xz', + 'sha256sum': '37dd250549fed5a9765c3a88e3487409189e0c9c63b691fc77daa0b5f214bced', + 'size_bytes': 409536908, + 'generation': 1773769784773096, 'condition': 'host_os == "win"', }, ], @@ -331,15 +331,15 @@ deps = { 'src/third_party/clang-format/script': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@c2725e0622e1a86d55f14514f2177a39efea4a0e', 'src/third_party/compiler-rt/src': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/compiler-rt.git@2a9f44874fa9d4e3cfb9c88cfe984997053008f2', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/compiler-rt.git@bb7645f5e11c9c1d719a890fcb09ccfaaa14580f', 'src/third_party/libc++/src': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@7ab65651aed6802d2599dcb7a73b1f82d5179d05', 'src/third_party/libc++abi/src': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@8f11bb1d4438d0239d0dfc1bd9456a9f31629dda', 'src/third_party/llvm-libc/src': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libc.git@e9f0fd9932428fc109b848892050daadc2d91cc9', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libc.git@adccc443070c58badd6414fd9a4380ca8c78e7c4', 'src/third_party/libunwind/src': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@a726f5347e1e423d59f5c2d434b6a29265c43051', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@db838d918570d4e381ecf9f5cc70a0098c9c2cd6', 'src/third_party/test_fonts/test_fonts': { 'dep_type': 'gcs', @@ -381,7 +381,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_system_sdk/public', - 'version': 'Pfb3HDUW_uRir_VVTCYkGhf6bnPPF55NUJO2WXOxIe0C', + 'version': 'EpgkrtsLblLuw0BrsWCF0h_njBzIZsBNDxQ5VtA4s2UC', }, ], 'condition': 'checkout_android and non_git_source', @@ -402,7 +402,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/aapt2', - 'version': 's6POXpUalcnuPehDsORiojCpgbNXT4LYq7DVUYgsfxEC', + 'version': 'gsaUgZUqoyD0XG1E9-xesSWknHZINEuS00iSEncvlE0C', }, ], 'condition': 'checkout_android', @@ -435,7 +435,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/error_prone', - 'version': 'vNPU2aqNbmnmcCMUnT5QhVROLmdXRj7FOeYt85YWxEEC', + 'version': 'ax2FOQ16-lz2R1o1P-xDhd2KOAFoY1wjZ-lQgZkygUYC', }, ], 'condition': 'checkout_android', @@ -457,7 +457,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/lint', - 'version': '9AgFghC99R1A3IPlL_OqHWtpnYHvS9mujd700QWghPIC', + 'version': 'lBgjWB8NdI2Mhnsy0SHkCyCZ3pbO1Qe4Zk0JZHs3yAIC', }, ], 'condition': 'checkout_android and non_git_source', @@ -469,7 +469,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/nullaway', - 'version': 'O_Zf07-x9h-o0nBYfdu032C5xXGXwCAD1lwZG1ZS9QEC', + 'version': 'rYm-c7W2XUUzeDYveLTl3YmrIqc483bjo6OB5rpQUrIC', }, ], 'condition': 'checkout_android and non_git_source', @@ -488,11 +488,11 @@ deps = { }, 'src/third_party/boringssl/src': - 'https://boringssl.googlesource.com/boringssl.git@41b290f319b3f6f7cc8a44c47f29b8dd27ce21c0', + 'https://boringssl.googlesource.com/boringssl.git@8dce4fd20ab7e768c0a5103edc1d8cb7e54366ba', 'src/third_party/breakpad/breakpad': - 'https://chromium.googlesource.com/breakpad/breakpad.git@bcf7b6f1596e52a8ff0bbd0c9e32a321380b3954', + 'https://chromium.googlesource.com/breakpad/breakpad.git@8be0e3114685fcc1589561067282edf75ea1259a', 'src/third_party/catapult': - 'https://chromium.googlesource.com/catapult.git@c9916a593bec75bdaa231475af0e8740f857bf10', + 'https://chromium.googlesource.com/catapult.git@5a34891efa6e41c8aca8842386b8ee528963ffdf', 'src/third_party/ced/src': { 'url': 'https://chromium.googlesource.com/external/github.com/google/compact_enc_det.git@ba412eaaacd3186085babcd901679a48863c7dd5', }, @@ -505,13 +505,13 @@ deps = { 'src/third_party/crc32c/src': 'https://chromium.googlesource.com/external/github.com/google/crc32c.git@d3d60ac6e0f16780bcfcc825385e1d338801a558', 'src/third_party/depot_tools': - 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@fb0b652edba70f5c4ac867f3beca9e535f905b4c', + 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@ce1ebad2c35c9387186f01d77edeea28a254c955', 'src/third_party/ffmpeg': - 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@fd8d327732d4c4a3ef831f4de49635e9528cb73e', + 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@b5e18fb9da84e26ceef30d4e4886696bf59337c0', 'src/third_party/flatbuffers/src': 'https://chromium.googlesource.com/external/github.com/google/flatbuffers.git@a86afae9399bbe631d1ea0783f8816e780e236cc', 'src/third_party/grpc/src': { - 'url': 'https://chromium.googlesource.com/external/github.com/grpc/grpc.git@ae8671efbd172bf80b8279903619b658ffd2486e', + 'url': 'https://chromium.googlesource.com/external/github.com/grpc/grpc.git@5e9fb9cbfb12a10ff9c16fbc360328d224b838d6', }, # Used for embedded builds. CrOS & Linux use the system version. 'src/third_party/fontconfig/src': { @@ -519,14 +519,14 @@ deps = { 'condition': 'checkout_linux', }, 'src/third_party/freetype/src': - 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@341049a95bacfc5debf6c9daf537b7acec27f3dd', + 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@99b479dc34728936b006679a31e12b8cf432fc55', 'src/third_party/harfbuzz-ng/src': - 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@fa2908bf16d2ccd6623f4d575455fea72a1a722b', + 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@6f4c5cec306d31e6822303f5ba248a14293d588e', 'src/third_party/google_benchmark/src': { 'url': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@188e8278990a9069ffc84441cb5a024fd0bede37', }, 'src/third_party/libpfm4/src': - Var('chromium_git') + '/external/git.code.sf.net/p/perfmon2/libpfm4.git' + '@' + '964baf9d35d5f88d8422f96d8a82c672042e7064', + Var('chromium_git') + '/external/git.code.sf.net/p/perfmon2/libpfm4.git' + '@' + '41878eab48c50bb9ec5f741a013e971bb5a9dff2', # WebRTC-only dependency (not present in Chromium). 'src/third_party/gtest-parallel': 'https://chromium.googlesource.com/external/github.com/google/gtest-parallel@cd488bdedc1d2cffb98201a17afc1b298b0b90f1', @@ -537,7 +537,7 @@ deps = { 'src/third_party/googletest/src': 'https://chromium.googlesource.com/external/github.com/google/googletest.git@4fe3307fb2d9f86d19777c7eb0e4809e9694dde7', 'src/third_party/icu': { - 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@a86a32e67b8d1384b33f8fa48c83a6079b86f8cd', + 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@b4aae6832c06df9d538d41b249403cf0678f16b4', }, 'src/third_party/jdk/current': { 'packages': [ @@ -573,7 +573,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/kotlin_stdlib', - 'version': 'pzmoMtwHT1hYqAVIaEEBdTjWfGqwltLK6Fvr4EaVP3UC', + 'version': 'uq9bdsIxS9Is_mAZr9OWBymcLtxheKkgzeUSLuZKhJUC', }, ], 'condition': 'checkout_android', @@ -584,7 +584,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/kotlinc', - 'version': 'swjrmkJ-TygIVaYOBj227R8b8-K9Zav25VJA6XSLqwsC', + 'version': 'RcyJsRii1TkItQ8HjsvzQnXGvIZ0FJvpF4bxYeFr7qAC', }, ], 'condition': 'checkout_android', @@ -593,29 +593,29 @@ deps = { 'src/third_party/libFuzzer/src': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/compiler-rt/lib/fuzzer.git@bea408a6e01f0f7e6c82a43121fe3af4506c932e', 'src/third_party/fuzztest/src': - 'https://chromium.googlesource.com/external/github.com/google/fuzztest.git@a72f099a943c257afe8d4d87c10a22b23e17786d', + 'https://chromium.googlesource.com/external/github.com/google/fuzztest.git@dc327134097700121e4ecd6e1d54d1d0a832a18d', 'src/third_party/libjpeg_turbo': - 'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@6bb85251a8382b5e07f635a981ac685cc5ab5053', + 'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@d1f5f2393e0d51f840207342ae86e55a86443288', 'src/third_party/libsrtp': - 'https://chromium.googlesource.com/chromium/deps/libsrtp.git@a52756acb1c5e133089c798736dd171567df11f5', + 'https://chromium.googlesource.com/chromium/deps/libsrtp.git@e8383771af8aa4096f5bcfe3743a5ea128f88a9a', 'src/third_party/dav1d/libdav1d': - 'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@b546257f770768b2c88258c533da38b91a06f737', + 'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@d69235dd804b24c04ed05639cffcc912cd6cfd75', 'src/third_party/libaom/source/libaom': - 'https://aomedia.googlesource.com/aom.git@e3d6ba6e5e9888a7ca69eb114c6eb913275f253c', + 'https://aomedia.googlesource.com/aom.git@f3dddebddd0dba76fbfb97b96b6336bcf1d3a30c', 'src/third_party/libgav1/src': Var('chromium_git') + '/codecs/libgav1.git' + '@' + '40f58ed32ff39071c3f2a51056dbc49a070af0dc', 'src/third_party/libunwindstack': { - 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@0928ad0d25e4af07c8be5ab06d0ca584f9f4ceb5', + 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@333fcafb91bd3830c5ef814c071ff73df9cdc976', 'condition': 'checkout_android', }, 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '698c3b289159cf14ac110e21d5ed424c8a9f35b4', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '40b1342aa7bd47d9c963c3617fd98ec1551528f9', 'src/third_party/protobuf-javascript/src': Var('chromium_git') + '/external/github.com/protocolbuffers/protobuf-javascript' + '@' + 'e6d763860001ba1a76a63adcff5efb12b1c96024', 'src/third_party/libvpx/source/libvpx': - 'https://chromium.googlesource.com/webm/libvpx.git@b0be221b6811038e1579a4281241a55549d7611d', + 'https://chromium.googlesource.com/webm/libvpx.git@3fce57ecc905d95a4619f33d09851d68c5a88663', 'src/third_party/libyuv': - 'https://chromium.googlesource.com/libyuv/libyuv.git@022efdb0b771f7353741dbe360b8bef4e0a874eb', + 'https://chromium.googlesource.com/libyuv/libyuv.git@30809ff64a9ca5e45f86439c0d474c2d3eef3d05', 'src/third_party/lss': { 'url': 'https://chromium.googlesource.com/linux-syscall-support.git@29164a80da4d41134950d76d55199ea33fbb9613', 'condition': 'checkout_android or checkout_linux', @@ -631,20 +631,20 @@ deps = { # Used by boringssl. 'src/third_party/nasm': { - 'url': 'https://chromium.googlesource.com/chromium/deps/nasm.git@af5eeeb054bebadfbb79c7bcd100a95e2ad4525f' + 'url': 'https://chromium.googlesource.com/chromium/deps/nasm.git@45252858722aad12e545819b2d0f370eb865431b' }, 'src/third_party/openh264/src': 'https://chromium.googlesource.com/external/github.com/cisco/openh264@652bdb7719f30b52b08e506645a7322ff1b2cc6f', 'src/third_party/re2/src': - 'https://chromium.googlesource.com/external/github.com/google/re2.git@e7aec5985072c1dbe735add802653ef4b36c231a', + 'https://chromium.googlesource.com/external/github.com/google/re2.git@972a15cedd008d846f1a39b2e88ce48d7f166cbd', 'src/third_party/r8/cipd': { 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': 'TXlzDJEcatuD9PYDocJSKkHs3M9Y2S_UV-gOLe3PGh8C', + 'version': '8ZRb6CCpZTU5dSpQyDlbusalGCjWV0sVSGTq_0Js3mcC', }, ], 'condition': 'checkout_android', @@ -657,7 +657,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': 'a4fVqbIycCDqs1714SLRqxEdz6P-sH-z1QT_eeeF0PcC', + 'version': '8ZRb6CCpZTU5dSpQyDlbusalGCjWV0sVSGTq_0Js3mcC', }, ], 'condition': 'checkout_android', @@ -668,7 +668,7 @@ deps = { 'condition': 'checkout_android', }, 'src/tools': - 'https://chromium.googlesource.com/chromium/src/tools@88162e4e0e83c5dd6bf391dac5fb0afa59be6960', + 'https://chromium.googlesource.com/chromium/src/tools@f363a79871a91f36322e845e3134e2f04e1fc18a', 'src/third_party/espresso': { 'packages': [ @@ -707,7 +707,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'WX3ELG4TgH2nEIDT11VHoWCYzpSsS-BVAZwPnNs34AUC', + 'version': 'xbJffE4gDOA4g5njwbpWZtgk4ouGMWOHiqq2ymCZ2ggC', }, ], 'condition': 'checkout_android and non_git_source', @@ -718,7 +718,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/manifest_merger', - 'version': 'OWV7CA1NXDbFTkcr9MBA__4sBlYfX-ORWPDBh8rSClkC', + 'version': '5JSVccMXNkpeH9lpydgxJ3QCoNpBC5yvil7NvdsqUasC', }, ], 'condition': 'checkout_android', @@ -728,8 +728,8 @@ deps = { 'src/third_party/android_sdk/public': { 'packages': [ { - 'package': 'chromium/third_party/android_sdk/public/build-tools/36.0.0', - 'version': 'y3EsZLg4bxPmpW0oYsAHylywNyMnIwPS3kh1VbQLAFAC', + 'package': 'chromium/third_party/android_sdk/public/build-tools/36.1.0', + 'version': '-jLl4Ibk_WmgTsZaP-ueQwZDhBwkWf5BsQ4UNrkzXF0C', }, { 'package': 'chromium/third_party/android_sdk/public/emulator', @@ -740,12 +740,12 @@ deps = { 'version': 'qTD9QdBlBf3dyHsN1lJ0RH6AhHxR42Hmg2Ih-Vj4zIEC' }, { - 'package': 'chromium/third_party/android_sdk/public/platforms/android-36', - 'version': '_YHemUrK49JrE7Mctdf5DDNOHu1VKBx_PTcWnZ-cbOAC', + 'package': 'chromium/third_party/android_sdk/public/platforms/android-36.1', + 'version': 'gxwLT70eR_ObwZJzKK8UIS-N549yAocNTmc0JHgO7gUC', }, { - 'package': 'chromium/third_party/android_sdk/public/cmdline-tools', - 'version': 'gekOVsZjseS1w9BXAT3FsoW__ByGDJYS9DgqesiwKYoC', + 'package': 'chromium/third_party/android_sdk/public/cmdline-tools/linux', + 'version': 'LZa8CWNVWS6UUQgQ7IJdFCqRV1Bmx2-alTNqEDJpJkcC', }, ], 'condition': 'checkout_android', @@ -786,13 +786,13 @@ deps = { }, 'src/third_party/tflite/src': - Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '48401a9c25ddb7cb882074d48c79f91d1f6a89e0', + Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '8fd527849069a358ad6c2980b9a9b34a53c53717', 'src/third_party/turbine/cipd': { 'packages': [ { 'package': 'chromium/third_party/turbine', - 'version': 'nJDryxCoihRuUxDq-oC-PbBAn1u8HeKRHU44sn9Cd7MC', + 'version': '0A4lFRLjqycR4-EvoBz1w2FxPEzzG94cFvwu8v39DRYC', }, ], 'condition': 'checkout_android', @@ -800,7 +800,7 @@ deps = { }, 'src/third_party/zstd/src': { - 'url': Var('chromium_git') + '/external/github.com/facebook/zstd.git' + '@' + 'ae9f20ca2716f2605822ca375995b7d876389b64', + 'url': Var('chromium_git') + '/external/github.com/facebook/zstd.git' + '@' + '3ae099b48dfcfe02b1b3ba81ab85457f8a922e9f', 'condition': 'checkout_android', }, @@ -808,15 +808,15 @@ deps = { 'packages': [ { 'package': 'infra/tools/luci/cas/${{platform}}', - 'version': 'git_revision:808a00437f24bb404c09608ad8bf3847a78de369', + 'version': 'git_revision:8cb5bd940d5f726f8a538212b2287987fcadf837', }, { 'package': 'infra/tools/luci/isolate/${{platform}}', - 'version': 'git_revision:808a00437f24bb404c09608ad8bf3847a78de369', + 'version': 'git_revision:8cb5bd940d5f726f8a538212b2287987fcadf837', }, { 'package': 'infra/tools/luci/swarming/${{platform}}', - 'version': 'git_revision:808a00437f24bb404c09608ad8bf3847a78de369', + 'version': 'git_revision:8cb5bd940d5f726f8a538212b2287987fcadf837', } ], 'dep_type': 'cipd', @@ -842,29 +842,29 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/autorolled', - 'version': 'LStkoCV2NKgZ1hqhnILaH4soxZtZa2-8RG67-JxAVUsC', + 'version': 'ioujkn6x8xwFvfYkiQGrRb9lX4Khn3Thhv3nsoN6lrQC', }, ], 'condition': 'checkout_android and non_git_source', 'dep_type': 'cipd', }, 'src/third_party/pthreadpool/src': - Var('chromium_git') + '/external/github.com/google/pthreadpool.git' + '@' + 'd90cd6f1493e09d12c407243f7f331a8cda55efb', + Var('chromium_git') + '/external/github.com/google/pthreadpool.git' + '@' + '9003ee6c137cea3b94161bd5c614fb43be523ee1', 'src/third_party/xnnpack/src': - Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + '4574c4d9b00703c15f2218634ddf101597b22b18', + Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + '97f3177fd836fff03b48a886bb130591866ad7ca', 'src/third_party/farmhash/src': Var('chromium_git') + '/external/github.com/google/farmhash.git' + '@' + '816a4ae622e964763ca0862d9dbd19324a1eaf45', 'src/third_party/ruy/src': - Var('chromium_git') + '/external/github.com/google/ruy.git' + '@' + '576e020f06334118994496b45f9796ed7fda3280', + Var('chromium_git') + '/external/github.com/google/ruy.git' + '@' + '2af88863614a8298689cc52b1a47b3fcad7be835', 'src/third_party/cpuinfo/src': - Var('chromium_git') + '/external/github.com/pytorch/cpuinfo.git' + '@' + '0fea7f5f88243ee354df0e0082b5f27d13fc9551', + Var('chromium_git') + '/external/github.com/pytorch/cpuinfo.git' + '@' + '7607ca500436b37ad23fb8d18614bec7796b68a7', 'src/third_party/eigen3/src': - Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '251bff28859af2ed2d5bdf14034175f03cafffc7', + Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '002229ce470065878afb7c2f6f96d22c9a9b7ba0', 'src/third_party/fp16/src': Var('chromium_git') + '/external/github.com/Maratyszcza/FP16.git' + '@' + '3d2de1816307bac63c16a297e8c4dc501b4076df', @@ -1014,17 +1014,6 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/android_deps/cipd/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava', - 'version': 'version:2@1.8.1.cr2', - }, - ], - 'condition': 'checkout_android and non_git_source', - 'dep_type': 'cipd', - }, - 'src/third_party/android_deps/cipd/libs/org_jsoup_jsoup': { 'packages': [ { @@ -1156,7 +1145,6 @@ hooks = [ 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', - '--no_auth', '--bucket', 'chromium-browser-clang/ciopfs', '-s', 'src/build/ciopfs.sha1', ] @@ -1212,7 +1200,6 @@ hooks = [ 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', - '--no_auth', '--bucket', 'chromium-browser-clang', '-s', 'src/tools/clang/dsymutil/bin/dsymutil.arm64.sha1', '-o', 'src/tools/clang/dsymutil/bin/dsymutil', @@ -1225,7 +1212,6 @@ hooks = [ 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', - '--no_auth', '--bucket', 'chromium-browser-clang', '-s', 'src/tools/clang/dsymutil/bin/dsymutil.x64.sha1', '-o', 'src/tools/clang/dsymutil/bin/dsymutil', @@ -1239,7 +1225,6 @@ hooks = [ 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', - '--no_auth', '--bucket', 'chromium-browser-clang/rc', '-s', 'src/build/toolchain/win/rc/win/rc.exe.sha1', ], @@ -1251,7 +1236,6 @@ hooks = [ 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', - '--no_auth', '--bucket', 'chromium-browser-clang/rc', '-s', 'src/build/toolchain/win/rc/mac/rc.sha1', ], @@ -1263,7 +1247,6 @@ hooks = [ 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', - '--no_auth', '--bucket', 'chromium-browser-clang/rc', '-s', 'src/build/toolchain/win/rc/linux64/rc.sha1', ], @@ -1275,7 +1258,6 @@ hooks = [ '--directory', '--recursive', '--num_threads=10', - '--no_auth', '--quiet', '--bucket', 'chromium-webrtc-resources', 'src/resources'], @@ -1370,6 +1352,7 @@ include_rules = [ "+absl/cleanup/cleanup.h", "+absl/container", "-absl/container/fixed_array.h", + "+absl/crc", "+absl/functional/any_invocable.h", "+absl/functional/bind_front.h", "+absl/memory/memory.h", @@ -1391,7 +1374,7 @@ include_rules = [ ] specific_include_rules = { - "webrtc_lib_link_test\.cc": [ + "webrtc_lib_link_test\\.cc": [ "+media/engine", "+modules/audio_device", "+modules/audio_processing", diff --git a/OWNERS b/OWNERS index bfcca980eb8..14b39cf614e 100644 --- a/OWNERS +++ b/OWNERS @@ -1,6 +1,6 @@ -henrika@webrtc.org +danilchap@webrtc.org +eshr@webrtc.org hta@webrtc.org -mflodman@webrtc.org stefan@webrtc.org tommi@webrtc.org include OWNERS_INFRA #{Owners for infra and repo related files} diff --git a/OWNERS_INFRA b/OWNERS_INFRA index 0a192b9c363..3c7098037d0 100644 --- a/OWNERS_INFRA +++ b/OWNERS_INFRA @@ -1,20 +1,21 @@ -#Owners for infra and repo related files +# Owners for infra and repo related files per-file .gitignore=* +per-file AUTHORS=* +per-file DEPS=* +per-file WATCHLISTS=* +per-file whitespace.txt=* + 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 .rustfmt.toml=boivie@webrtc.org,mbonadei@webrtc.org,jleconte@webrtc.org per-file pylintrc_old_style=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org -per-file WATCHLISTS=* -per-file whitespace.txt=* -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 + +per-file .rustfmt.toml=boivie@webrtc.org,mbonadei@webrtc.org,jleconte@webrtc.org diff --git a/PRESUBMIT.py b/PRESUBMIT.py index fa6888cda98..60a78a5e4be 100755 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -662,6 +662,23 @@ def CheckGnGen(input_api, output_api): return [] +def CheckDeps(input_api, output_api): + """Runs checkdeps """ + repo_root = input_api.change.RepositoryRoot() + checkdeps_path = input_api.os_path.join(repo_root, 'buildtools', + 'checkdeps') + with _AddToPath(checkdeps_path): + import checkdeps + + deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath()) + deps_checker.CheckDirectory(input_api.PresubmitLocalPath()) + results = [] + if deps_checker.results_formatter.GetResults(): + results.append( + output_api.PresubmitError('\n'.join( + deps_checker.results_formatter.GetResults()))) + return results + def CheckUnwantedDependencies(input_api, output_api, source_file_filter): """Runs checkdeps on #include statements added in this change. Breaking - rules is an error, breaking ! rules is a @@ -815,10 +832,7 @@ def Join(*args): 'process_perf_results_test.py', ] - test_directories = [ - input_api.PresubmitLocalPath(), - Join('rtc_tools', 'py_event_log_analyzer'), - ] + [ + test_directories = [input_api.PresubmitLocalPath()] + [ root for root, _, files in os.walk(Join('tools_webrtc')) if any( f.endswith('_test.py') and f not in excluded_files for f in files) ] @@ -1000,6 +1014,7 @@ def CommonChecks(input_api, output_api): results.extend( input_api.canned_checks.CheckPatchFormatted(input_api, output_api)) results.extend(CheckNativeApiHeaderChanges(input_api, output_api)) + results.extend(CheckDeps(input_api, output_api)) results.extend( CheckNoIOStreamInHeaders(input_api, output_api, diff --git a/README.chromium b/README.chromium index a97e58fa5cb..b4c19c0575d 100644 --- a/README.chromium +++ b/README.chromium @@ -6,6 +6,7 @@ License: BSD-3-Clause License File: LICENSE Shipped: yes Security Critical: yes +Update Mechanism: Manual Mitigated: CVE-2022-2294 CVE-2022-2294: Fixed by https://crbug.com/40060120. diff --git a/WATCHLISTS b/WATCHLISTS index 3c35dd4be7b..7b38c2084f3 100644 --- a/WATCHLISTS +++ b/WATCHLISTS @@ -102,76 +102,41 @@ 'WATCHLISTS': { 'this_file': [], 'all_webrtc': [], - 'root_files': ['peah@webrtc.org', - 'qiang.lu@intel.com', - 'yujie.mao@webrtc.org'], + 'root_files': ['peah@webrtc.org'], 'build_files': ['mbonadei@webrtc.org'], - 'common_audio': ['alessiob@webrtc.org', - 'audio-team@agora.io', - 'peah@webrtc.org', + 'common_audio': ['peah@webrtc.org', 'saza@webrtc.org'], 'audio': ['peah@webrtc.org'], 'api': ['hta@webrtc.org', 'peah@webrtc.org'], 'base': ['hta@webrtc.org'], - 'call': ['mflodman@webrtc.org', - 'stefan@webrtc.org'], - 'video': ['mflodman@webrtc.org', - 'stefan@webrtc.org', - 'video-team@agora.io', - 'yujie.mao@webrtc.org', - 'zhengzhonghou@agora.io'], - 'video_capture': ['mflodman@webrtc.org', - 'perkj@webrtc.org', - 'sdk-team@agora.io', - 'zhengzhonghou@agora.io'], - 'audio_device': ['audio-team@agora.io', - 'henrika@webrtc.org', + 'call': ['stefan@webrtc.org'], + 'video': ['stefan@webrtc.org'], + 'video_capture': ['perkj@webrtc.org'], + 'audio_device': ['henrika@webrtc.org', 'peah@webrtc.org', - 'saza@webrtc.org', - 'sdk-team@agora.io'], - 'audio_coding': ['alessiob@webrtc.org', - 'audio-team@agora.io', - 'henrik.lundin@webrtc.org', + 'saza@webrtc.org'], + 'audio_coding': ['henrik.lundin@webrtc.org', 'peah@webrtc.org', 'saza@webrtc.org'], - 'neteq': ['alessiob@webrtc.org', - 'audio-team@agora.io', - 'henrik.lundin@webrtc.org', + 'neteq': ['henrik.lundin@webrtc.org', 'saza@webrtc.org'], - 'audio_mixer': ['aleloi@webrtc.org', - 'henrik.lundin@webrtc.org', + 'audio_mixer': ['henrik.lundin@webrtc.org', 'peah@webrtc.org', 'saza@webrtc.org'], - 'audio_processing': ['alessiob@webrtc.org', - 'audio-team@agora.io', - 'henrik.lundin@webrtc.org', + 'audio_processing': ['henrik.lundin@webrtc.org', 'peah@webrtc.org', 'saza@webrtc.org'], - 'video_coding': ['mflodman@webrtc.org', - 'stefan@webrtc.org', - 'video-team@agora.io', - 'zhengzhonghou@agora.io'], - 'bitrate_controller': ['mflodman@webrtc.org', - 'stefan@webrtc.org', - 'zhuangzesen@agora.io'], + 'video_coding': ['stefan@webrtc.org'], + 'bitrate_controller': ['stefan@webrtc.org'], 'congestion_controller': [], - 'remote_bitrate_estimator': ['mflodman@webrtc.org', - 'stefan@webrtc.org', - 'zhuangzesen@agora.io'], - 'pacing': ['mflodman@webrtc.org', - 'stefan@webrtc.org', - 'zhuangzesen@agora.io'], - 'rtp_rtcp': ['mflodman@webrtc.org', - 'stefan@webrtc.org', - 'danilchap@webrtc.org', - 'zhuangzesen@agora.io'], - 'system_wrappers': ['fengyue@agora.io', - 'henrika@webrtc.org', - 'mflodman@webrtc.org', - 'peah@webrtc.org', - 'zhengzhonghou@agora.io'], - 'pc': ['steveanton+watch@webrtc.org'], + 'remote_bitrate_estimator': ['stefan@webrtc.org'], + 'pacing': ['stefan@webrtc.org'], + 'rtp_rtcp': ['stefan@webrtc.org', + 'danilchap@webrtc.org'], + 'system_wrappers': ['henrika@webrtc.org', + 'peah@webrtc.org'], + 'pc': [], 'logging': ['terelius@webrtc.org'], }, } diff --git a/api/BUILD.gn b/api/BUILD.gn index 7b55c579687..b3ca6490476 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -26,7 +26,6 @@ rtc_library("enable_media") { deps = [ ":peer_connection_interface", ":scoped_refptr", - "../call", "../call:call_interfaces", "../media:media_engine", "../media:rtc_audio_video", @@ -76,7 +75,6 @@ rtc_library("create_modular_peer_connection_factory") { "../pc:peer_connection_factory_proxy", "../rtc_base:threading", "../rtc_base/system:rtc_export", - "../stats:rtc_stats", ] } @@ -126,7 +124,6 @@ rtc_library("rtp_headers") { "rtp_headers.h", ] deps = [ - ":array_view", "../rtc_base:checks", "../rtc_base/system:rtc_export", "units:timestamp", @@ -142,11 +139,11 @@ rtc_library("rtp_packet_info") { "rtp_packet_infos.h", ] deps = [ - ":array_view", ":make_ref_counted", ":refcountedbase", ":rtp_headers", ":scoped_refptr", + "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base/system:rtc_export", "units:time_delta", "units:timestamp", @@ -168,7 +165,6 @@ rtc_library("media_stream_interface") { ] deps = [ ":audio_options_api", - ":make_ref_counted", ":ref_count", ":rtp_parameters", ":scoped_refptr", @@ -199,7 +195,6 @@ rtc_library("candidate") { "../rtc_base:crc32", "../rtc_base:crypto_random", "../rtc_base:ip_address", - "../rtc_base:logging", "../rtc_base:net_helper", "../rtc_base:network_constants", "../rtc_base:socket_address", @@ -227,9 +222,7 @@ rtc_source_set("ice_transport_interface") { deps = [ ":async_dns_resolver", ":local_network_access_permission", - ":packet_socket_factory", ":ref_count", - ":rtc_error", ":scoped_refptr", "environment", "rtc_event_log", @@ -265,9 +258,31 @@ rtc_library("dtmf_sender_interface") { visibility = [ "*" ] sources = [ "dtmf_sender_interface.h" ] + deps = [ ":ref_count" ] +} + +rtc_library("sframe_types") { + visibility = [ "*" ] + sources = [ "sframe/sframe_types.h" ] +} + +rtc_library("sframe_encrypter_interface") { + visibility = [ "*" ] + sources = [ "sframe/sframe_encrypter_interface.h" ] deps = [ - ":media_stream_interface", ":ref_count", + ":rtc_error", + ":sframe_types", + ] +} + +rtc_library("sframe_decrypter_interface") { + visibility = [ "*" ] + sources = [ "sframe/sframe_decrypter_interface.h" ] + deps = [ + ":ref_count", + ":rtc_error", + ":sframe_types", ] } @@ -287,6 +302,7 @@ rtc_library("rtp_sender_interface") { ":rtc_error", ":rtp_parameters", ":scoped_refptr", + ":sframe_encrypter_interface", "../rtc_base:checks", "../rtc_base/system:rtc_export", "crypto:frame_encryptor_interface", @@ -324,6 +340,7 @@ rtc_library("jsep") { ] deps = [ ":candidate", + ":payload_type", ":ref_count", ":rtc_error", ":rtp_parameters", @@ -366,6 +383,7 @@ rtc_library("jsep") { } rtc_library("data_channel_interface") { + visibility = [ "*" ] sources = [ "data_channel_interface.cc", "data_channel_interface.h", @@ -382,6 +400,7 @@ rtc_library("data_channel_interface") { } rtc_library("legacy_stats_types") { + visibility = [ "*" ] sources = [ "legacy_stats_types.cc", "legacy_stats_types.h", @@ -400,6 +419,7 @@ rtc_library("legacy_stats_types") { } rtc_library("peer_connection_interface") { + visibility = [ "*" ] sources = [ "peer_connection_interface.cc", "peer_connection_interface.h", @@ -447,6 +467,7 @@ rtc_library("peer_connection_interface") { "../rtc_base:ssl", "../rtc_base:ssl_adapter", "../rtc_base:threading", + "../rtc_base/system:plan_b_only", "../rtc_base/system:rtc_export", "adaptation:resource_adaptation_api", "audio:audio_device", @@ -473,6 +494,7 @@ rtc_library("peer_connection_interface") { } rtc_library("rtp_receiver_interface") { + visibility = [ "*" ] sources = [ "rtp_receiver_interface.cc", "rtp_receiver_interface.h", @@ -482,8 +504,12 @@ rtc_library("rtp_receiver_interface") { ":frame_transformer_interface", ":media_stream_interface", ":ref_count", + ":rtc_error", ":rtp_parameters", ":scoped_refptr", + ":sframe_decrypter_interface", + ":sframe_types", + "../rtc_base:checks", "../rtc_base/system:rtc_export", "crypto:frame_decryptor_interface", "transport/rtp:rtp_source", @@ -491,12 +517,12 @@ rtc_library("rtp_receiver_interface") { } rtc_library("rtp_transceiver_interface") { + visibility = [ "*" ] sources = [ "rtp_transceiver_interface.cc", "rtp_transceiver_interface.h", ] deps = [ - ":array_view", ":ref_count", ":rtc_error", ":rtp_parameters", @@ -511,6 +537,7 @@ rtc_library("rtp_transceiver_interface") { } rtc_library("set_local_description_observer_interface") { + visibility = [ "*" ] sources = [ "set_local_description_observer_interface.h" ] deps = [ ":ref_count", @@ -519,6 +546,7 @@ rtc_library("set_local_description_observer_interface") { } rtc_library("set_remote_description_observer_interface") { + visibility = [ "*" ] sources = [ "set_remote_description_observer_interface.h" ] deps = [ ":ref_count", @@ -527,19 +555,16 @@ rtc_library("set_remote_description_observer_interface") { } rtc_library("uma_metrics") { + visibility = [ "*" ] sources = [ "uma_metrics.h" ] - deps = [ - ":ref_count", - ":rtc_error", - ] + deps = [] } rtc_library("video_track_source_proxy_factory") { + visibility = [ "*" ] sources = [ "video_track_source_proxy_factory.h" ] deps = [ ":media_stream_interface", - ":ref_count", - ":rtc_error", ":scoped_refptr", "../rtc_base:threading", "../rtc_base/system:rtc_export", @@ -568,53 +593,38 @@ rtc_library("libjingle_peerconnection_api") { "video_track_source_proxy_factory.h", # Used downstream ] deps = [ - ":array_view", ":async_dns_resolver", ":audio_options_api", ":candidate", ":data_channel_event_observer_interface", - ":data_channel_interface", ":dtls_transport_interface", ":dtmf_sender_interface", ":fec_controller_api", ":field_trials_view", ":frame_transformer_interface", ":ice_transport_interface", - ":jsep", - ":legacy_stats_types", ":libjingle_logging_api", ":local_network_access_permission", - ":make_ref_counted", ":media_stream_interface", ":network_state_predictor_api", ":packet_socket_factory", - ":peer_connection_interface", ":priority", ":ref_count", ":rtc_error", ":rtc_stats_api", - ":rtp_packet_info", ":rtp_parameters", - ":rtp_receiver_interface", - ":rtp_sender_interface", ":rtp_transceiver_direction", - ":rtp_transceiver_interface", ":rtp_transport_factory", ":scoped_refptr", - ":sctp_transport_interface", ":sequence_checker", - ":set_local_description_observer_interface", + ":sframe_decrypter_interface", + ":sframe_encrypter_interface", + ":sframe_types", ":turn_customizer", - "../call:rtp_interfaces", - "../media:media_engine", - "../p2p:connection", "../p2p:dtls_transport_factory", "../p2p:port", "../p2p:port_allocator", - "../pc:media_factory", - "../pc:session_description", "../rtc_base:copy_on_write_buffer", - "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:network", "../rtc_base:network_constants", @@ -622,8 +632,8 @@ rtc_library("libjingle_peerconnection_api") { "../rtc_base:socket_factory", "../rtc_base:ssl", "../rtc_base:ssl_adapter", - "../rtc_base:stringutils", "../rtc_base/system:no_unique_address", + "../rtc_base/system:plan_b_only", "adaptation:resource_adaptation_api", "audio:audio_device", "audio:audio_frame_processor", @@ -637,17 +647,13 @@ rtc_library("libjingle_peerconnection_api") { "metronome", "neteq:neteq_api", "rtc_event_log:rtc_event_log_factory_interface", - "task_queue", "transport:bandwidth_estimation_settings", "transport:bitrate_settings", "transport:enums", "transport:network_control", "transport:sctp_transport_factory_interface", "transport/rtp:rtp_source", - "units:data_rate", "units:time_delta", - "units:timestamp", - "video:encoded_image", "video:video_bitrate_allocator_factory", "video:video_frame", "video:yuv_helper", @@ -665,10 +671,10 @@ rtc_library("libjingle_peerconnection_api") { # Basically, don't add stuff here. You might break sensitive downstream # targets like pnacl. API should not depend on anything outside of this # file, really. All these should arguably go away in time. - "../media:rtc_media_config", - "../rtc_base:checks", - "../rtc_base:threading", - "../rtc_base/system:rtc_export", + "../media:rtc_media_config", # keep + "../rtc_base:checks", # keep + "../rtc_base:threading", # keep + "../rtc_base/system:rtc_export", # keep ] } @@ -679,16 +685,12 @@ rtc_library("frame_transformer_interface") { "frame_transformer_interface.h", ] deps = [ - ":array_view", - ":make_ref_counted", ":ref_count", ":scoped_refptr", "../rtc_base:checks", - "../rtc_base:refcount", "../rtc_base/system:rtc_export", "units:time_delta", "units:timestamp", - "video:encoded_frame", "video:video_frame_metadata", ] } @@ -727,6 +729,7 @@ rtc_source_set("packet_socket_factory") { deps = [ ":async_dns_resolver", "../rtc_base:async_packet_socket", + "../rtc_base:checks", "../rtc_base:socket_address", "../rtc_base:ssl", "../rtc_base/system:rtc_export", @@ -738,7 +741,6 @@ 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", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -749,9 +751,7 @@ rtc_source_set("local_network_access_permission") { visibility = [ "*" ] sources = [ "local_network_access_permission.h" ] deps = [ - "../rtc_base:checks", "../rtc_base:socket_address", - "../rtc_base/system:rtc_export", "//third_party/abseil-cpp/absl/functional:any_invocable", ] } @@ -784,14 +784,12 @@ rtc_source_set("video_quality_analyzer_api") { sources = [ "test/video_quality_analyzer_interface.h" ] deps = [ - ":array_view", ":rtc_stats_api", ":scoped_refptr", ":stats_observer_interface", "../rtc_base:checks", "video:encoded_image", "video:video_frame", - "video:video_rtp_headers", "video_codecs:video_codecs_api", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -808,6 +806,15 @@ rtc_source_set("rtp_transceiver_direction") { sources = [ "rtp_transceiver_direction.h" ] } +rtc_source_set("payload_type") { + visibility = [ "*" ] + sources = [ "payload_type.h" ] + deps = [ + "../rtc_base:strong_alias", + "//third_party/abseil-cpp/absl/strings:str_format", + ] +} + rtc_library("priority") { visibility = [ "*" ] sources = [ @@ -830,7 +837,6 @@ rtc_library("rtp_parameters") { "rtp_parameters.h", ] deps = [ - ":array_view", ":priority", ":rtc_error", ":rtp_transceiver_direction", @@ -900,39 +906,11 @@ rtc_source_set("peer_connection_quality_test_fixture_api") { sources = [ "test/peerconnection_quality_test_fixture.h" ] deps = [ - ":array_view", - ":audio_quality_analyzer_api", - ":fec_controller_api", - ":frame_generator_api", - ":function_view", - ":media_stream_interface", - ":network_state_predictor_api", - ":packet_socket_factory", - ":rtp_parameters", - ":simulated_network_api", ":stats_observer_interface", ":track_id_stream_info_map", - ":video_quality_analyzer_api", - "../media:media_constants", - "../rtc_base:checks", - "../rtc_base:network", - "../rtc_base:rtc_certificate_generator", - "../rtc_base:ssl", - "../rtc_base:stringutils", - "../rtc_base:threading", - "../test:fileutils", - "audio:audio_mixer_api", - "audio:audio_processing", - "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", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings:string_view", @@ -980,10 +958,6 @@ if (rtc_include_tests) { ":network_state_predictor_api", ":rtp_parameters", ":simulated_network_api", - "../call:fake_network", - "../call:rtp_interfaces", - "../test:test_common", - "../test:video_test_common", "../video/config:encoder_config", "transport:bitrate_settings", "transport:network_control", @@ -1038,7 +1012,6 @@ rtc_library("create_frame_generator") { "../system_wrappers", "../test:frame_generator_impl", "environment", - "environment:environment_factory", "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -1065,10 +1038,7 @@ rtc_library("create_peer_connection_quality_test_frame_generator") { rtc_source_set("data_channel_event_observer_interface") { visibility = [ "*" ] sources = [ "data_channel_event_observer_interface.h" ] - deps = [ - ":array_view", - "//third_party/abseil-cpp/absl/strings:string_view", - ] + deps = [ "//third_party/abseil-cpp/absl/strings:string_view" ] } rtc_source_set("libjingle_logging_api") { @@ -1106,12 +1076,10 @@ rtc_source_set("rtc_stats_api") { ] deps = [ - ":make_ref_counted", ":ref_count", + ":refcountedbase", ":scoped_refptr", - "../api:refcountedbase", "../rtc_base:checks", - "../rtc_base:refcount", "../rtc_base/system:rtc_export", "units:timestamp", ] @@ -1125,7 +1093,6 @@ rtc_library("audio_options_api") { ] deps = [ - ":array_view", "../rtc_base:stringutils", "../rtc_base/system:rtc_export", ] @@ -1137,11 +1104,7 @@ rtc_library("transport_api") { "call/transport.cc", "call/transport.h", ] - deps = [ - ":array_view", - ":refcountedbase", - ":scoped_refptr", - ] + deps = [] } rtc_source_set("bitrate_allocation") { @@ -1158,7 +1121,6 @@ rtc_source_set("simulated_network_api") { visibility = [ "*" ] sources = [ "test/simulated_network.h" ] deps = [ - "../rtc_base:random", "transport:ecn_marking", "units:data_rate", "units:data_size", @@ -1175,15 +1137,12 @@ rtc_library("network_emulation_manager_api") { "test/network_emulation_manager.h", ] deps = [ - ":array_view", ":field_trials_view", - ":packet_socket_factory", ":peer_network_dependencies", ":simulated_network_api", ":time_controller", "../rtc_base:checks", "../rtc_base:ip_address", - "../rtc_base:network", "../rtc_base:network_constants", "../rtc_base:socket_address", "../test/network:simulated_network", @@ -1206,11 +1165,9 @@ rtc_library("time_controller") { ":function_view", "../rtc_base:socket_server", "../rtc_base:threading", - "../rtc_base/synchronization:yield_policy", "../system_wrappers", "task_queue", "units:time_delta", - "units:timestamp", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -1238,10 +1195,6 @@ rtc_source_set("network_state_predictor_api") { rtc_source_set("array_view") { visibility = [ "*" ] sources = [ "array_view.h" ] - deps = [ - "../rtc_base:checks", - "../rtc_base:type_traits", - ] } rtc_source_set("refcountedbase") { @@ -1262,18 +1215,13 @@ rtc_library("ice_transport_factory") { deps = [ ":ice_transport_interface", ":make_ref_counted", - ":packet_socket_factory", ":scoped_refptr", ":sequence_checker", - "../p2p:connection", "../p2p:ice_transport_internal", "../p2p:p2p_constants", "../p2p:p2p_transport_channel", - "../p2p:port_allocator", "../rtc_base:macromagic", - "../rtc_base:threading", "../rtc_base/system:rtc_export", - "rtc_event_log", ] } @@ -1307,13 +1255,10 @@ rtc_library("datagram_connection") { visibility = [ "*" ] sources = [ "datagram_connection.h" ] deps = [ - ":array_view", ":candidate", ":ref_count", "../p2p:transport_description", - "../rtc_base:macromagic", "../rtc_base/system:rtc_export", - "environment", "units:timestamp", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings:string_view", @@ -1329,11 +1274,9 @@ rtc_library("datagram_connection_factory") { deps = [ ":datagram_connection", ":make_ref_counted", - ":ref_count", ":scoped_refptr", "../p2p:port_allocator", "../pc:datagram_connection_internal", - "../rtc_base:macromagic", "../rtc_base:ssl", "../rtc_base/system:rtc_export", "environment", @@ -1352,8 +1295,6 @@ if (rtc_include_tests) { ] deps = [ - ":scoped_refptr", - "../modules/audio_processing", "../modules/audio_processing:audioproc_f_impl", "audio:audio_processing", "audio:builtin_audio_processing_builder", @@ -1371,7 +1312,6 @@ if (rtc_include_tests) { deps = [ ":neteq_simulator_api", "../modules/audio_coding:neteq_test_factory", - "../rtc_base:checks", "neteq:neteq_api", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", @@ -1408,9 +1348,9 @@ if (rtc_include_tests) { "test/videocodec_test_stats.h", ] deps = [ - "../api/units:data_rate", - "../api/units:frequency", "../rtc_base:stringutils", + "units:data_rate", + "units:frequency", "video:video_frame_type", ] } @@ -1423,7 +1363,6 @@ if (rtc_include_tests) { ":field_trials", ":videocodec_test_stats_api", "../modules/video_coding:codec_globals_headers", - "../modules/video_coding:video_codec_interface", "video:encoded_image", "video:video_frame", "video_codecs:video_codecs_api", @@ -1439,7 +1378,6 @@ if (rtc_include_tests) { ] deps = [ ":videocodec_test_fixture_api", - "../modules/video_coding:video_codecs_test_framework", "../modules/video_coding:videocodec_test_impl", "video_codecs:video_codecs_api", ] @@ -1463,7 +1401,7 @@ if (rtc_include_tests) { sources = [ "test/mock_audio_sink.h" ] deps = [ - "../api:media_stream_interface", + ":media_stream_interface", "../test:test_support", ] } @@ -1513,10 +1451,7 @@ if (rtc_include_tests) { testonly = true sources = [ "test/mock_frame_encryptor.h" ] deps = [ - ":array_view", ":rtp_parameters", - - # For api/crypto/frame_encryptor_interface.h "../test:test_support", "crypto:frame_encryptor_interface", ] @@ -1527,7 +1462,6 @@ if (rtc_include_tests) { testonly = true sources = [ "test/mock_frame_decryptor.h" ] deps = [ - ":array_view", ":rtp_parameters", "../test:test_support", "crypto:frame_decryptor_interface", @@ -1550,10 +1484,10 @@ if (rtc_include_tests) { testonly = true sources = [ "test/mock_encoder_selector.h" ] deps = [ - "../api/video_codecs:video_codecs_api", "../test:test_support", "units:data_rate", "video:render_resolution", + "video_codecs:video_codecs_api", ] } @@ -1565,9 +1499,6 @@ if (rtc_include_tests) { "test/fake_frame_encryptor.h", ] deps = [ - ":array_view", - ":make_ref_counted", - ":ref_count", ":rtp_parameters", "../rtc_base:checks", "../rtc_base:refcount", @@ -1583,8 +1514,6 @@ if (rtc_include_tests) { "test/fake_frame_decryptor.h", ] deps = [ - ":array_view", - ":make_ref_counted", ":rtp_parameters", "../rtc_base:checks", "crypto:frame_decryptor_interface", @@ -1626,7 +1555,6 @@ if (rtc_include_tests) { sources = [ "test/mock_peerconnectioninterface.h" ] deps = [ - ":candidate", ":data_channel_event_observer_interface", ":data_channel_interface", ":dtls_transport_interface", @@ -1635,24 +1563,21 @@ if (rtc_include_tests) { ":make_ref_counted", ":media_stream_interface", ":peer_connection_interface", - ":ref_count", ":rtc_error", ":rtc_stats_api", ":rtp_parameters", ":rtp_receiver_interface", ":rtp_sender_interface", ":rtp_transceiver_interface", + ":scoped_refptr", ":sctp_transport_interface", - ":set_local_description_observer_interface", ":set_remote_description_observer_interface", - "../api:scoped_refptr", "../rtc_base:refcount", "../rtc_base:threading", "../test:test_support", "adaptation:resource_adaptation_api", "transport:bandwidth_estimation_settings", "transport:bitrate_settings", - "transport:network_control", ] } @@ -1679,7 +1604,6 @@ if (rtc_include_tests) { testonly = true sources = [ "test/mock_transformable_frame.h" ] deps = [ - ":array_view", ":frame_transformer_interface", "../test:test_support", "units:time_delta", @@ -1725,7 +1649,6 @@ if (rtc_include_tests) { ] deps = [ - ":array_view", ":dtls_transport_interface", ":dtmf_sender_interface", ":frame_transformer_interface", @@ -1738,9 +1661,9 @@ if (rtc_include_tests) { ":rtp_transceiver_direction", ":rtp_transceiver_interface", ":scoped_refptr", - "../api/crypto:frame_decryptor_interface", "../rtc_base:refcount", "../test:test_support", + "crypto:frame_decryptor_interface", "crypto:frame_encryptor_interface", "transport/rtp:rtp_source", "video_codecs:video_codecs_api", @@ -1753,11 +1676,10 @@ if (rtc_include_tests) { sources = [ "test/mock_transformable_audio_frame.h" ] deps = [ - ":array_view", ":frame_transformer_interface", - "../api/units:timestamp", "../test:test_support", "units:time_delta", + "units:timestamp", ] } @@ -1767,7 +1689,6 @@ if (rtc_include_tests) { sources = [ "test/mock_transformable_video_frame.h" ] deps = [ - ":array_view", ":frame_transformer_interface", "../test:test_support", "units:time_delta", @@ -1782,9 +1703,9 @@ if (rtc_include_tests) { sources = [ "test/mock_video_bitrate_allocator.h" ] deps = [ - "../api/video:video_bitrate_allocator", "../test:test_support", "video:video_bitrate_allocation", + "video:video_bitrate_allocator", ] } @@ -1794,10 +1715,10 @@ if (rtc_include_tests) { sources = [ "test/mock_video_bitrate_allocator_factory.h" ] deps = [ - "../api/video:video_bitrate_allocator_factory", "../test:test_support", "environment", "video:video_bitrate_allocator", + "video:video_bitrate_allocator_factory", "video_codecs:video_codecs_api", ] } @@ -1811,9 +1732,9 @@ if (rtc_include_tests) { ] deps = [ - "../api/video_codecs:video_codecs_api", "../test:test_support", "environment", + "video_codecs:video_codecs_api", ] } @@ -1823,10 +1744,10 @@ if (rtc_include_tests) { sources = [ "test/mock_video_decoder.h" ] deps = [ - "../api/video_codecs:video_codecs_api", "../test:test_support", "video:encoded_image", "video:video_frame", + "video_codecs:video_codecs_api", ] } @@ -1837,11 +1758,11 @@ if (rtc_include_tests) { deps = [ ":fec_controller_api", - "../api/video_codecs:video_codecs_api", "../test:test_support", "video:encoded_image", "video:video_frame", "video:video_frame_type", + "video_codecs:video_codecs_api", ] } @@ -1851,9 +1772,8 @@ if (rtc_include_tests) { sources = [ "test/mock_video_track.h" ] deps = [ - ":ref_count", - "../api:media_stream_interface", - "../api:scoped_refptr", + ":media_stream_interface", + ":scoped_refptr", "../rtc_base:refcount", "../test:test_support", "video:video_frame", @@ -1865,7 +1785,6 @@ if (rtc_include_tests) { testonly = true sources = [ "test/mock_datagram_connection.h" ] deps = [ - ":array_view", ":candidate", ":datagram_connection", "../p2p:transport_description", @@ -1880,7 +1799,6 @@ if (rtc_include_tests) { testonly = true sources = [ "test/mock_datagram_connection_observer.h" ] deps = [ - ":array_view", ":candidate", ":datagram_connection", "//test:test_support", @@ -1944,6 +1862,7 @@ if (rtc_include_tests) { "../media:media_constants", "../media:rid_description", "../media:stream_params", + "../modules/rtp_rtcp:rtp_rtcp_format", "../p2p:p2p_constants", "../p2p:transport_description", "../p2p:transport_info", @@ -2017,11 +1936,9 @@ 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_transformable_audio_frame", ":mock_transformable_frame", ":mock_transformable_video_frame", ":mock_video_bitrate_allocator", @@ -2044,7 +1961,7 @@ rtc_library("field_trials_registry") { ] deps = [ ":field_trials_view", - "../experiments:registered_field_trials", + "../experiments:registered_field_trials", # keep "../rtc_base:checks", "../rtc_base:logging", "../rtc_base/containers:flat_set", @@ -2095,14 +2012,10 @@ rtc_library("frame_transformer_factory") { ] deps = [ ":frame_transformer_interface", - ":ref_count", - ":scoped_refptr", "../audio", "../modules/rtp_rtcp", "../rtc_base:checks", "../rtc_base/system:rtc_export", - "video:encoded_frame", - "video:video_frame_metadata", ] } diff --git a/api/DEPS b/api/DEPS index f5d4fb8db95..90b81d2deeb 100644 --- a/api/DEPS +++ b/api/DEPS @@ -1,6 +1,7 @@ # This is supposed to be a complete list of top-level directories, # excepting only api/ itself. include_rules = [ + "-.agents", "-agents", "-audio", "-base", @@ -45,105 +46,110 @@ include_rules = [ specific_include_rules = { # Some internal headers are allowed even in API headers: - ".*\.h": [ + ".*\\.h": [ "+rtc_base/checks.h", "+rtc_base/system/rtc_export.h", "+rtc_base/system/rtc_export_template.h", "+rtc_base/units/unit_base.h", ], - "array_view\.h": [ + "array_view\\.h": [ "+rtc_base/type_traits.h", ], # Needed because AudioEncoderOpus is in the wrong place for # backwards compatibilty reasons. See # https://bugs.chromium.org/p/webrtc/issues/detail?id=7847 - "audio_encoder_opus\.h": [ + "audio_encoder_opus\\.h": [ "+modules/audio_coding/codecs/opus/audio_encoder_opus.h", ], - "async_resolver_factory\.h": [ + "async_resolver_factory\\.h": [ "+rtc_base/async_resolver_interface.h", ], - "async_dns_resolver\.h": [ + "async_dns_resolver\\.h": [ "+rtc_base/socket_address.h", ], - "audio_device_defines\.h": [ + "audio_device_defines\\.h": [ "+rtc_base/strings/string_builder.h", ], - "audio_format\.h": [ + "audio_format\\.h": [ "+rtc_base/strings/string_builder.h", ], - "candidate\.h": [ + "candidate\\.h": [ "+rtc_base/network_constants.h", "+rtc_base/socket_address.h", ], - "create_peerconnection_factory\.h": [ + "create_peerconnection_factory\\.h": [ "+rtc_base/thread.h", ], - "data_channel_interface\.h": [ + "data_channel_interface\\.h": [ "+rtc_base/copy_on_write_buffer.h", ], - "data_channel_transport_interface\.h": [ + "data_channel_transport_interface\\.h": [ "+rtc_base/copy_on_write_buffer.h", ], - "datagram_connection\.h": [ + "datagram_connection\\.h": [ "+p2p/base/transport_description.h", ], - "mock_datagram_connection\.h": [ + "mock_datagram_connection\\.h": [ "+p2p/base/transport_description.h", ], - "datagram_connection_factory\.h": [ + "datagram_connection_factory\\.h": [ "+p2p/base/port_allocator.h", "+rtc_base/rtc_certificate.h", ], - "dtls_transport_interface\.h": [ + "dtls_transport_interface\\.h": [ "+rtc_base/ssl_certificate.h", ], - "fec_controller\.h": [ + "fec_controller\\.h": [ "+modules/include/module_fec_types.h", ], - "ice_server_parsing\.h": [ + "ice_server_parsing\\.h": [ "+p2p/base/port_allocator.h", "+rtc_base/socket_address.h", ], - "jsep\.h": [ + "jsep\\.h": [ "+absl/strings/has_absl_stringify.h", "+absl/strings/str_format.h", "+rtc_base/system/no_unique_address.h", "+rtc_base/thread_annotations.h", ], - "local_network_access_permission\.h": [ + "local_network_access_permission\\.h": [ "+rtc_base/socket_address.h", ], - "packet_socket_factory\.h": [ + "packet_socket_factory\\.h": [ "+rtc_base/async_packet_socket.h", "+rtc_base/socket_address.h", "+rtc_base/ssl_certificate.h", ], - "turn_customizer\.h": [ + "payload_type\\.h": [ + "+absl/strings/str_format.h", + "+rtc_base/strong_alias.h", + ], + + "turn_customizer\\.h": [ "+p2p/base/port_interface.h", ], - "peer_connection_interface\.h": [ + "peer_connection_interface\\.h": [ "+media/base/media_config.h", "+media/base/media_engine.h", "+p2p/base/port.h", @@ -158,128 +164,134 @@ specific_include_rules = { "+rtc_base/socket_factory.h", "+rtc_base/ssl_certificate.h", "+rtc_base/ssl_stream_adapter.h", + "+rtc_base/system/plan_b_only.h", "+rtc_base/thread.h", ], - "proxy\.h": [ + "proxy\\.h": [ "+rtc_base/event.h", "+rtc_base/message_handler.h", # Inherits from it. "+rtc_base/thread.h", ], - "ref_counted_base\.h": [ + "ref_counted_base\\.h": [ "+rtc_base/ref_counter.h", ], - "rtc_error\.h": [ + "rtc_error\\.h": [ "+rtc_base/logging.h", "+rtc_base/strings/string_builder.h", "+absl/strings/has_absl_stringify.h", "+absl/strings/str_format.h", ], - "rtc_event_log_output_file.h": [ + "rtc_event_log_output_file\\.h": [ # For private member and constructor. "+rtc_base/system/file_wrapper.h", ], - "legacy_stats_types\.h": [ + "legacy_stats_types\\.h": [ "+rtc_base/thread_annotations.h", "+rtc_base/thread_checker.h", ], - "audio_decoder\.h": [ + "audio_decoder\\.h": [ "+rtc_base/buffer.h", ], - "audio_encoder\.h": [ + "audio_encoder\\.h": [ "+rtc_base/buffer.h", ], - "make_ref_counted\.h": [ + "make_ref_counted\\.h": [ "+rtc_base/ref_counted_object.h", ], - "mock.*\.h": [ + "mock.*\\.h": [ "+test/gmock.h", ], - "mock_peerconnectioninterface\.h": [ + "mock_peerconnectioninterface\\.h": [ "+rtc_base/ref_counted_object.h", ], - "mock_video_track\.h": [ + "mock_video_track\\.h": [ "+rtc_base/ref_counted_object.h", ], - "notifier\.h": [ + "notifier\\.h": [ "+rtc_base/system/no_unique_address.h", "+rtc_base/thread_annotations.h", ], - "priority\.h": [ + "priority\\.h": [ "+rtc_base/strong_alias.h", ], - "sctp_transport_interface\.h": [ + "sctp_transport_interface\\.h": [ "+absl/strings/str_format.h", ], - "simulated_network\.h": [ + "simulated_network\\.h": [ "+rtc_base/random.h", "+rtc_base/thread_annotations.h", ], - "time_controller\.h": [ + "time_controller\\.h": [ "+rtc_base/thread.h", ], - "videocodec_test_fixture\.h": [ + "videocodec_test_fixture\\.h": [ "+modules/video_coding/include/video_codec_interface.h" ], - "rtp_parameters\.h": [ + "rtp_parameters\\.h": [ "+absl/strings/str_format.h", + "+rtc_base/strings/str_join.h" ], - "sequence_checker\.h": [ + "sequence_checker\\.h": [ "+rtc_base/synchronization/sequence_checker_internal.h", "+rtc_base/thread_annotations.h", ], - "video_encoder_factory_template.*\.h": [ + "video_encoder_factory_template.*\\.h": [ "+modules/video_coding", ], - "video_encoder_factory_interface\.h": [ + "video_encoder_factory_interface\\.h": [ "+rtc_base/numerics", ], - "video_encoder_interface\.h": [ + "video_encoder_interface\\.h": [ "+rtc_base/numerics", ], - "simple_encoder_wrapper\.h": [ + "video_quality_test_fixture\\.h": [ + "+video/config/video_encoder_config.h", + ], + + "simple_encoder_wrapper\\.h": [ "+common_video", "+modules", ], - "video_decoder_factory_template.*\.h": [ + "video_decoder_factory_template.*\\.h": [ "+modules/video_coding", ], - "field_trials\.h": [ + "field_trials\\.h": [ "+rtc_base/containers/flat_map.h", ], - "video_track_source_proxy_factory.h": [ + "video_track_source_proxy_factory\\.h": [ "+rtc_base/thread.h", ], - "field_trials_registry\.h": [ + "field_trials_registry\\.h": [ "+rtc_base/containers/flat_set.h", ], - "ice_transport_factory\.h": [ + "ice_transport_factory\\.h": [ "+p2p/base/port_allocator.h", ], @@ -287,7 +299,7 @@ specific_include_rules = { # 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 # do not since no one #includes them.) - ".*\.cc": [ + ".*\\.cc": [ "+audio", "+call", "+common_audio", diff --git a/api/OWNERS b/api/OWNERS index ebaea3b1e70..0ef7a47c7b8 100644 --- a/api/OWNERS +++ b/api/OWNERS @@ -1,5 +1,4 @@ hta@webrtc.org -magjed@webrtc.org perkj@webrtc.org tommi@webrtc.org @@ -7,6 +6,8 @@ tommi@webrtc.org deadbeef@webrtc.org per-file peer_connection*=hbos@webrtc.org +per-file peer_connection*=eshr@webrtc.org +per-file peer_connection*=guidou@webrtc.org per-file DEPS=mbonadei@webrtc.org @@ -16,6 +17,7 @@ per-file uma_metrics.h=kron@webrtc.org per-file webrtc_sdp.cc = set noparent per-file webrtc_sdp.cc = hta@webrtc.org per-file webrtc_sdp.cc = hbos@webrtc.org +per-file webrtc_sdp.cc = eshr@webrtc.org # If none of the above are present, may also try one of these per-file webrtc_sdp.cc = tommi@webrtc.org per-file webrtc_sdp.cc = guidou@webrtc.org diff --git a/api/array_view.h b/api/array_view.h index e8f4cf4ae2f..8414ac04719 100644 --- a/api/array_view.h +++ b/api/array_view.h @@ -11,331 +11,20 @@ #ifndef API_ARRAY_VIEW_H_ #define API_ARRAY_VIEW_H_ -#include -#include #include -#include -#include - -#include "rtc_base/checks.h" -#include "rtc_base/type_traits.h" +#include namespace webrtc { -// tl;dr: ArrayView is the same thing as gsl::span from the Guideline -// Support Library. -// -// Many functions read from or write to arrays. The obvious way to do this is -// to use two arguments, a pointer to the first element and an element count: -// -// bool Contains17(const int* arr, size_t size) { -// for (size_t i = 0; i < size; ++i) { -// if (arr[i] == 17) -// return true; -// } -// return false; -// } -// -// This is flexible, since it doesn't matter how the array is stored (C array, -// std::vector, Buffer, ...), but it's error-prone because the caller -// has to correctly specify the array length: -// -// Contains17(arr, std::size(arr)); // C array -// Contains17(arr.data(), arr.size()); // std::vector -// Contains17(arr, size); // pointer + size -// ... -// -// It's also kind of messy to have two separate arguments for what is -// conceptually a single thing. -// -// Enter ArrayView. It contains a T pointer (to an array it doesn't -// own) and a count, and supports the basic things you'd expect, such as -// indexing and iteration. It allows us to write our function like this: -// -// bool Contains17(ArrayView arr) { -// for (auto e : arr) { -// if (e == 17) -// return true; -// } -// return false; -// } -// -// And even better, because a bunch of things will implicitly convert to -// ArrayView, we can call it like this: -// -// Contains17(arr); // C array -// Contains17(arr); // std::vector -// Contains17(ArrayView(arr, size)); // pointer + size -// Contains17(nullptr); // nullptr -> empty ArrayView -// ... -// -// ArrayView stores both a pointer and a size, but you may also use -// ArrayView, which has a size that's fixed at compile time (which means -// it only has to store the pointer). -// -// One important point is that ArrayView and ArrayView are -// different types, which allow and don't allow mutation of the array elements, -// respectively. The implicit conversions work just like you'd hope, so that -// e.g. vector will convert to either ArrayView or ArrayView, but const vector will convert only to ArrayView. -// (ArrayView itself can be the source type in such conversions, so -// ArrayView will convert to ArrayView.) -// -// Note: ArrayView is tiny (just a pointer and a count if variable-sized, just -// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to -// pass it by value than by const reference. - -namespace array_view_internal { - -// Magic constant for indicating that the size of an ArrayView is variable -// instead of fixed. -enum : std::ptrdiff_t { kArrayViewVarSize = -4711 }; - -// Base class for ArrayViews of fixed nonzero size. -template -class ArrayViewBase { - static_assert(Size > 0, "ArrayView size must be variable or non-negative"); - - public: - ArrayViewBase(T* data, size_t /* size */) : data_(data) {} - - static constexpr size_t size() { return Size; } - static constexpr bool empty() { return false; } - T* data() const { return data_; } - - protected: - static constexpr bool fixed_size() { return true; } - - private: - T* data_; -}; - -// Specialized base class for ArrayViews of fixed zero size. -template -class ArrayViewBase { - public: - explicit ArrayViewBase(T* /* data */, size_t /* size */) {} - - static constexpr size_t size() { return 0; } - static constexpr bool empty() { return true; } - T* data() const { return nullptr; } - - protected: - static constexpr bool fixed_size() { return true; } -}; - -// Specialized base class for ArrayViews of variable size. -template -class ArrayViewBase { - public: - ArrayViewBase(T* data, size_t size) - : data_(size == 0 ? nullptr : data), size_(size) {} - - size_t size() const { return size_; } - bool empty() const { return size_ == 0; } - T* data() const { return data_; } - - protected: - static constexpr bool fixed_size() { return false; } - - private: - T* data_; - size_t size_; -}; - -} // namespace array_view_internal - -template -class ArrayView final : public array_view_internal::ArrayViewBase { - public: - using value_type = T; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = value_type*; - using const_pointer = const value_type*; - using const_iterator = const T*; - - // Construct an ArrayView from a pointer and a length. - template - ArrayView(U* data, size_t size) - : array_view_internal::ArrayViewBase::ArrayViewBase(data, size) { - RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data()); - RTC_DCHECK_EQ(size, this->size()); - RTC_DCHECK_EQ(!this->data(), - this->size() == 0); // data is null iff size == 0. - } - - // Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0 - // cannot be empty. - ArrayView() : ArrayView(nullptr, 0) {} - ArrayView(std::nullptr_t) // NOLINT - : ArrayView() {} - ArrayView(std::nullptr_t, size_t size) - : ArrayView(static_cast(nullptr), size) { - static_assert(Size == 0 || Size == array_view_internal::kArrayViewVarSize, - ""); - RTC_DCHECK_EQ(0, size); - } - - // Construct an ArrayView from a C-style array. - template - ArrayView(U (&array)[N]) // NOLINT - : ArrayView(array, N) { - static_assert(Size == N || Size == array_view_internal::kArrayViewVarSize, - "Array size must match ArrayView size"); - } - - // (Only if size is fixed.) Construct a fixed size ArrayView from a - // non-const std::array instance. For an ArrayView with variable size, the - // used ctor is ArrayView(U& u) instead. - template (N)>::type* = nullptr> - ArrayView(std::array& u) // NOLINT - : ArrayView(u.data(), u.size()) {} - - // (Only if size is fixed.) Construct a fixed size ArrayView where T is - // const from a const(expr) std::array instance. For an ArrayView with - // variable size, the used ctor is ArrayView(U& u) instead. - template (N)>::type* = nullptr> - ArrayView(const std::array& u) // NOLINT - : ArrayView(u.data(), u.size()) {} - - // (Only if size is fixed.) Construct an ArrayView from any type U that has a - // static constexpr size() method whose return value is equal to Size, and a - // data() method whose return value converts implicitly to T*. In particular, - // this means we allow conversion from ArrayView to ArrayView, but not the other way around. We also don't allow conversion from - // ArrayView to ArrayView, or from ArrayView to ArrayView when M != N. - template > && - Size != array_view_internal::kArrayViewVarSize && - HasDataAndSize::value>* = nullptr> - ArrayView(U& u) // NOLINT - : ArrayView(u.data(), u.size()) { - static_assert(U::size() == Size, "Sizes must match exactly"); - } - - template > && - Size != array_view_internal::kArrayViewVarSize && - HasDataAndSize::value>* = nullptr> - ArrayView(const U& u) // NOLINT(runtime/explicit) - : ArrayView(u.data(), u.size()) { - static_assert(U::size() == Size, "Sizes must match exactly"); - } - - // (Only if size is variable.) Construct an ArrayView from any type U that - // has a size() method whose return value converts implicitly to size_t, and - // a data() method whose return value converts implicitly to T*. In - // particular, this means we allow conversion from ArrayView to - // ArrayView, but not the other way around. Other allowed - // conversions include - // ArrayView to ArrayView or ArrayView, - // std::vector to ArrayView or ArrayView, - // const std::vector to ArrayView, - // Buffer to ArrayView or ArrayView, and - // const Buffer to ArrayView. - template > && - Size == array_view_internal::kArrayViewVarSize && - HasDataAndSize::value>* = nullptr> - ArrayView(U& u) // NOLINT - : ArrayView(u.data(), u.size()) {} - - template > && - Size == array_view_internal::kArrayViewVarSize && - HasDataAndSize::value>* = nullptr> - ArrayView(const U& u) // NOLINT(runtime/explicit) - : ArrayView(u.data(), u.size()) {} - - // Indexing and iteration. These allow mutation even if the ArrayView is - // const, because the ArrayView doesn't own the array. (To prevent mutation, - // use a const element type.) - T& operator[](size_t idx) const { - RTC_DCHECK_LT(idx, this->size()); - RTC_DCHECK(this->data()); - return this->data()[idx]; - } - T* begin() const { return this->data(); } - T* end() const { return this->data() + this->size(); } - const T* cbegin() const { return this->data(); } - const T* cend() const { return this->data() + this->size(); } - std::reverse_iterator rbegin() const { - return std::make_reverse_iterator(end()); - } - std::reverse_iterator rend() const { - return std::make_reverse_iterator(begin()); - } - std::reverse_iterator crbegin() const { - return std::make_reverse_iterator(cend()); - } - std::reverse_iterator crend() const { - return std::make_reverse_iterator(cbegin()); - } - - ArrayView subview(size_t offset, size_t size) const { - return offset < this->size() - ? ArrayView(this->data() + offset, - std::min(size, this->size() - offset)) - : ArrayView(); - } - ArrayView subview(size_t offset) const { - return subview(offset, this->size()); - } -}; - -// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not* -// dereference the pointers. -template -bool operator==(const ArrayView& a, const ArrayView& b) { - return a.data() == b.data() && a.size() == b.size(); -} -template -bool operator!=(const ArrayView& a, const ArrayView& b) { - return !(a == b); -} - -// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews -// are the size of one pointer. (And as a special case, fixed-size ArrayViews -// of size 0 require no storage.) -static_assert(sizeof(ArrayView) == 2 * sizeof(int*), ""); -static_assert(sizeof(ArrayView) == sizeof(int*), ""); -static_assert(std::is_empty>::value, ""); +template +using ArrayView = std::span; +// TODO: bugs.webrtc.org/439801349 - deprecate when unused by WebRTC template inline ArrayView MakeArrayView(T* data, size_t size) { return ArrayView(data, size); } -// Only for primitive types that have the same size and aligment. -// Allow reinterpret cast of the array view to another primitive type of the -// same size. -// Template arguments order is (U, T, Size) to allow deduction of the template -// arguments in client calls: reinterpret_array_view(array_view). -template -inline ArrayView reinterpret_array_view(ArrayView view) { - static_assert(sizeof(U) == sizeof(T) && alignof(U) == alignof(T), - "ArrayView reinterpret_cast is only supported for casting " - "between views that represent the same chunk of memory."); - static_assert( - std::is_fundamental::value && std::is_fundamental::value, - "ArrayView reinterpret_cast is only supported for casting between " - "fundamental types."); - return ArrayView(reinterpret_cast(view.data()), view.size()); -} - } // namespace webrtc diff --git a/api/array_view_unittest.cc b/api/array_view_unittest.cc index f424cd2ff6c..ae0f830a82b 100644 --- a/api/array_view_unittest.cc +++ b/api/array_view_unittest.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -70,10 +71,10 @@ TEST(ArrayViewDeathTest, TestConstructFromPtrAndArray) { EXPECT_EQ(arr, wf.data()); ArrayView q(arr, 0); EXPECT_EQ(0u, q.size()); - EXPECT_EQ(nullptr, q.data()); + EXPECT_TRUE(q.empty()); ArrayView qf(arr, 0); static_assert(qf.size() == 0, ""); - EXPECT_EQ(nullptr, qf.data()); + EXPECT_TRUE(qf.empty()); #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) // DCHECK error (nullptr with nonzero size). EXPECT_DEATH(ArrayView(static_cast(nullptr), 5), ""); @@ -428,7 +429,8 @@ TEST(ArrayViewDeathTest, TestIndexing) { EXPECT_EQ('Y', y[2]); EXPECT_EQ('X', z[3]); #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) - EXPECT_DEATH(z[8], ""); // DCHECK error (index out of bounds). + // DCHECK error (index out of bounds). + EXPECT_DEATH(std::ignore = z[8], ""); #endif } @@ -436,7 +438,6 @@ TEST(ArrayViewTest, TestIterationEmpty) { // Variable-size. ArrayView>>> av; EXPECT_EQ(av.begin(), av.end()); - EXPECT_EQ(av.cbegin(), av.cend()); for (auto& e : av) { EXPECT_TRUE(false); EXPECT_EQ(42u, e.size()); // Dummy use of e to prevent unused var warning. @@ -445,7 +446,6 @@ TEST(ArrayViewTest, TestIterationEmpty) { // Fixed-size. ArrayView>>, 0> af; EXPECT_EQ(af.begin(), af.end()); - EXPECT_EQ(af.cbegin(), af.cend()); for (auto& e : af) { EXPECT_TRUE(false); EXPECT_EQ(42u, e.size()); // Dummy use of e to prevent unused var warning. @@ -456,13 +456,11 @@ TEST(ArrayViewTest, TestReverseIterationEmpty) { // Variable-size. ArrayView>>> av; EXPECT_EQ(av.rbegin(), av.rend()); - EXPECT_EQ(av.crbegin(), av.crend()); EXPECT_TRUE(av.empty()); // Fixed-size. ArrayView>>, 0> af; EXPECT_EQ(af.begin(), af.end()); - EXPECT_EQ(af.cbegin(), af.cend()); EXPECT_TRUE(af.empty()); } @@ -470,9 +468,7 @@ TEST(ArrayViewTest, TestIterationVariable) { char arr[] = "Arrr!"; ArrayView av(arr); EXPECT_EQ('A', *av.begin()); - EXPECT_EQ('A', *av.cbegin()); EXPECT_EQ('\0', *(av.end() - 1)); - EXPECT_EQ('\0', *(av.cend() - 1)); char i = 0; for (auto& e : av) { EXPECT_EQ(arr + i, &e); @@ -491,16 +487,9 @@ TEST(ArrayViewTest, TestReverseIterationVariable) { char arr[] = "Arrr!"; ArrayView av(arr); EXPECT_EQ('\0', *av.rbegin()); - EXPECT_EQ('\0', *av.crbegin()); EXPECT_EQ('A', *(av.rend() - 1)); - EXPECT_EQ('A', *(av.crend() - 1)); - const char* cit = av.cend() - 1; - for (auto crit = av.crbegin(); crit != av.crend(); ++crit, --cit) { - EXPECT_EQ(*cit, *crit); - } - - char* it = av.end() - 1; + auto it = av.end() - 1; for (auto rit = av.rbegin(); rit != av.rend(); ++rit, --it) { EXPECT_EQ(*it, *rit); } @@ -510,9 +499,7 @@ TEST(ArrayViewTest, TestIterationFixed) { char arr[] = "Arrr!"; ArrayView av(arr); EXPECT_EQ('A', *av.begin()); - EXPECT_EQ('A', *av.cbegin()); EXPECT_EQ('\0', *(av.end() - 1)); - EXPECT_EQ('\0', *(av.cend() - 1)); char i = 0; for (auto& e : av) { EXPECT_EQ(arr + i, &e); @@ -531,16 +518,9 @@ TEST(ArrayViewTest, TestReverseIterationFixed) { char arr[] = "Arrr!"; ArrayView av(arr); EXPECT_EQ('\0', *av.rbegin()); - EXPECT_EQ('\0', *av.crbegin()); EXPECT_EQ('A', *(av.rend() - 1)); - EXPECT_EQ('A', *(av.crend() - 1)); - const char* cit = av.cend() - 1; - for (auto crit = av.crbegin(); crit != av.crend(); ++crit, --cit) { - EXPECT_EQ(*cit, *crit); - } - - char* it = av.end() - 1; + auto it = av.end() - 1; for (auto rit = av.rbegin(); rit != av.rend(); ++rit, --it) { EXPECT_EQ(*it, *rit); } @@ -551,81 +531,84 @@ TEST(ArrayViewTest, TestEmpty) { const int a[] = {1, 2, 3}; EXPECT_FALSE(ArrayView(a).empty()); - static_assert(ArrayView::empty(), ""); - static_assert(!ArrayView::empty(), ""); + static_assert(ArrayView().empty()); } -TEST(ArrayViewTest, TestCompare) { +TEST(ArrayViewTest, TestSubSpanVariable) { int a[] = {1, 2, 3}; - int b[] = {1, 2, 3}; - - EXPECT_EQ(ArrayView(a), ArrayView(a)); - EXPECT_EQ((ArrayView(a)), (ArrayView(a))); - EXPECT_EQ(ArrayView(a), (ArrayView(a))); - EXPECT_EQ(ArrayView(), ArrayView()); - EXPECT_EQ(ArrayView(), ArrayView(a, 0)); - EXPECT_EQ(ArrayView(a, 0), ArrayView(b, 0)); - EXPECT_EQ((ArrayView(a, 0)), ArrayView()); - - EXPECT_NE(ArrayView(a), ArrayView(b)); - EXPECT_NE((ArrayView(a)), (ArrayView(b))); - EXPECT_NE((ArrayView(a)), ArrayView(b)); - EXPECT_NE(ArrayView(a), ArrayView()); - EXPECT_NE(ArrayView(a), ArrayView(a, 2)); - EXPECT_NE((ArrayView(a)), (ArrayView(a, 2))); + ArrayView av(a); + + EXPECT_THAT(av.subspan(0), ElementsAre(1, 2, 3)); + EXPECT_THAT(av.subspan(1), ElementsAre(2, 3)); + EXPECT_THAT(av.subspan(2), ElementsAre(3)); + EXPECT_THAT(av.subspan(3), IsEmpty()); + + EXPECT_THAT(av.subspan(1, 0), IsEmpty()); + EXPECT_THAT(av.subspan(1, 1), ElementsAre(2)); + EXPECT_THAT(av.subspan(1, 2), ElementsAre(2, 3)); } -TEST(ArrayViewTest, TestSubViewVariable) { +TEST(ArrayViewTest, TestSubSpanWithInvalidInput) { int a[] = {1, 2, 3}; ArrayView av(a); + EXPECT_DEATH_IF_SUPPORTED(std::ignore = av.subspan(4), ""); + EXPECT_DEATH_IF_SUPPORTED(std::ignore = av.subspan(1, 3), ""); +} - EXPECT_EQ(av.subview(0), av); +TEST(ArrayViewTest, SubspanFixed) { + std::array a = {1, 2, 3}; + ArrayView av(a); - EXPECT_THAT(av.subview(1), ElementsAre(2, 3)); - EXPECT_THAT(av.subview(2), ElementsAre(3)); - EXPECT_THAT(av.subview(3), IsEmpty()); - EXPECT_THAT(av.subview(4), IsEmpty()); + EXPECT_THAT(av.subspan<0>(), ElementsAre(1, 2, 3)); + EXPECT_THAT(av.subspan<1>(), ElementsAre(2, 3)); + EXPECT_THAT(av.subspan<2>(), ElementsAre(3)); + EXPECT_THAT(av.subspan<3>(), IsEmpty()); - EXPECT_THAT(av.subview(1, 0), IsEmpty()); - EXPECT_THAT(av.subview(1, 1), ElementsAre(2)); - EXPECT_THAT(av.subview(1, 2), ElementsAre(2, 3)); - EXPECT_THAT(av.subview(1, 3), ElementsAre(2, 3)); -} + EXPECT_THAT((av.subspan<1, 0>()), IsEmpty()); + EXPECT_THAT((av.subspan<1, 1>()), ElementsAre(2)); + EXPECT_THAT((av.subspan<1, 2>()), ElementsAre(2, 3)); -TEST(ArrayViewTest, TestSubViewFixed) { - int a[] = {1, 2, 3}; - ArrayView av(a); + EXPECT_EQ(av.subspan<1>().extent, 2u); + EXPECT_EQ((av.subspan<1, 1>().extent), 1u); - EXPECT_EQ(av.subview(0), av); + ArrayView dynamic_av(a); + EXPECT_THAT(dynamic_av.subspan<0>(), ElementsAre(1, 2, 3)); + EXPECT_THAT(dynamic_av.subspan<1>(), ElementsAre(2, 3)); + EXPECT_THAT(dynamic_av.subspan<2>(), ElementsAre(3)); + EXPECT_THAT(dynamic_av.subspan<3>(), IsEmpty()); - EXPECT_THAT(av.subview(1), ElementsAre(2, 3)); - EXPECT_THAT(av.subview(2), ElementsAre(3)); - EXPECT_THAT(av.subview(3), IsEmpty()); - EXPECT_THAT(av.subview(4), IsEmpty()); + EXPECT_THAT((dynamic_av.subspan<1, 0>()), IsEmpty()); + EXPECT_THAT((dynamic_av.subspan<1, 1>()), ElementsAre(2)); + EXPECT_THAT((dynamic_av.subspan<1, 2>()), ElementsAre(2, 3)); - EXPECT_THAT(av.subview(1, 0), IsEmpty()); - EXPECT_THAT(av.subview(1, 1), ElementsAre(2)); - EXPECT_THAT(av.subview(1, 2), ElementsAre(2, 3)); - EXPECT_THAT(av.subview(1, 3), ElementsAre(2, 3)); + EXPECT_EQ(dynamic_av.subspan<1>().extent, std::dynamic_extent); + EXPECT_EQ((dynamic_av.subspan<1, 1>()).extent, 1u); } -TEST(ArrayViewTest, TestReinterpretCastFixedSize) { - uint8_t bytes[] = {1, 2, 3}; - ArrayView uint8_av(bytes); - ArrayView int8_av = reinterpret_array_view(uint8_av); - EXPECT_EQ(int8_av.size(), uint8_av.size()); - EXPECT_EQ(int8_av[0], 1); - EXPECT_EQ(int8_av[1], 2); - EXPECT_EQ(int8_av[2], 3); +TEST(ArrayViewTest, FirstFixed) { + std::array a = {1, 2, 3}; + ArrayView av(a); + + EXPECT_THAT(av.first<0>(), IsEmpty()); + EXPECT_THAT(av.first<1>(), ElementsAre(1)); + EXPECT_THAT(av.first<2>(), ElementsAre(1, 2)); + EXPECT_THAT(av.first<3>(), ElementsAre(1, 2, 3)); + + EXPECT_EQ((av.first<0>()).extent, 0u); + EXPECT_EQ((av.first<2>()).extent, 2u); } -TEST(ArrayViewTest, TestReinterpretCastVariableSize) { - std::vector v = {1, 2, 3}; - ArrayView int8_av(v); - ArrayView uint8_av = reinterpret_array_view(int8_av); - EXPECT_EQ(int8_av.size(), uint8_av.size()); - EXPECT_EQ(uint8_av[0], 1); - EXPECT_EQ(uint8_av[1], 2); - EXPECT_EQ(uint8_av[2], 3); +TEST(ArrayViewTest, LastFixed) { + std::array a = {1, 2, 3}; + ArrayView av(a); + + EXPECT_THAT(av.last<0>(), IsEmpty()); + EXPECT_THAT(av.last<1>(), ElementsAre(3)); + EXPECT_THAT(av.last<2>(), ElementsAre(2, 3)); + EXPECT_THAT(av.last<3>(), ElementsAre(1, 2, 3)); + + EXPECT_EQ((av.last<0>()).extent, 0u); + EXPECT_EQ((av.last<2>()).extent, 2u); } + } // namespace webrtc diff --git a/api/audio/BUILD.gn b/api/audio/BUILD.gn index a5e6ce506d4..578930e2ebf 100644 --- a/api/audio/BUILD.gn +++ b/api/audio/BUILD.gn @@ -48,11 +48,11 @@ rtc_library("audio_frame_api") { ] deps = [ - "..:array_view", "..:rtp_packet_info", "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:timeutils", + "//third_party/abseil-cpp/absl/algorithm:container", ] } @@ -83,7 +83,6 @@ rtc_library("audio_processing") { ":aec3_config", ":audio_processing_statistics", ":echo_control", - "..:array_view", "..:ref_count", "..:scoped_refptr", "../../rtc_base:checks", @@ -175,10 +174,7 @@ rtc_source_set("echo_control") { rtc_source_set("neural_residual_echo_estimator_api") { visibility = [ "*" ] sources = [ "neural_residual_echo_estimator.h" ] - deps = [ - ":aec3_config", - "..:array_view", - ] + deps = [ ":aec3_config" ] } if (rtc_enable_protobuf) { diff --git a/api/audio/audio_frame.cc b/api/audio/audio_frame.cc index 04282394a4a..14afaded890 100644 --- a/api/audio/audio_frame.cc +++ b/api/audio/audio_frame.cc @@ -13,8 +13,9 @@ #include #include #include +#include -#include "api/array_view.h" +#include "absl/algorithm/container.h" #include "api/audio/audio_view.h" #include "api/audio/channel_layout.h" #include "api/rtp_packet_infos.h" @@ -102,7 +103,7 @@ void AudioFrame::CopyFrom(const AudioFrame& src) { // copying over new values. If we don't, msan might complain in some tests. // Consider locking down construction, avoiding the default constructor and // prefering construction that initializes all state. - ClearSamples(data_); + absl::c_fill(data_, 0); } timestamp_ = src.timestamp_; @@ -126,18 +127,16 @@ void AudioFrame::CopyFrom(const AudioFrame& src) { } const int16_t* AudioFrame::data() const { - return muted_ ? zeroed_data().begin() : data_.data(); + return muted_ ? zeroed_data().data() : data_.data(); } InterleavedView AudioFrame::data_view() const { // If you get a nullptr from `data_view()`, it's likely because the // samples_per_channel_ and/or num_channels_ members haven't been properly - // set. Since `data_view()` returns an InterleavedView<> (which internally - // uses ArrayView<>), we inherit the behavior in InterleavedView when - // the view size is 0 that ArrayView<>::data() returns nullptr. So, even when - // an AudioFrame is muted and we want to return `zeroed_data()`, if - // samples_per_channel_ or num_channels_ is 0, the view will point to - // nullptr. + // set. `data_view()` returns an InterleavedView<> which internally + // uses std::span<>. So, even when an AudioFrame is muted and we want to + // return `zeroed_data()`, if samples_per_channel_ or num_channels_ is 0, + // the view might point to nullptr. return InterleavedView(muted_ ? &zeroed_data()[0] : &data_[0], samples_per_channel_, num_channels_); } @@ -147,7 +146,7 @@ int16_t* AudioFrame::mutable_data() { // Consider instead if we should rather zero the buffer when `muted_` is set // to `true`. if (muted_) { - ClearSamples(data_); + absl::c_fill(data_, 0); muted_ = false; } return &data_[0]; @@ -170,7 +169,7 @@ InterleavedView AudioFrame::mutable_data(size_t samples_per_channel, // Consider instead if we should rather zero the whole buffer when `muted_` is // set to `true`. if (muted_) { - ClearSamples(data_, total_samples); + absl::c_fill_n(data_, total_samples, 0); muted_ = false; } samples_per_channel_ = samples_per_channel; @@ -212,9 +211,9 @@ void AudioFrame::SetSampleRateAndChannelSize(int sample_rate) { } // static -ArrayView AudioFrame::zeroed_data() { +std::span AudioFrame::zeroed_data() { static int16_t* null_data = new int16_t[kMaxDataSizeSamples](); - return ArrayView(null_data, kMaxDataSizeSamples); + return std::span(null_data, kMaxDataSizeSamples); } } // namespace webrtc diff --git a/api/audio/audio_frame.h b/api/audio/audio_frame.h index c4c93031e6e..095db192a4e 100644 --- a/api/audio/audio_frame.h +++ b/api/audio/audio_frame.h @@ -16,8 +16,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "api/audio/channel_layout.h" #include "api/rtp_packet_infos.h" @@ -199,7 +199,7 @@ class AudioFrame { // A permanently zeroed out buffer to represent muted frames. This is a // header-only class, so the only way to avoid creating a separate zeroed // buffer per translation unit is to wrap a static in an inline function. - static ArrayView zeroed_data(); + static std::span zeroed_data(); std::array data_; bool muted_ = true; diff --git a/api/audio/audio_processing.cc b/api/audio/audio_processing.cc index 76f0d71ddc0..bf0af030fef 100644 --- a/api/audio/audio_processing.cc +++ b/api/audio/audio_processing.cc @@ -145,7 +145,6 @@ std::string AudioProcessing::Config::ToString() const { << capture_level_adjustment.analog_mic_gain_emulation.initial_level << " }}, high_pass_filter: { enabled: " << high_pass_filter.enabled << " }, echo_canceller: { enabled: " << echo_canceller.enabled - << ", mobile_mode: " << echo_canceller.mobile_mode << ", enforce_high_pass_filtering: " << echo_canceller.enforce_high_pass_filtering << " }, noise_suppression: { enabled: " << noise_suppression.enabled diff --git a/api/audio/audio_processing.h b/api/audio/audio_processing.h index 0954c9a6aed..5517414204d 100644 --- a/api/audio/audio_processing.h +++ b/api/audio/audio_processing.h @@ -18,11 +18,11 @@ #include #include #include +#include #include #include "absl/base/nullability.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_processing_statistics.h" #include "api/audio/echo_control.h" #include "api/environment/environment.h" @@ -80,7 +80,6 @@ class EchoDetector; // // AudioProcessing::Config config; // config.echo_canceller.enabled = true; -// config.echo_canceller.mobile_mode = false; // // config.gain_controller1.enabled = true; // config.gain_controller1.mode = @@ -140,20 +139,23 @@ class RTC_EXPORT AudioProcessing : public RefCountInterface { // Ways to downmix a multi-channel track to mono. enum class DownmixMethod { kAverageChannels, // Average across channels. - kUseFirstChannel // Use the first channel. + kUseFirstChannel, // Use the first channel. + kAdaptive // Adaptively choose how to downmix. }; // Maximum allowed processing rate used internally. May only be set to // 32000 or 48000 and any differing values will be treated as 48000. int maximum_internal_processing_rate = 32000; // Allow multi-channel processing of render audio. - bool multi_channel_render = false; + bool multi_channel_render = true; // Allow multi-channel processing of capture audio when AEC3 is active - // or a custom AEC is injected.. - bool multi_channel_capture = false; + // or a custom AEC is injected. + bool multi_channel_capture = true; // Indicates how to downmix multi-channel capture audio to mono (when // needed). DownmixMethod capture_downmix_method = DownmixMethod::kAverageChannels; + DownmixMethod capture_downmix_method_stereo_aec = + DownmixMethod::kAverageChannels; } pipeline; // Enabled the pre-amplifier. It amplifies the capture signal @@ -196,10 +198,8 @@ class RTC_EXPORT AudioProcessing : public RefCountInterface { struct EchoCanceller { bool enabled = false; - bool mobile_mode = false; bool export_linear_aec_output = false; - // Enforce the highpass filter to be on (has no effect for the mobile - // mode). + // Enforce the highpass filter to be on. bool enforce_high_pass_filtering = true; } echo_canceller; @@ -393,8 +393,8 @@ class RTC_EXPORT AudioProcessing : public RefCountInterface { // Play-out audio device properties. struct PlayoutAudioDeviceInfo { - int id; // Identifies the audio device. - int max_volume; // Maximum play-out volume. + int id = 0; // Identifies the audio device. + int max_volume = 0; // Maximum play-out volume. }; RuntimeSetting() : type_(Type::kNotSpecified), value_(0.0f) {} @@ -583,7 +583,7 @@ class RTC_EXPORT AudioProcessing : public RefCountInterface { // representation of the input is returned. Returns true/false to indicate // whether an output returned. virtual bool GetLinearAecOutput( - ArrayView> linear_output) const = 0; + std::span> linear_output) const = 0; // This must be called prior to ProcessStream() if and only if adaptive analog // gain control is enabled, to pass the current analog level from the audio @@ -865,10 +865,10 @@ class EchoDetector : public RefCountInterface { int num_render_channels) = 0; // Analysis (not changing) of the first channel of the render signal. - virtual void AnalyzeRenderAudio(ArrayView render_audio) = 0; + virtual void AnalyzeRenderAudio(std::span render_audio) = 0; // Analysis (not changing) of the capture signal. - virtual void AnalyzeCaptureAudio(ArrayView capture_audio) = 0; + virtual void AnalyzeCaptureAudio(std::span capture_audio) = 0; struct Metrics { std::optional echo_likelihood; diff --git a/api/audio/audio_view.h b/api/audio/audio_view.h index 7b7e1bd7af5..9f7291b9d83 100644 --- a/api/audio/audio_view.h +++ b/api/audio/audio_view.h @@ -13,10 +13,11 @@ #include #include +#include #include #include -#include "api/array_view.h" +#include "absl/algorithm/container.h" #include "rtc_base/checks.h" namespace webrtc { @@ -33,7 +34,7 @@ namespace webrtc { // buffer. Channels can be enumerated and accessing the individual channel // data is done via MonoView<>. // -// The views are comparable to and built on ArrayView<> but add +// The views are comparable to and built on std::span<> but add // audio specific properties for the dimensions of the buffer and the above // specialized [de]interleaved support. // @@ -44,14 +45,13 @@ namespace webrtc { // can be either an single channel (mono) interleaved buffer (e.g. AudioFrame), // or a de-interleaved channel (e.g. from AudioBuffer). template -using MonoView = ArrayView; +using MonoView = std::span; // The maximum number of audio channels supported by WebRTC encoders, decoders // and the AudioFrame class. -// TODO(peah, tommi): Should kMaxNumberOfAudioChannels be 16 rather than 24? -// The reason is that AudioFrame's max number of samples is 7680, which can -// hold 16 10ms 16bit channels at 48 kHz (and not 24 channels). -static constexpr size_t kMaxNumberOfAudioChannels = 24; +// AudioFrame's max number of samples is 7680, which can hold 16 10ms 16bit +// channels at 48 kHz. +static constexpr size_t kMaxNumberOfAudioChannels = 16; // InterleavedView<> is a view over an interleaved audio buffer (e.g. from // AudioFrame). @@ -59,6 +59,8 @@ template class InterleavedView { public: using value_type = T; + using iterator = typename std::span::iterator; + using const_iterator = typename std::span::iterator; InterleavedView() = default; @@ -88,7 +90,7 @@ class InterleavedView { size_t num_channels() const { return num_channels_; } size_t samples_per_channel() const { return samples_per_channel_; } - ArrayView data() const { return data_; } + std::span data() const { return data_; } bool empty() const { return data_.empty(); } size_t size() const { return data_.size(); } @@ -112,14 +114,16 @@ class InterleavedView { } T& operator[](size_t idx) const { return data_[idx]; } - T* begin() const { return data_.begin(); } - T* end() const { return data_.end(); } - const T* cbegin() const { return data_.cbegin(); } - const T* cend() const { return data_.cend(); } - std::reverse_iterator rbegin() const { return data_.rbegin(); } - std::reverse_iterator rend() const { return data_.rend(); } - std::reverse_iterator crbegin() const { return data_.crbegin(); } - std::reverse_iterator crend() const { return data_.crend(); } + iterator begin() const { return data_.begin(); } + iterator end() const { return data_.end(); } + const_iterator cbegin() const { return data_.begin(); } + const_iterator cend() const { return data_.end(); } + std::reverse_iterator rbegin() const { return data_.rbegin(); } + std::reverse_iterator rend() const { return data_.rend(); } + std::reverse_iterator crbegin() const { + return data_.rbegin(); + } + std::reverse_iterator crend() const { return data_.rend(); } private: // TODO(tommi): Consider having these both be stored as uint16_t to @@ -127,7 +131,7 @@ class InterleavedView { // construction. size_t num_channels_ = 0u; size_t samples_per_channel_ = 0u; - ArrayView data_; + std::span data_; }; template @@ -207,7 +211,7 @@ class DeinterleavedView { void Clear() { for (size_t i = 0u; i < num_channels_; ++i) { MonoView view = (*this)[i]; - ClearSamples(view); + absl::c_fill(view, 0); } } @@ -304,22 +308,6 @@ void CopySamples(D& destination, const S& source) { source.size() * sizeof(typename S::value_type)); } -// Sets all the samples in a view to 0. This template function is a simple -// wrapper around `memset()` but adds the benefit of automatically calculating -// the byte size from the number of samples and sample type. -template -void ClearSamples(T& view) { - memset(&view[0], 0, view.size() * sizeof(typename T::value_type)); -} - -// Same as `ClearSamples()` above but allows for clearing only the first -// `sample_count` number of samples. -template -void ClearSamples(T& view, size_t sample_count) { - RTC_DCHECK_LE(sample_count, view.size()); - memset(&view[0], 0, sample_count * sizeof(typename T::value_type)); -} - } // namespace webrtc #endif // API_AUDIO_AUDIO_VIEW_H_ diff --git a/api/audio/neural_residual_echo_estimator.h b/api/audio/neural_residual_echo_estimator.h index b4936adc607..974bc672631 100644 --- a/api/audio/neural_residual_echo_estimator.h +++ b/api/audio/neural_residual_echo_estimator.h @@ -12,8 +12,8 @@ #define API_AUDIO_NEURAL_RESIDUAL_ECHO_ESTIMATOR_H_ #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" namespace webrtc { @@ -45,17 +45,25 @@ class NeuralResidualEchoEstimator { // Other inputs: // * dominant_nearend: True if dominant nearend is active virtual void Estimate(const Block& render, - ArrayView> y, - ArrayView> e, - ArrayView> S2, - ArrayView> Y2, - ArrayView> E2, + std::span> y, + std::span> e, + std::span> S2, + std::span> Y2, + std::span> E2, bool dominant_nearend, - ArrayView> R2, - ArrayView> R2_unbounded) = 0; + std::span> R2, + std::span> R2_unbounded) = 0; // Returns a recommended AEC3 configuration for this estimator. virtual EchoCanceller3Config GetConfiguration(bool multi_channel) const = 0; + + // Adjusts the provided AEC3 suppressor configuration based on the estimator's + // requirements. + virtual EchoCanceller3Config::Suppressor AdjustConfig( + const EchoCanceller3Config::Suppressor& config) const = 0; + + // Resets the internal state of the estimator. + virtual void Reset() = 0; }; } // namespace webrtc diff --git a/api/audio/test/BUILD.gn b/api/audio/test/BUILD.gn index b1060faae33..edd3dcd701a 100644 --- a/api/audio/test/BUILD.gn +++ b/api/audio/test/BUILD.gn @@ -23,7 +23,6 @@ if (rtc_include_tests) { deps = [ "..:aec3_config", "..:audio_frame_api", - "../..:array_view", "../../../modules/audio_processing:aec3_config_json", "../../../rtc_base:checks", "../../../test:test_support", diff --git a/api/audio/test/audio_frame_unittest.cc b/api/audio/test/audio_frame_unittest.cc index 9ee422eea14..e9a1e6fd814 100644 --- a/api/audio/test/audio_frame_unittest.cc +++ b/api/audio/test/audio_frame_unittest.cc @@ -66,7 +66,7 @@ TEST(AudioFrameTest, FrameStartsZeroedAndMuted) { EXPECT_TRUE(AllSamplesAre(0, frame)); } -// TODO: b/335805780 - Delete test when `mutable_data()` returns ArrayView. +// TODO: b/335805780 - Delete test when `mutable_data()` returns std::span. TEST(AudioFrameTest, UnmutedFrameIsInitiallyZeroedLegacy) { AudioFrame frame(kSampleRateHz, kNumChannelsMono, CHANNEL_LAYOUT_NONE); frame.mutable_data(); @@ -88,7 +88,7 @@ TEST(AudioFrameTest, UnmutedFrameIsInitiallyZeroed) { TEST(AudioFrameTest, MutedFrameBufferIsZeroed) { AudioFrame frame; int16_t* frame_data = - frame.mutable_data(kSamplesPerChannel, kNumChannelsMono).begin(); + frame.mutable_data(kSamplesPerChannel, kNumChannelsMono).data().data(); EXPECT_FALSE(frame.muted()); // Fill the reserved buffer with non-zero data. for (size_t i = 0; i < frame.max_16bit_samples(); i++) { diff --git a/api/audio/test/audio_view_unittest.cc b/api/audio/test/audio_view_unittest.cc index e28c73ca11e..833f936fe7a 100644 --- a/api/audio/test/audio_view_unittest.cc +++ b/api/audio/test/audio_view_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -37,7 +37,7 @@ void Increment(int16_t& t) { // Fills a given buffer with monotonically increasing values. template -void FillBuffer(ArrayView buffer) { +void FillBuffer(std::span buffer) { T value = {}; for (T& t : buffer) { Increment(value); @@ -50,7 +50,7 @@ void FillBuffer(ArrayView buffer) { TEST(AudioViewTest, MonoView) { const size_t kArraySize = 100u; int16_t arr[kArraySize]; - FillBuffer(ArrayView(arr)); + FillBuffer(std::span(arr)); MonoView mono(arr); MonoView const_mono(arr); @@ -69,15 +69,16 @@ TEST(AudioViewTest, MonoView) { TEST(AudioViewTest, InterleavedView) { const size_t kArraySize = 100u; int16_t arr[kArraySize]; - FillBuffer(ArrayView(arr)); + FillBuffer(std::span(arr)); InterleavedView interleaved(arr, kArraySize, 1); EXPECT_EQ(NumChannels(interleaved), 1u); EXPECT_TRUE(IsMono(interleaved)); EXPECT_EQ(SamplesPerChannel(interleaved), kArraySize); - EXPECT_EQ(interleaved.AsMono().size(), kArraySize); - EXPECT_EQ(&interleaved.AsMono()[0], &arr[0]); - EXPECT_EQ(interleaved.AsMono(), interleaved.data()); + EXPECT_EQ(interleaved.AsMono().size(), std::size(arr)); + EXPECT_EQ(interleaved.AsMono().data(), std::data(arr)); + EXPECT_EQ(interleaved.AsMono().size(), interleaved.data().size()); + EXPECT_EQ(interleaved.AsMono().data(), interleaved.data().data()); // Basic iterator test. int i = 0; @@ -121,7 +122,9 @@ TEST(AudioViewTest, DeinterleavedView) { auto mono_ch = di.AsMono(); EXPECT_EQ(NumChannels(mono_ch), 1u); EXPECT_EQ(SamplesPerChannel(mono_ch), 10u); - EXPECT_EQ(di[0], mono_ch); // first channel should be same as mono. + // first channel should be same as mono. + EXPECT_EQ(di[0].data(), mono_ch.data()); + EXPECT_EQ(di[0].size(), mono_ch.size()); di = DeinterleavedView(arr, 50, 2); // Test assignment. @@ -141,7 +144,7 @@ TEST(AudioViewTest, CopySamples) { const size_t kArraySize = 100u; int16_t source_arr[kArraySize] = {}; int16_t dest_arr[kArraySize] = {}; - FillBuffer(ArrayView(source_arr)); + FillBuffer(std::span(source_arr)); InterleavedView source(source_arr, 2); InterleavedView destination(dest_arr, 2); @@ -162,36 +165,6 @@ TEST(AudioViewTest, CopySamples) { } } -TEST(AudioViewTest, ClearSamples) { - std::array samples = {}; - FillBuffer(ArrayView(samples)); - ASSERT_NE(samples[0], 0); - ClearSamples(samples); - for (const auto s : samples) { - ASSERT_EQ(s, 0); - } - - std::array samples_f = {}; - FillBuffer(ArrayView(samples_f)); - ASSERT_NE(samples_f[0], 0.0); - ClearSamples(samples_f); - for (const auto s : samples_f) { - ASSERT_EQ(s, 0.0); - } - - // Clear only half of the buffer - FillBuffer(ArrayView(samples)); - const auto half_way = samples.size() / 2; - ClearSamples(samples, half_way); - for (size_t i = 0u; i < samples.size(); ++i) { - if (i < half_way) { - ASSERT_EQ(samples[i], 0); - } else { - ASSERT_NE(samples[i], 0); - } - } -} - TEST(AudioViewTest, DeinterleavedViewPointerArray) { // Create vectors of varying sizes to guarantee that they don't end up // aligned in memory. diff --git a/api/audio_codecs/BUILD.gn b/api/audio_codecs/BUILD.gn index 25ea12557ba..0d5b919e8a4 100644 --- a/api/audio_codecs/BUILD.gn +++ b/api/audio_codecs/BUILD.gn @@ -29,7 +29,6 @@ rtc_library("audio_codecs_api") { "audio_format.h", ] deps = [ - "..:array_view", "..:bitrate_allocation", "..:make_ref_counted", "..:ref_count", diff --git a/api/audio_codecs/OWNERS b/api/audio_codecs/OWNERS index 77b414abc30..f18110a4b6a 100644 --- a/api/audio_codecs/OWNERS +++ b/api/audio_codecs/OWNERS @@ -1,3 +1,2 @@ -alessiob@webrtc.org henrik.lundin@webrtc.org jakobi@webrtc.org diff --git a/api/audio_codecs/audio_decoder.cc b/api/audio_codecs/audio_decoder.cc index ecc1decfa50..dd2920e3fb3 100644 --- a/api/audio_codecs/audio_decoder.cc +++ b/api/audio_codecs/audio_decoder.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" #include "rtc_base/sanitizer.h" @@ -41,7 +41,7 @@ class OldStyleEncodedFrame final : public AudioDecoder::EncodedAudioFrame { } std::optional Decode( - ArrayView decoded) const override { + std::span decoded) const override { auto speech_type = AudioDecoder::kSpeech; const int ret = decoder_->Decode( payload_.data(), payload_.size(), decoder_->SampleRateHz(), @@ -94,7 +94,7 @@ int AudioDecoder::Decode(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { TRACE_EVENT0("webrtc", "AudioDecoder::Decode"); - MsanCheckInitialized(MakeArrayView(encoded, encoded_len)); + MsanCheckInitialized(std::span(encoded, encoded_len)); int duration = PacketDuration(encoded, encoded_len); if (duration >= 0 && duration * Channels() * sizeof(int16_t) > max_decoded_bytes) { @@ -111,7 +111,7 @@ int AudioDecoder::DecodeRedundant(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { TRACE_EVENT0("webrtc", "AudioDecoder::DecodeRedundant"); - MsanCheckInitialized(MakeArrayView(encoded, encoded_len)); + MsanCheckInitialized(std::span(encoded, encoded_len)); int duration = PacketDurationRedundant(encoded, encoded_len); if (duration >= 0 && duration * Channels() * sizeof(int16_t) > max_decoded_bytes) { diff --git a/api/audio_codecs/audio_decoder.h b/api/audio_codecs/audio_decoder.h index a085d3b0592..e15ffc90c3e 100644 --- a/api/audio_codecs/audio_decoder.h +++ b/api/audio_codecs/audio_decoder.h @@ -16,9 +16,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "rtc_base/buffer.h" @@ -63,7 +63,7 @@ class AudioDecoder { // decoder produced comfort noise or speech. On failure, returns an empty // std::optional. Decode may be called at most once per frame object. virtual std::optional Decode( - ArrayView decoded) const = 0; + std::span decoded) const = 0; }; struct ParseResult { diff --git a/api/audio_codecs/audio_encoder.cc b/api/audio_codecs/audio_encoder.cc index ece18fe046d..3fef371f2bd 100644 --- a/api/audio_codecs/audio_encoder.cc +++ b/api/audio_codecs/audio_encoder.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/call/bitrate_allocation.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -45,7 +45,7 @@ int AudioEncoder::RtpTimestampRateHz() const { } AudioEncoder::EncodedInfo AudioEncoder::Encode(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) { TRACE_EVENT0("webrtc", "AudioEncoder::Encode"); RTC_CHECK_EQ(audio.size(), @@ -77,9 +77,9 @@ void AudioEncoder::SetMaxPlaybackRate(int /* frequency_hz */) {} void AudioEncoder::SetTargetBitrate(int /* target_bps */) {} -ArrayView> +std::span> AudioEncoder::ReclaimContainedEncoders() { - return nullptr; + return {}; } bool AudioEncoder::EnableAudioNetworkAdaptor(absl::string_view /*config*/) { diff --git a/api/audio_codecs/audio_encoder.h b/api/audio_codecs/audio_encoder.h index 8c4fe33b114..d82a074c293 100644 --- a/api/audio_codecs/audio_encoder.h +++ b/api/audio_codecs/audio_encoder.h @@ -16,12 +16,12 @@ #include #include +#include #include #include #include "absl/base/attributes.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "api/call/bitrate_allocation.h" #include "api/units/data_rate.h" @@ -150,7 +150,7 @@ class AudioEncoder { // EncodeImpl() which does the actual work, and then checks some // postconditions. EncodedInfo Encode(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded); // Resets the encoder to its starting state, discarding any input that has @@ -196,7 +196,7 @@ class AudioEncoder { // not call any methods on this encoder afterwards, except for the // destructor. The default implementation just returns an empty array. // NOTE: This method is subject to change. Do not call or override it. - virtual ArrayView> ReclaimContainedEncoders(); + virtual std::span> ReclaimContainedEncoders(); // Enables audio network adaptor. Returns true if successful. virtual bool EnableAudioNetworkAdaptor(absl::string_view config); @@ -260,7 +260,7 @@ class AudioEncoder { // Subclasses implement this to perform the actual encoding. Called by // Encode(). virtual EncodedInfo EncodeImpl(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) = 0; }; } // namespace webrtc diff --git a/api/audio_options.cc b/api/audio_options.cc index 46913b8a4c5..83429206d8c 100644 --- a/api/audio_options.cc +++ b/api/audio_options.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { diff --git a/api/call/transport.h b/api/call/transport.h index 74d7addf6ca..aade6a5db5f 100644 --- a/api/call/transport.h +++ b/api/call/transport.h @@ -13,7 +13,7 @@ #include -#include "api/array_view.h" +#include namespace webrtc { @@ -41,9 +41,9 @@ struct PacketOptions { class Transport { public: - virtual bool SendRtp(ArrayView packet, + virtual bool SendRtp(std::span packet, const PacketOptions& options) = 0; - virtual bool SendRtcp(ArrayView packet, + virtual bool SendRtcp(std::span packet, const PacketOptions& options) = 0; protected: diff --git a/api/crypto/BUILD.gn b/api/crypto/BUILD.gn index 9d51744fd79..aac50425540 100644 --- a/api/crypto/BUILD.gn +++ b/api/crypto/BUILD.gn @@ -73,7 +73,6 @@ rtc_source_set("frame_decryptor_interface") { visibility = [ "*" ] sources = [ "frame_decryptor_interface.h" ] deps = [ - "..:array_view", "..:ref_count", "..:rtp_parameters", "../../rtc_base:refcount", @@ -84,7 +83,6 @@ rtc_source_set("frame_encryptor_interface") { visibility = [ "*" ] sources = [ "frame_encryptor_interface.h" ] deps = [ - "..:array_view", "..:ref_count", "..:rtp_parameters", "../../rtc_base:refcount", diff --git a/api/crypto/frame_crypto_transformer.cc b/api/crypto/frame_crypto_transformer.cc index a9a2b2d55ce..01625f4c08e 100644 --- a/api/crypto/frame_crypto_transformer.cc +++ b/api/crypto/frame_crypto_transformer.cc @@ -542,7 +542,7 @@ void FrameCryptorTransformer::decryptFrame( auto uncrypted_magic_bytes = key_provider_->options().uncrypted_magic_bytes; if (uncrypted_magic_bytes.size() > 0 && data_in.size() >= uncrypted_magic_bytes.size()) { - auto tmp = data_in.subview(data_in.size() - (uncrypted_magic_bytes.size()), + auto tmp = data_in.subspan(data_in.size() - (uncrypted_magic_bytes.size()), uncrypted_magic_bytes.size()); auto data = std::vector(tmp.begin(), tmp.end()); if (uncrypted_magic_bytes == data) { @@ -557,7 +557,7 @@ void FrameCryptorTransformer::decryptFrame( // decryption. Buffer data_out; data_out.AppendData( - data_in.subview(0, data_in.size() - uncrypted_magic_bytes.size())); + data_in.subspan(0, data_in.size() - uncrypted_magic_bytes.size())); frame->SetData(data_out); sink_callback->OnTransformedFrame(std::move(frame)); return; diff --git a/api/crypto/frame_decryptor_interface.h b/api/crypto/frame_decryptor_interface.h index 659f16654c6..c0b8801473d 100644 --- a/api/crypto/frame_decryptor_interface.h +++ b/api/crypto/frame_decryptor_interface.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/media_types.h" #include "api/ref_count.h" @@ -62,9 +62,9 @@ class FrameDecryptorInterface : public RefCountInterface { // cases. virtual Result Decrypt(MediaType media_type, const std::vector& csrcs, - ArrayView additional_data, - ArrayView encrypted_frame, - ArrayView frame) = 0; + std::span additional_data, + std::span encrypted_frame, + std::span frame) = 0; // Returns the total required length in bytes for the output of the // decryption. This can be larger than the actual number of bytes you need but diff --git a/api/crypto/frame_encryptor_interface.h b/api/crypto/frame_encryptor_interface.h index 6a1797249d4..4360cd5bbc1 100644 --- a/api/crypto/frame_encryptor_interface.h +++ b/api/crypto/frame_encryptor_interface.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/media_types.h" #include "api/ref_count.h" @@ -40,9 +40,9 @@ class FrameEncryptorInterface : public RefCountInterface { // selected by the implementer to represent error codes. virtual int Encrypt(MediaType media_type, uint32_t ssrc, - ArrayView additional_data, - ArrayView frame, - ArrayView encrypted_frame, + std::span additional_data, + std::span frame, + std::span encrypted_frame, size_t* bytes_written) = 0; // Returns the total required length in bytes for the output of the diff --git a/api/data_channel_event_observer_interface.h b/api/data_channel_event_observer_interface.h index f9b59020f4a..135db62ea68 100644 --- a/api/data_channel_event_observer_interface.h +++ b/api/data_channel_event_observer_interface.h @@ -12,11 +12,11 @@ #define API_DATA_CHANNEL_EVENT_OBSERVER_INTERFACE_H_ #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" namespace webrtc { @@ -55,7 +55,7 @@ class DataChannelEventObserverInterface { void set_data_type(DataType type) { data_type_ = type; } const std::vector& data() const { return data_; } - void set_data(ArrayView d) { + void set_data(std::span d) { data_.assign(d.begin(), d.end()); } diff --git a/api/datagram_connection.h b/api/datagram_connection.h index 9b52e97ec70..e18588b52c3 100644 --- a/api/datagram_connection.h +++ b/api/datagram_connection.h @@ -12,11 +12,11 @@ #include #include +#include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/ref_count.h" #include "api/units/timestamp.h" @@ -48,7 +48,7 @@ class RTC_EXPORT DatagramConnection : public RefCountInterface { struct PacketMetadata { Timestamp receive_time; }; - virtual void OnPacketReceived(ArrayView data, + virtual void OnPacketReceived(std::span data, PacketMetadata metadata) = 0; // Notification of outcome of an earlier call to SendPacket. @@ -96,13 +96,13 @@ class RTC_EXPORT DatagramConnection : public RefCountInterface { // performed, the caller is responsible for ensuring uniqueness and handing // rollovers. PacketId id = 0; - ArrayView payload; + std::span payload; }; // Send a batch of packets on this connection. Listen to // Observer::OnSendOutcome for notification of whether each was sent // successfully. - virtual void SendPackets(ArrayView packets) = 0; + virtual void SendPackets(std::span packets) = 0; // Initiate closing connection and releasing resources. Must be called before // destruction. diff --git a/api/environment/environment_unittest.cc b/api/environment/environment_unittest.cc index c00c10491a2..048f2501acd 100644 --- a/api/environment/environment_unittest.cc +++ b/api/environment/environment_unittest.cc @@ -93,7 +93,7 @@ TEST(EnvironmentTest, DefaultEnvironmentHasAllUtilities) { // Try to use each utility, expect no crashes. env.clock().CurrentTime(); EXPECT_THAT(env.task_queue_factory().CreateTaskQueue( - "test", TaskQueueFactory::Priority::NORMAL), + "test", TaskQueueFactory::Priority::kNormal), NotNull()); env.event_log().Log(std::make_unique()); env.field_trials().Lookup("WebRTC-Debugging-RtpDump"); diff --git a/api/frame_transformer_interface.h b/api/frame_transformer_interface.h index 42ac2c12664..9f4ed927f22 100644 --- a/api/frame_transformer_interface.h +++ b/api/frame_transformer_interface.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/ref_count.h" #include "api/scoped_refptr.h" #include "api/units/time_delta.h" @@ -46,10 +46,10 @@ class TransformableFrameInterface { // Returns the frame payload data. The data is valid until the next non-const // method call. - virtual ArrayView GetData() const = 0; + virtual std::span GetData() const = 0; // Copies `data` into the owned frame payload data. - virtual void SetData(ArrayView data) = 0; + virtual void SetData(std::span data) = 0; virtual uint8_t GetPayloadType() const = 0; virtual bool CanSetPayloadType() const { return false; } @@ -125,7 +125,7 @@ class TransformableAudioFrameInterface : public TransformableFrameInterface { RTC_EXPORT explicit TransformableAudioFrameInterface(Passkey passkey); ~TransformableAudioFrameInterface() override = default; - virtual ArrayView GetContributingSources() const = 0; + virtual std::span GetContributingSources() const = 0; virtual const std::optional SequenceNumber() const = 0; diff --git a/api/neteq/BUILD.gn b/api/neteq/BUILD.gn index 826e426e861..b68fbe8b577 100644 --- a/api/neteq/BUILD.gn +++ b/api/neteq/BUILD.gn @@ -17,7 +17,6 @@ rtc_library("neteq_api") { ] deps = [ - "..:array_view", "..:rtp_headers", "..:rtp_packet_info", "..:scoped_refptr", diff --git a/api/neteq/DEPS b/api/neteq/DEPS index 6c1c602b42a..4eb72f0567e 100644 --- a/api/neteq/DEPS +++ b/api/neteq/DEPS @@ -1,14 +1,14 @@ specific_include_rules = { - "custom_neteq_factory\.h": [ + "custom_neteq_factory\\.h": [ "+system_wrappers/include/clock.h", ], - "default_neteq_factory\.h": [ + "default_neteq_factory\\.h": [ "+system_wrappers/include/clock.h", ], - "neteq_controller\.h": [ + "neteq_controller\\.h": [ "+system_wrappers/include/clock.h", ], - "neteq_factory\.h": [ + "neteq_factory\\.h": [ "+system_wrappers/include/clock.h", ], } diff --git a/api/neteq/neteq.h b/api/neteq/neteq.h index 0afb04cc4bc..da667b59918 100644 --- a/api/neteq/neteq.h +++ b/api/neteq/neteq.h @@ -16,10 +16,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/rtp_headers.h" #include "api/rtp_packet_info.h" @@ -31,28 +31,29 @@ namespace webrtc { class AudioFrame; struct NetEqNetworkStatistics { - uint16_t current_buffer_size_ms; // Current jitter buffer size in ms. - uint16_t preferred_buffer_size_ms; // Target buffer size in ms. - uint16_t jitter_peaks_found; // 1 if adding extra delay due to peaky - // jitter; 0 otherwise. - uint16_t expand_rate; // Fraction (of original stream) of synthesized - // audio inserted through expansion (in Q14). - uint16_t speech_expand_rate; // Fraction (of original stream) of synthesized - // speech inserted through expansion (in Q14). - uint16_t preemptive_rate; // Fraction of data inserted through pre-emptive - // expansion (in Q14). - uint16_t accelerate_rate; // Fraction of data removed through acceleration - // (in Q14). - uint16_t secondary_decoded_rate; // Fraction of data coming from FEC/RED - // decoding (in Q14). - uint16_t secondary_discarded_rate; // Fraction of discarded FEC/RED data (in - // Q14). + uint16_t current_buffer_size_ms = 0; // Current jitter buffer size in ms. + uint16_t preferred_buffer_size_ms = 0; // Target buffer size in ms. + uint16_t jitter_peaks_found = + 0; // 1 if adding extra delay due to peaky jitter; 0 otherwise. + uint16_t expand_rate = 0; // Fraction (of original stream) of synthesized + // audio inserted through expansion (in Q14). + uint16_t speech_expand_rate = + 0; // Fraction (of original stream) of synthesized speech inserted + // through expansion (in Q14). + uint16_t preemptive_rate = + 0; // Fraction of data inserted through preemptive expansion (in Q14). + uint16_t accelerate_rate = + 0; // Fraction of data removed through acceleration (in Q14). + uint16_t secondary_decoded_rate = + 0; // Fraction of data coming from FEC/RED decoding (in Q14). + uint16_t secondary_discarded_rate = + 0; // Fraction of discarded FEC/RED data (in Q14). // Statistics for packet waiting times, i.e., the time between a packet // arrives until it is decoded. - int mean_waiting_time_ms; - int median_waiting_time_ms; - int min_waiting_time_ms; - int max_waiting_time_ms; + int mean_waiting_time_ms = 0; + int median_waiting_time_ms = 0; + int min_waiting_time_ms = 0; + int max_waiting_time_ms = 0; }; // NetEq statistics that persist over the lifetime of the class. @@ -185,14 +186,14 @@ class NetEq { virtual ~NetEq() {} virtual int InsertPacket(const RTPHeader& rtp_header, - ArrayView payload) { + std::span payload) { return InsertPacket(rtp_header, payload, /*receive_time=*/Timestamp::MinusInfinity()); } // TODO: webrtc:343501093 - removed unused method. virtual int InsertPacket(const RTPHeader& rtp_header, - ArrayView payload, + std::span payload, Timestamp receive_time) { return InsertPacket(rtp_header, payload, RtpPacketInfo(rtp_header, receive_time)); @@ -202,7 +203,7 @@ class NetEq { // Returns 0 on success, -1 on failure. // TODO: webrtc:343501093 - Make this method pure virtual. virtual int InsertPacket(const RTPHeader& rtp_header, - ArrayView payload, + std::span payload, const RtpPacketInfo& /* rtp_packet_info */) { return InsertPacket(rtp_header, payload); } diff --git a/api/numerics/BUILD.gn b/api/numerics/BUILD.gn index 3ec8c37391f..2d9f6a2a857 100644 --- a/api/numerics/BUILD.gn +++ b/api/numerics/BUILD.gn @@ -16,7 +16,6 @@ rtc_library("numerics") { "samples_stats_counter.h", ] deps = [ - "..:array_view", "../../rtc_base:checks", "../../rtc_base:rtc_numerics", "../units:timestamp", diff --git a/api/numerics/DEPS b/api/numerics/DEPS index 2d89d575575..02b9be2669d 100644 --- a/api/numerics/DEPS +++ b/api/numerics/DEPS @@ -1,6 +1,6 @@ specific_include_rules = { # Some internal headers are allowed even in API headers: - "samples_stats_counter\.h": [ + "samples_stats_counter\\.h": [ "+rtc_base/numerics/running_statistics.h", ] } diff --git a/api/numerics/samples_stats_counter.h b/api/numerics/samples_stats_counter.h index 38d0cbe043f..89bedb2a7cb 100644 --- a/api/numerics/samples_stats_counter.h +++ b/api/numerics/samples_stats_counter.h @@ -15,10 +15,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/units/timestamp.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/running_statistics.h" @@ -102,7 +102,7 @@ class SamplesStatsCounter { // guarantees of order, so samples can be in different order comparing to in // which they were added into counter. Also return value will be invalidate // after call to any non const method. - ArrayView GetTimedSamples() const { return samples_; } + std::span GetTimedSamples() const { return samples_; } std::vector GetSamples() const { std::vector out; out.reserve(samples_.size()); diff --git a/api/packet_socket_factory.h b/api/packet_socket_factory.h index e87e9cd9b3f..06a2d2849e3 100644 --- a/api/packet_socket_factory.h +++ b/api/packet_socket_factory.h @@ -19,6 +19,7 @@ #include "api/async_dns_resolver.h" #include "api/environment/environment.h" #include "rtc_base/async_packet_socket.h" +#include "rtc_base/checks.h" #include "rtc_base/socket_address.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/system/rtc_export.h" @@ -43,6 +44,10 @@ class RTC_EXPORT PacketSocketFactory { enum Options { OPT_STUN = 0x04, + // The DTLS options below are mutually exclusive. + OPT_DTLS = 0x20, // Real and secure DTLS. + OPT_DTLS_INSECURE = 0x10, // Insecure DTLS without certificate validation. + // The TLS options below are mutually exclusive. OPT_TLS = 0x02, // Real and secure TLS. OPT_TLS_FAKE = 0x01, // Fake TLS with a dummy SSL handshake. @@ -80,6 +85,19 @@ class RTC_EXPORT PacketSocketFactory { virtual std::unique_ptr CreateAsyncDnsResolver() = 0; + + // TODO(issues.webrtc.org/42225835): + // Make pure virtual once downstream is updated + virtual std::unique_ptr CreateClientUdpSocket( + const Environment& env, + const SocketAddress& local_address, + const SocketAddress& remote_address, + uint16_t min_port, + uint16_t max_port, + const PacketSocketTcpOptions& options) { + RTC_DCHECK_NOTREACHED(); + return nullptr; + } }; } // namespace webrtc diff --git a/api/payload_type.h b/api/payload_type.h new file mode 100644 index 00000000000..dc40b1f696f --- /dev/null +++ b/api/payload_type.h @@ -0,0 +1,77 @@ +/* + * Copyright 2024 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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_PAYLOAD_TYPE_H_ +#define API_PAYLOAD_TYPE_H_ + +#include + +#include "absl/strings/str_format.h" +#include "rtc_base/strong_alias.h" + +namespace webrtc { + +class PayloadType : public StrongAlias { + public: + // The default constructor makes a NotSet. + PayloadType() : StrongAlias(-1) {} + // Non-explicit conversions from and to ints are to be deprecated and + // removed once calling code is upgraded. + constexpr PayloadType(int pt) : StrongAlias(pt) { // NOLINT: explicit + // The number of tests that use invalid values is high enough that + // this DCHECK can't be deployed yet. + // Also, allow -1 as argument as a temporary measure. Those calls should + // eventually be replaced with PayloadType::NotSet() values. + // Intended check: + // RTC_DCHECK(pt >= -1 && pt <= 127) << "Payload type " << pt << " is + // invalid"; + } + + constexpr operator int() const& { return value(); } // NOLINT: explicit + + // Factory function to create a value if you need to check for + // values in the valid range. + static std::optional Create(int pt) { + if (pt < 0 || pt > 127) { + return std::nullopt; + } + return PayloadType(pt); + } + // Factory function for the NotSet value. This should be the only way + // to create a value outside the valid range. + static constexpr PayloadType NotSet() { return PayloadType(Internal{}, -1); } + bool Valid(bool rtcp_mux = false) { + // A payload type is a 7-bit value in the RTP header, so max = 127. + // If RTCP multiplexing is used, the numbers from 64 to 95 are reserved + // for RTCP packets. + if (rtcp_mux && (value() > 63 && value() < 96)) { + return false; + } + return value() >= 0 && value() <= 127; + } + // Older interface to validity check. + static bool IsValid(PayloadType id, bool rtcp_mux) { + return id.Valid(rtcp_mux); + } + bool IsSet() { return value() >= 0; } + + private: + class Internal {}; + // Allow -1 for "NotSet" + explicit constexpr PayloadType(Internal tag, int pt) : StrongAlias(pt) {} + template + friend void AbslStringify(Sink& sink, const PayloadType pt) { + absl::Format(&sink, "%d", pt.value()); + } +}; + +} // namespace webrtc + +#endif // API_PAYLOAD_TYPE_H_ diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index 9ff57fa4513..038001ac774 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h @@ -144,6 +144,7 @@ #include "rtc_base/socket_factory.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/thread.h" @@ -766,12 +767,14 @@ class RTC_EXPORT PeerConnectionInterface : public RefCountInterface { // Accessor methods to active local streams. // This method is not supported with kUnifiedPlan semantics. Please use // GetSenders() instead. - virtual scoped_refptr local_streams() = 0; + PLAN_B_ONLY virtual scoped_refptr + local_streams() = 0; // Accessor methods to remote streams. // This method is not supported with kUnifiedPlan semantics. Please use // GetReceivers() instead. - virtual scoped_refptr remote_streams() = 0; + PLAN_B_ONLY virtual scoped_refptr + remote_streams() = 0; // Add a new MediaStream to be sent on this PeerConnection. // Note that a SessionDescription negotiation is needed before the @@ -785,7 +788,7 @@ class RTC_EXPORT PeerConnectionInterface : public RefCountInterface { // // This method is not supported with kUnifiedPlan semantics. Please use // AddTrack instead. - virtual bool AddStream(MediaStreamInterface* stream) = 0; + PLAN_B_ONLY virtual bool AddStream(MediaStreamInterface* stream) = 0; // Remove a MediaStream from this PeerConnection. // Note that a SessionDescription negotiation is needed before the @@ -793,7 +796,7 @@ class RTC_EXPORT PeerConnectionInterface : public RefCountInterface { // // This method is not supported with kUnifiedPlan semantics. Please use // RemoveTrack instead. - virtual void RemoveStream(MediaStreamInterface* stream) = 0; + PLAN_B_ONLY virtual void RemoveStream(MediaStreamInterface* stream) = 0; // Add a new MediaStreamTrack to be sent on this PeerConnection, and return // the newly created RtpSender. The RtpSender will be associated with the @@ -891,7 +894,7 @@ class RTC_EXPORT PeerConnectionInterface : public RefCountInterface { // // This method is not supported with kUnifiedPlan semantics. Please use // AddTransceiver instead. - virtual scoped_refptr CreateSender( + PLAN_B_ONLY virtual scoped_refptr CreateSender( const std::string& kind, const std::string& stream_id) = 0; @@ -938,9 +941,10 @@ class RTC_EXPORT PeerConnectionInterface : public RefCountInterface { // // TODO(hbos): Deprecate and remove this when third parties have migrated to // the spec-compliant GetStats() API. https://crbug.com/822696 - virtual bool GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track, // Optional - StatsOutputLevel level) = 0; + [[deprecated]] virtual bool GetStats( + StatsObserver* observer, + MediaStreamTrackInterface* track, // Optional + StatsOutputLevel level) = 0; // The spec-compliant GetStats() API. This correspond to the promise-based // version of getStats() in JavaScript. Implementation status is described in // api/stats/rtcstats_objects.h. For more details on stats, see spec: diff --git a/api/rtc_error.cc b/api/rtc_error.cc index 65f220bc942..cda1fe15f35 100644 --- a/api/rtc_error.cc +++ b/api/rtc_error.cc @@ -16,6 +16,8 @@ #include "absl/strings/string_view.h" #include "rtc_base/strings/string_builder.h" +namespace webrtc { + namespace { absl::string_view kRTCErrorTypeNames[] = { @@ -32,11 +34,10 @@ absl::string_view kRTCErrorTypeNames[] = { "INTERNAL_ERROR", "OPERATION_ERROR_WITH_DATA", }; -static_assert( - static_cast(webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA) == - (std::size(kRTCErrorTypeNames) - 1), - "kRTCErrorTypeNames must have as many strings as RTCErrorType " - "has values."); +static_assert(static_cast(RTCErrorType::OPERATION_ERROR_WITH_DATA) == + (std::size(kRTCErrorTypeNames) - 1), + "kRTCErrorTypeNames must have as many strings as RTCErrorType " + "has values."); absl::string_view kRTCErrorDetailTypeNames[] = { "NONE", @@ -48,16 +49,13 @@ absl::string_view kRTCErrorDetailTypeNames[] = { "HARDWARE_ENCODER_NOT_AVAILABLE", "HARDWARE_ENCODER_ERROR", }; -static_assert( - static_cast(webrtc::RTCErrorDetailType::HARDWARE_ENCODER_ERROR) == - (std::size(kRTCErrorDetailTypeNames) - 1), - "kRTCErrorDetailTypeNames must have as many strings as " - "RTCErrorDetailType has values."); +static_assert(static_cast(RTCErrorDetailType::HARDWARE_ENCODER_ERROR) == + (std::size(kRTCErrorDetailTypeNames) - 1), + "kRTCErrorDetailTypeNames must have as many strings as " + "RTCErrorDetailType has values."); } // namespace -namespace webrtc { - // static RTCError RTCError::OK() { return RTCError(); diff --git a/api/rtc_error.h b/api/rtc_error.h index 19ea5d98248..a0a19ae5ba6 100644 --- a/api/rtc_error.h +++ b/api/rtc_error.h @@ -147,6 +147,18 @@ class RTC_EXPORT RTCError { static RTCError InvalidParameter() { return RTCError(RTCErrorType::INVALID_PARAMETER); } + static RTCError InvalidState() { + return RTCError(RTCErrorType::INVALID_STATE); + } + static RTCError InvalidModification() { + return RTCError(RTCErrorType::INVALID_MODIFICATION); + } + static RTCError UnsupportedOperation() { + return RTCError(RTCErrorType::UNSUPPORTED_OPERATION); + } + static RTCError UnsupportedParameter() { + return RTCError(RTCErrorType::UNSUPPORTED_PARAMETER); + } // Error type. RTCErrorType type() const { return type_; } @@ -213,20 +225,6 @@ class RTC_EXPORT RTCError { std::optional sctp_cause_code_; }; -// Helper macro that can be used by implementations to create an error with a -// message and log it. `message` should be a string literal or movable -// std::string. -#define LOG_AND_RETURN_ERROR_EX(type, message, severity) \ - { \ - RTC_DCHECK(type != RTCErrorType::NONE); \ - RTC_LOG(severity) << message << " (" << ::webrtc::ToString(type) << ")"; \ - return ::webrtc::RTCError(type, message); \ - } - -// LOG_AND_RETURN_ERROR is a s simpler variant of `LOG_AND_RETURN_ERROR_EX`. -#define LOG_AND_RETURN_ERROR(type, message) \ - LOG_AND_RETURN_ERROR_EX(type, message, LS_ERROR) - inline RTCError LogErrorImpl(RTCError error, LoggingSeverity severity, const char* file, @@ -243,7 +241,6 @@ inline RTCError LogErrorImpl(RTCError error, return error; } -// A slightly more C++ looking alternative to the LOG_AND_RETURN_ERROR() macro. // This approach does not hide the return statement and also allows for // constructing/formatting the error string inline. // diff --git a/api/rtc_error_unittest.cc b/api/rtc_error_unittest.cc index 596bc6413fc..7527ae62610 100644 --- a/api/rtc_error_unittest.cc +++ b/api/rtc_error_unittest.cc @@ -259,7 +259,7 @@ TEST(RTCErrorOrTest, BuildString) { EXPECT_STREQ(error.message(), "StringyBuilder #2"); } -// Tests a non macro based alternative to `LOG_AND_RETURN_ERROR`. +// Tests `LOG_ERROR`. TEST(RTCErrorOrTest, BuildStringLog) { class LogSinkImpl : public LogSink { public: diff --git a/api/rtp_packet_info.cc b/api/rtp_packet_info.cc index 557247b5237..6bbb135a5d2 100644 --- a/api/rtp_packet_info.cc +++ b/api/rtp_packet_info.cc @@ -18,24 +18,48 @@ #include "api/rtp_headers.h" #include "api/units/timestamp.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" namespace webrtc { RtpPacketInfo::RtpPacketInfo() - : ssrc_(0), rtp_timestamp_(0), receive_time_(Timestamp::MinusInfinity()) {} + : sequence_number_(0), + ssrc_(0), + rtp_timestamp_(0), + receive_time_(Timestamp::MinusInfinity()) {} + +RtpPacketInfo::RtpPacketInfo(const RtpPacketReceived& rtp_packet) + : sequence_number_(rtp_packet.SequenceNumber()), + ssrc_(rtp_packet.Ssrc()), + csrcs_(rtp_packet.Csrcs()), + rtp_timestamp_(rtp_packet.Timestamp()), + receive_time_(rtp_packet.arrival_time()) { + AudioLevel audio_level; + if (rtp_packet.GetExtension(&audio_level)) { + audio_level_ = audio_level.level(); + } + + AbsoluteCaptureTime capture_time; + if (rtp_packet.GetExtension(&capture_time)) { + absolute_capture_time_ = std::move(capture_time); + } +} RtpPacketInfo::RtpPacketInfo(uint32_t ssrc, std::vector csrcs, uint32_t rtp_timestamp, Timestamp receive_time) - : ssrc_(ssrc), + : sequence_number_(0), + ssrc_(ssrc), csrcs_(std::move(csrcs)), rtp_timestamp_(rtp_timestamp), receive_time_(receive_time) {} RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, Timestamp receive_time) - : ssrc_(rtp_header.ssrc), + : sequence_number_(rtp_header.sequenceNumber), + ssrc_(rtp_header.ssrc), rtp_timestamp_(rtp_header.timestamp), receive_time_(receive_time) { const auto& extension = rtp_header.extension; @@ -50,13 +74,6 @@ RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, absolute_capture_time_ = extension.absolute_capture_time; } -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.local_capture_clock_offset() == rhs.local_capture_clock_offset()); -} +bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) = default; } // namespace webrtc diff --git a/api/rtp_packet_info.h b/api/rtp_packet_info.h index 4059108c835..943975b3194 100644 --- a/api/rtp_packet_info.h +++ b/api/rtp_packet_info.h @@ -23,6 +23,8 @@ namespace webrtc { +class RtpPacketReceived; + // // Structure to hold information about a received `RtpPacket`. It is primarily // used to carry per-packet information from when a packet is received until @@ -32,11 +34,17 @@ class RTC_EXPORT RtpPacketInfo { public: RtpPacketInfo(); + explicit RtpPacketInfo(const RtpPacketReceived& rtp_packet); + + // TODO: bugs.webrtc.org/42225366 - The constructor will be deprecated. Use + // RtpPacketInfo(const RtpPacketReceived& rtp_packet). RtpPacketInfo(uint32_t ssrc, std::vector csrcs, uint32_t rtp_timestamp, Timestamp receive_time); + // TODO: bugs.webrtc.org/42225366 - The constructor will be deprecated. Use + // RtpPacketInfo(const RtpPacketReceived& rtp_packet). RtpPacketInfo(const RTPHeader& rtp_header, Timestamp receive_time); RtpPacketInfo(const RtpPacketInfo& other) = default; @@ -47,6 +55,9 @@ class RTC_EXPORT RtpPacketInfo { uint32_t ssrc() const { return ssrc_; } void set_ssrc(uint32_t value) { ssrc_ = value; } + uint16_t sequence_number() const { return sequence_number_; } + void set_sequence_number(uint16_t value) { sequence_number_ = value; } + const std::vector& csrcs() const { return csrcs_; } void set_csrcs(std::vector value) { csrcs_ = std::move(value); } @@ -80,9 +91,12 @@ class RTC_EXPORT RtpPacketInfo { return *this; } + friend bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs); + private: // Fields from the RTP header: // https://tools.ietf.org/html/rfc3550#section-5.1 + uint16_t sequence_number_; uint32_t ssrc_; std::vector csrcs_; uint32_t rtp_timestamp_; @@ -106,12 +120,6 @@ class RTC_EXPORT RtpPacketInfo { std::optional local_capture_clock_offset_; }; -bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs); - -inline bool operator!=(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) { - return !(lhs == rhs); -} - } // namespace webrtc #endif // API_RTP_PACKET_INFO_H_ diff --git a/api/rtp_packet_info_unittest.cc b/api/rtp_packet_info_unittest.cc index 9aea11d24c6..c61d1bdd0db 100644 --- a/api/rtp_packet_info_unittest.cc +++ b/api/rtp_packet_info_unittest.cc @@ -17,9 +17,29 @@ #include "api/rtp_headers.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/checks.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { +namespace { + +using ::testing::ElementsAreArray; +using ::testing::Optional; + +template +RtpPacketReceived CreateRtpPacketReceivedWithExtension(ExtensionValue value) { + RtpHeaderExtensionMap extensions; + extensions.Register(5); + RtpPacketReceived packet(&extensions); + RTC_CHECK(packet.SetExtension(value)) + << "Unable to set extension."; + return packet; +} TEST(RtpPacketInfoTest, Ssrc) { constexpr uint32_t kValue = 4038189233; @@ -44,13 +64,39 @@ TEST(RtpPacketInfoTest, Ssrc) { rhs = RtpPacketInfo(); EXPECT_NE(rhs.ssrc(), kValue); - rhs = RtpPacketInfo(/*ssrc=*/kValue, /*csrcs=*/{}, /*rtp_timestamp=*/{}, - /*receive_time=*/Timestamp::Zero()); + rhs.set_ssrc(kValue); EXPECT_EQ(rhs.ssrc(), kValue); } +TEST(RtpPacketInfoTest, SequenceNumber) { + constexpr uint16_t kValue = 34653; + + RtpPacketInfo lhs; + RtpPacketInfo rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs.set_sequence_number(kValue); + EXPECT_EQ(rhs.sequence_number(), kValue); + + EXPECT_FALSE(lhs == rhs); + EXPECT_TRUE(lhs != rhs); + + lhs = rhs; + + EXPECT_TRUE(lhs == rhs); + EXPECT_FALSE(lhs != rhs); + + rhs = RtpPacketInfo(); + EXPECT_NE(rhs.sequence_number(), kValue); + + rhs.set_sequence_number(kValue); + EXPECT_EQ(rhs.sequence_number(), kValue); +} + TEST(RtpPacketInfoTest, Csrcs) { - const std::vector value = {4038189233, 3016333617, 1207992985}; + const std::vector kValue = {4038189233, 3016333617, 1207992985}; RtpPacketInfo lhs; RtpPacketInfo rhs; @@ -58,8 +104,8 @@ TEST(RtpPacketInfoTest, Csrcs) { EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - rhs.set_csrcs(value); - EXPECT_EQ(rhs.csrcs(), value); + rhs.set_csrcs(kValue); + EXPECT_EQ(rhs.csrcs(), kValue); EXPECT_FALSE(lhs == rhs); EXPECT_TRUE(lhs != rhs); @@ -70,11 +116,10 @@ TEST(RtpPacketInfoTest, Csrcs) { EXPECT_FALSE(lhs != rhs); rhs = RtpPacketInfo(); - EXPECT_NE(rhs.csrcs(), value); + EXPECT_NE(rhs.csrcs(), kValue); - rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/value, /*rtp_timestamp=*/{}, - /*receive_time=*/Timestamp::Zero()); - EXPECT_EQ(rhs.csrcs(), value); + rhs.set_csrcs(kValue); + EXPECT_EQ(rhs.csrcs(), kValue); } TEST(RtpPacketInfoTest, RtpTimestamp) { @@ -100,8 +145,7 @@ TEST(RtpPacketInfoTest, RtpTimestamp) { rhs = RtpPacketInfo(); EXPECT_NE(rhs.rtp_timestamp(), kValue); - rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/kValue, - /*receive_time=*/Timestamp::Zero()); + rhs.set_rtp_timestamp(kValue); EXPECT_EQ(rhs.rtp_timestamp(), kValue); } @@ -128,8 +172,7 @@ TEST(RtpPacketInfoTest, ReceiveTimeMs) { rhs = RtpPacketInfo(); EXPECT_NE(rhs.receive_time(), kValue); - rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/{}, - /*receive_time=*/kValue); + rhs.set_receive_time(kValue); EXPECT_EQ(rhs.receive_time(), kValue); } @@ -156,8 +199,6 @@ TEST(RtpPacketInfoTest, AudioLevel) { rhs = RtpPacketInfo(); EXPECT_NE(rhs.audio_level(), kValue); - rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/{}, - /*receive_time=*/Timestamp::Zero()); rhs.set_audio_level(kValue); EXPECT_EQ(rhs.audio_level(), kValue); } @@ -186,8 +227,6 @@ TEST(RtpPacketInfoTest, AbsoluteCaptureTime) { rhs = RtpPacketInfo(); EXPECT_NE(rhs.absolute_capture_time(), kValue); - rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/{}, - /*receive_time=*/Timestamp::Zero()); rhs.set_absolute_capture_time(kValue); EXPECT_EQ(rhs.absolute_capture_time(), kValue); } @@ -215,10 +254,52 @@ TEST(RtpPacketInfoTest, LocalCaptureClockOffset) { rhs = RtpPacketInfo(); EXPECT_EQ(rhs.local_capture_clock_offset(), std::nullopt); - 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); } +TEST(RtpPacketInfoTest, RtpPacketReceivedNoExtensions) { + constexpr Timestamp kArrivalTime = Timestamp::Micros(8868963877546349045LL); + constexpr uint32_t kSsrc = 4038189233; + constexpr uint16_t kSequenceNumber = 1234; + constexpr uint32_t kRtpTimestamp = 5684353; + const std::vector kCsrcs = {15, 60}; + RtpPacketReceived packet; + packet.set_arrival_time(kArrivalTime); + packet.SetSsrc(kSsrc); + packet.SetCsrcs(kCsrcs); + packet.SetSequenceNumber(kSequenceNumber); + packet.SetTimestamp(kRtpTimestamp); + + RtpPacketInfo rtp_packet_info(packet); + + EXPECT_EQ(rtp_packet_info.ssrc(), kSsrc); + EXPECT_EQ(rtp_packet_info.sequence_number(), kSequenceNumber); + EXPECT_THAT(rtp_packet_info.csrcs(), ElementsAreArray(kCsrcs)); + EXPECT_EQ(rtp_packet_info.rtp_timestamp(), kRtpTimestamp); + EXPECT_EQ(rtp_packet_info.receive_time(), kArrivalTime); +} + +TEST(RtpPacketInfoTest, RtpPacketReceivedAudioLevel) { + constexpr uint8_t kValue = 14; + RtpPacketReceived packet = + CreateRtpPacketReceivedWithExtension( + AudioLevel(/*voice_activity=*/true, /*level=*/kValue)); + + EXPECT_THAT(RtpPacketInfo(packet).audio_level(), Optional(kValue)); +} + +TEST(RtpPacketInfoTest, RtpPacketReceivedAbsoluteCaptureTime) { + const AbsoluteCaptureTime kAbsoluteCaptureTime = { + .absolute_capture_timestamp = 1493489345934859334, + .estimated_capture_clock_offset = 3453534534}; + RtpPacketReceived packet = + CreateRtpPacketReceivedWithExtension( + kAbsoluteCaptureTime); + + EXPECT_THAT(RtpPacketInfo(packet).absolute_capture_time(), + Optional(kAbsoluteCaptureTime)); +} + +} // namespace } // namespace webrtc diff --git a/api/rtp_parameters.cc b/api/rtp_parameters.cc index 7ca5481bca8..b6b2f891685 100644 --- a/api/rtp_parameters.cc +++ b/api/rtp_parameters.cc @@ -18,7 +18,6 @@ #include "absl/strings/ascii.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_error.h" #include "api/rtp_transceiver_direction.h" #include "media/base/media_constants.h" @@ -206,15 +205,14 @@ RtpParameters::RtpParameters(const RtpParameters& rhs) = default; RtpParameters::~RtpParameters() = default; std::string RtpExtension::ToString() const { - char buf[256]; - SimpleStringBuilder sb(buf); + StringBuilder sb; sb << "{uri: " << uri; sb << ", id: " << id; if (encrypt) { sb << ", encrypt"; } - sb << '}'; - return sb.str(); + sb << "}"; + return sb.Release(); } bool RtpExtension::IsSupportedForAudio(absl::string_view uri) { @@ -248,14 +246,6 @@ bool RtpExtension::IsSupportedForVideo(absl::string_view uri) { bool RtpExtension::IsEncryptionSupported(absl::string_view uri) { return -#if defined(ENABLE_EXTERNAL_AUTH) - // TODO(jbauch): Figure out a way to always allow "kAbsSendTimeUri" - // here and filter out later if external auth is really used in - // srtpfilter. External auth is used by Chromium and replaces the - // extension header value of "kAbsSendTimeUri", so it must not be - // encrypted (which can't be done by Chromium). - uri != RtpExtension::kAbsSendTimeUri && -#endif uri != RtpExtension::kEncryptHeaderExtensionsUri; } diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h index ab268104f47..88a8df3ac16 100644 --- a/api/rtp_parameters.h +++ b/api/rtp_parameters.h @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -27,6 +28,7 @@ #include "api/rtp_transceiver_direction.h" #include "api/video/resolution.h" #include "api/video_codecs/scalability_mode.h" +#include "rtc_base/strings/str_join.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -227,6 +229,30 @@ struct RTC_EXPORT RtpCodec { bool operator!=(const RtpCodec& o) const { return !(*this == o); } bool IsResiliencyCodec() const; bool IsMediaCodec() const; + + template + friend void AbslStringify(Sink& sink, const RtpCodec& c) { + absl::Format(&sink, "{mime_type: %s", c.mime_type()); + if (c.clock_rate) { + absl::Format(&sink, ", clock_rate: %d", *c.clock_rate); + } + if (c.num_channels) { + absl::Format(&sink, ", num_channels: %d", *c.num_channels); + } + if (!c.parameters.empty()) { + sink.Append(", parameters: {"); + bool first = true; + for (const auto& kv : c.parameters) { + if (!first) { + sink.Append(", "); + } + absl::Format(&sink, "%s: %s", kv.first, kv.second); + first = false; + } + sink.Append("}"); + } + sink.Append("}"); + } }; // RtpCodecCapability is to RtpCodecParameters as RtpCapabilities is to @@ -261,6 +287,14 @@ struct RTC_EXPORT RtpCodecCapability : public RtpCodec { } }; +enum class RtpTransceiverIdDomain { + // Only allocate IDs that fit in one-byte header extensions. + kOneByteOnly, + // Prefer to allocate one-byte header extension IDs, but overflow to + // two-byte if none are left. + kTwoByteAllowed, +}; + // Used in RtpCapabilities and RtpTransceiverInterface's header extensions query // and setup methods; represents the capabilities/preferences of an // implementation for a header extension. @@ -642,6 +676,44 @@ struct RTC_EXPORT RtpEncodingParameters { bool operator!=(const RtpEncodingParameters& o) const { return !(*this == o); } + + template + friend void AbslStringify(Sink& sink, const RtpEncodingParameters& p) { + sink.Append("{"); + if (p.ssrc) + absl::Format(&sink, "ssrc: %u, ", *p.ssrc); + absl::Format(&sink, "active: %s, ", p.active ? "true" : "false"); + absl::Format(&sink, "rid: '%s', ", p.rid); + if (p.max_bitrate_bps) { + absl::Format(&sink, "max_bitrate_bps: %d, ", *p.max_bitrate_bps); + } + if (p.min_bitrate_bps) { + absl::Format(&sink, "min_bitrate_bps: %d, ", *p.min_bitrate_bps); + } + if (p.max_framerate) { + absl::Format(&sink, "max_framerate: %.2f, ", *p.max_framerate); + } + if (p.num_temporal_layers) { + absl::Format(&sink, "num_temporal_layers: %d, ", *p.num_temporal_layers); + } + if (p.scale_resolution_down_by) { + absl::Format(&sink, "scale_resolution_down_by: %.2f, ", + *p.scale_resolution_down_by); + } + if (p.scale_resolution_down_to) { + absl::Format(&sink, "scale_resolution_down_to: %dx%d, ", + p.scale_resolution_down_to->width, + p.scale_resolution_down_to->height); + } + if (p.scalability_mode) { + absl::Format(&sink, "scalability_mode: '%s', ", *p.scalability_mode); + } + if (p.codec) { + absl::Format(&sink, "codec: %v, ", *p.codec); + } + absl::Format(&sink, "adaptive_ptime: %s}", + p.adaptive_ptime ? "true" : "false"); + } }; struct RTC_EXPORT RtpCodecParameters : public RtpCodec { @@ -660,7 +732,8 @@ struct RTC_EXPORT RtpCodecParameters : public RtpCodec { bool operator!=(const RtpCodecParameters& o) const { return !(*this == o); } template friend void AbslStringify(Sink& sink, const RtpCodecParameters& p) { - absl::Format(&sink, "[%d: %s]", p.payload_type, p.mime_type()); + absl::Format(&sink, "{payload_type: %d, codec: %v}", p.payload_type, + static_cast(p)); } }; @@ -721,6 +794,17 @@ struct RtcpParameters final { reduced_size == o.reduced_size && mux == o.mux; } bool operator!=(const RtcpParameters& o) const { return !(*this == o); } + + template + friend void AbslStringify(Sink& sink, const RtcpParameters& p) { + sink.Append("{"); + if (p.ssrc) + absl::Format(&sink, "ssrc: %u, ", *p.ssrc); + absl::Format(&sink, "cname: '%s', ", p.cname); + absl::Format(&sink, "reduced_size: %s, ", + p.reduced_size ? "true" : "false"); + absl::Format(&sink, "mux: %s}", p.mux ? "true" : "false"); + } }; struct RTC_EXPORT RtpParameters { @@ -767,6 +851,23 @@ struct RTC_EXPORT RtpParameters { // If at least two active encodings have different codec values // (including one being unset and another set), this is considered mixed. bool IsMixedCodec() const; + + template + friend void AbslStringify(Sink& sink, const RtpParameters& p) { + sink.Append("{"); + absl::Format(&sink, "transaction_id: '%s', ", p.transaction_id); + absl::Format(&sink, "mid: '%s', ", p.mid); + absl::Format(&sink, "codecs: [%v], ", StrJoin(p.codecs, ", ")); + absl::Format(&sink, "header_extensions: [%v], ", + StrJoin(p.header_extensions, ", ")); + absl::Format(&sink, "encodings: [%v], ", StrJoin(p.encodings, ", ")); + absl::Format(&sink, "rtcp: %v", p.rtcp); + if (p.degradation_preference) { + absl::Format(&sink, ", degradation_preference: %s", + DegradationPreferenceToString(*p.degradation_preference)); + } + sink.Append("}"); + } }; } // namespace webrtc diff --git a/api/rtp_receiver_interface.h b/api/rtp_receiver_interface.h index 9ac347b45f2..7dae849c48e 100644 --- a/api/rtp_receiver_interface.h +++ b/api/rtp_receiver_interface.h @@ -25,9 +25,13 @@ #include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/ref_count.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "api/transport/rtp/rtp_source.h" +#include "rtc_base/checks.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -131,6 +135,17 @@ class RTC_EXPORT RtpReceiverInterface : public RefCountInterface, void SetFrameTransformer( scoped_refptr frame_transformer) override; + // Creates an internal Sframe decrypter and returns a handle for key + // management. + // Default implementation of CreateSframeDecrypterOrError. + // TODO: issues.webrtc.org/479862368 - make pure virtual when all + // implementations are updated + virtual RTCErrorOr> + CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) { + RTC_DCHECK_NOTREACHED(); + return RTCError(); + } + protected: ~RtpReceiverInterface() override = default; }; diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h index be839285bfd..5ea6efee182 100644 --- a/api/rtp_sender_interface.h +++ b/api/rtp_sender_interface.h @@ -31,7 +31,9 @@ #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/video_codecs/video_encoder_factory.h" +#include "rtc_base/checks.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -135,6 +137,17 @@ class RTC_EXPORT RtpSenderInterface : public RefCountInterface, void SetFrameTransformer(scoped_refptr /* frame_transformer */) override {} + // Creates an internal Sframe encrypter and returns a handle for key + // management. + // Default implementation of CreateSframeEncrypterOrError. + // TODO: bugs.webrtc.org/479862368 - remove when all implementations are + // updated + virtual RTCErrorOr> + CreateSframeEncrypterOrError(const SframeEncrypterInit& options) { + RTC_DCHECK_NOTREACHED(); + return RTCError(); + } + // TODO(crbug.com/1354101): make pure virtual again after Chrome roll. virtual RTCError GenerateKeyFrame(const std::vector& rids) { return RTCError::OK(); diff --git a/api/rtp_transceiver_interface.h b/api/rtp_transceiver_interface.h index a4ff39ab74e..021b215d7b8 100644 --- a/api/rtp_transceiver_interface.h +++ b/api/rtp_transceiver_interface.h @@ -12,11 +12,11 @@ #define API_RTP_TRANSCEIVER_INTERFACE_H_ #include +#include #include #include #include "absl/base/attributes.h" -#include "api/array_view.h" #include "api/media_types.h" #include "api/ref_count.h" #include "api/rtc_error.h" @@ -152,7 +152,7 @@ class RTC_EXPORT RtpTransceiverInterface : public RefCountInterface { // by WebRTC for this transceiver. // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-setcodecpreferences virtual RTCError SetCodecPreferences( - ArrayView codecs) = 0; + std::span codecs) = 0; virtual std::vector codec_preferences() const = 0; // Returns the set of header extensions that was set @@ -172,7 +172,7 @@ class RTC_EXPORT RtpTransceiverInterface : public RefCountInterface { // so that it negotiates use of header extensions which are not kStopped. // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface virtual RTCError SetHeaderExtensionsToNegotiate( - ArrayView header_extensions) = 0; + std::span header_extensions) = 0; protected: ~RtpTransceiverInterface() override = default; diff --git a/api/sframe/sframe_decrypter_interface.h b/api/sframe/sframe_decrypter_interface.h new file mode 100644 index 00000000000..7ad85bd1ed3 --- /dev/null +++ b/api/sframe/sframe_decrypter_interface.h @@ -0,0 +1,36 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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_SFRAME_SFRAME_DECRYPTER_INTERFACE_H_ +#define API_SFRAME_SFRAME_DECRYPTER_INTERFACE_H_ + +#include +#include + +#include "api/ref_count.h" +#include "api/rtc_error.h" + +namespace webrtc { + +// Key management handle for Sframe receiver decryption. +class SframeDecrypterInterface : public RefCountInterface { + public: + virtual RTCError AddDecryptionKey(uint64_t key_id, + std::span key_material) = 0; + + virtual RTCError RemoveDecryptionKey(uint64_t key_id) = 0; + + protected: + ~SframeDecrypterInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_SFRAME_SFRAME_DECRYPTER_INTERFACE_H_ diff --git a/api/sframe/sframe_encrypter_interface.h b/api/sframe/sframe_encrypter_interface.h new file mode 100644 index 00000000000..60babc33895 --- /dev/null +++ b/api/sframe/sframe_encrypter_interface.h @@ -0,0 +1,40 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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_SFRAME_SFRAME_ENCRYPTER_INTERFACE_H_ +#define API_SFRAME_SFRAME_ENCRYPTER_INTERFACE_H_ + +#include +#include + +#include "api/ref_count.h" +#include "api/rtc_error.h" +#include "api/sframe/sframe_types.h" + +namespace webrtc { + +struct SframeEncrypterInit { + SframeMode mode; + SframeCipherSuite cipher_suite; +}; + +// Key management handle for Sframe sender encryption. +class SframeEncrypterInterface : public RefCountInterface { + public: + virtual RTCError SetEncryptionKey(uint64_t key_id, + std::span key_material) = 0; + + protected: + ~SframeEncrypterInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_SFRAME_SFRAME_ENCRYPTER_INTERFACE_H_ diff --git a/api/sframe/sframe_types.h b/api/sframe/sframe_types.h new file mode 100644 index 00000000000..2414bf3d9fe --- /dev/null +++ b/api/sframe/sframe_types.h @@ -0,0 +1,31 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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_SFRAME_SFRAME_TYPES_H_ +#define API_SFRAME_SFRAME_TYPES_H_ + +namespace webrtc { + +enum class SframeMode { + kPerFrame, + kPerPacket, +}; + +enum class SframeCipherSuite { + kAes128CtrHmacSha256_80, + kAes128CtrHmacSha256_64, + kAes128CtrHmacSha256_32, + kAes128GcmSha256_128, + kAes256GcmSha512_128, +}; + +} // namespace webrtc + +#endif // API_SFRAME_SFRAME_TYPES_H_ diff --git a/api/stats/OWNERS b/api/stats/OWNERS index 1556231b31a..b35697f14bf 100644 --- a/api/stats/OWNERS +++ b/api/stats/OWNERS @@ -1,3 +1,4 @@ set noparent hbos@webrtc.org +eshr@webrtc.org hta@webrtc.org diff --git a/api/task_queue/DEPS b/api/task_queue/DEPS index 1365edb504c..a56f855c600 100644 --- a/api/task_queue/DEPS +++ b/api/task_queue/DEPS @@ -1,13 +1,13 @@ specific_include_rules = { - "task_queue_base\.h": [ + "task_queue_base\\.h": [ # Make TaskQueueBase RTC_LOCKABALE to allow annotate variables are only # accessed on specific task queue. "+rtc_base/thread_annotations.h", ], - "task_queue_test\.h": [ + "task_queue_test\\.h": [ "+test/gtest.h", ], - "pending_task_safety_flag.h": [ + "pending_task_safety_flag\\.h": [ "+rtc_base/checks.h", "+rtc_base/system/no_unique_address.h", ], diff --git a/api/task_queue/pending_task_safety_flag.h b/api/task_queue/pending_task_safety_flag.h index 875976eede4..a3c3cbd02e6 100644 --- a/api/task_queue/pending_task_safety_flag.h +++ b/api/task_queue/pending_task_safety_flag.h @@ -19,6 +19,7 @@ #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/task_queue/task_queue_base.h" +#include "rtc_base/checks.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/system/rtc_export.h" @@ -165,6 +166,7 @@ class RTC_EXPORT ScopedTaskSafetyDetached final { inline absl::AnyInvocable SafeTask( scoped_refptr flag, absl::AnyInvocable task) { + RTC_DCHECK(flag); return [flag = std::move(flag), task = std::move(task)]() mutable { if (flag->alive()) { std::move(task)(); @@ -176,6 +178,7 @@ inline absl::AnyInvocable SafeTask( inline absl::AnyInvocable SafeInvocable( scoped_refptr flag, absl::AnyInvocable task) { + RTC_DCHECK(flag); return [flag = std::move(flag), task = std::move(task)]() mutable { if (flag->alive()) { task(); diff --git a/api/task_queue/task_queue_factory.h b/api/task_queue/task_queue_factory.h index b68ab33f691..7586f238de0 100644 --- a/api/task_queue/task_queue_factory.h +++ b/api/task_queue/task_queue_factory.h @@ -22,7 +22,25 @@ class TaskQueueFactory { public: // TaskQueue priority levels. On some platforms these will map to thread // priorities, on others such as Mac and iOS, GCD queue priorities. - enum class Priority { NORMAL = 0, HIGH, LOW }; + enum class Priority { + // Default priority. + kNormal = 0, + // High priority. Use for latency-sensitive tasks. + kHigh, + // Video priority. Use for video pipeline tasks. + kVideo, + // Audio priority. Use for audio pipeline tasks not including rendering. + kAudio, + // Low priority. Use for background tasks (e.g. logging, cleanup). + kLow, + + // Deprecated: Use kNormal instead. + NORMAL = kNormal, + // Deprecated: Use kHigh instead. + HIGH = kHigh, + // Deprecated: Use kLow instead. + LOW = kLow, + }; virtual ~TaskQueueFactory() = default; virtual std::unique_ptr CreateTaskQueue( diff --git a/api/task_queue/task_queue_test.cc b/api/task_queue/task_queue_test.cc index 24491aac953..ba07e39f9f0 100644 --- a/api/task_queue/task_queue_test.cc +++ b/api/task_queue/task_queue_test.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -38,11 +39,14 @@ #include "rtc_base/event.h" #include "rtc_base/ref_counter.h" #include "rtc_base/time_utils.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { +using ::testing::ElementsAre; + // Avoids a dependency to system_wrappers. void SleepFor(TimeDelta duration) { ScopedAllowBaseSyncPrimitivesForTesting allow; @@ -53,7 +57,7 @@ void SleepFor(TimeDelta duration) { std::unique_ptr CreateTaskQueue( const std::unique_ptr& factory, absl::string_view task_queue_name, - TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) { + TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::kNormal) { return factory->CreateTaskQueue(task_queue_name, priority); } @@ -81,6 +85,29 @@ TEST_P(TaskQueueTest, PostAndCheckCurrent) { EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); } +// Verifies that a task can post a new task from within the task +// and that the new one eventually runs. +TEST_P(TaskQueueTest, TaskCanPostContinuationTask) { + std::unique_ptr factory = GetParam()(nullptr); + auto queue = CreateTaskQueue(factory, "TaskCanPostContinuationTask"); + + std::vector events; + Event done; + + queue->PostTask([&events, &done, queue = queue.get()] { + events.push_back("Start"); + queue->PostTask([&events, &done] { + events.push_back("Continue"); + done.Set(); + }); + events.push_back("FirstDone"); + }); + + EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1))); + + EXPECT_THAT(events, ElementsAre("Start", "FirstDone", "Continue")); +} + TEST_P(TaskQueueTest, PostCustomTask) { std::unique_ptr factory = GetParam()(nullptr); Event ran; @@ -122,8 +149,8 @@ TEST_P(TaskQueueTest, PostFromQueue) { TEST_P(TaskQueueTest, PostDelayed) { std::unique_ptr factory = GetParam()(nullptr); Event event; - auto queue = - CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH); + auto queue = CreateTaskQueue(factory, "PostDelayed", + TaskQueueFactory::Priority::kHigh); int64_t start = TimeMillis(); queue->PostDelayedTask( diff --git a/api/test/DEPS b/api/test/DEPS index b3504bd021b..2fbfb6d4f11 100644 --- a/api/test/DEPS +++ b/api/test/DEPS @@ -8,23 +8,23 @@ specific_include_rules = { ".*": [ "+rtc_base/ref_counted_object.h", ], - "dummy_peer_connection\.h": [ + "dummy_peer_connection\\.h": [ "+rtc_base/ref_counted_object.h", ], - "neteq_factory_with_codecs\.h": [ + "neteq_factory_with_codecs\\.h": [ "+system_wrappers/include/clock.h", ], - "network_emulation_manager\.h": [ + "network_emulation_manager\\.h": [ "+rtc_base/network_constants.h", "+rtc_base/ip_address.h", "+rtc_base/socket_address.h", ], - "peer_network_dependencies\.h": [ + "peer_network_dependencies\\.h": [ "+rtc_base/socket_factory.h", "+rtc_base/network.h", "+rtc_base/thread.h", ], - "peerconnection_quality_test_fixture\.h": [ + "peerconnection_quality_test_fixture\\.h": [ "+logging/rtc_event_log/rtc_event_log_factory_interface.h", "+rtc_base/network.h", "+rtc_base/rtc_certificate_generator.h", @@ -33,38 +33,38 @@ specific_include_rules = { "+media/base/media_constants.h", "+modules/audio_processing/include/audio_processing.h", ], - "time_controller\.h": [ + "time_controller\\.h": [ "+rtc_base/synchronization/yield_policy.h", "+system_wrappers/include/clock.h", "+rtc_base/socket_server.h", ], - "create_frame_generator\.h": [ + "create_frame_generator\\.h": [ "+system_wrappers/include/clock.h", ], - "mock_async_dns_resolver\.h": [ + "mock_async_dns_resolver\\.h": [ "+rtc_base/socket_address.h", ], - "mock_local_network_access_permission\.h": [ + "mock_local_network_access_permission\\.h": [ "+rtc_base/socket_address.h", ], - "mock_packet_socket_factory\.h": [ + "mock_packet_socket_factory\\.h": [ "+rtc_base/async_packet_socket.h", "+rtc_base/socket_address.h", ], - "mock_peerconnectioninterface\.h": [ + "mock_peerconnectioninterface\\.h": [ "+rtc_base/thread.h", ], - "mock_peer_connection_factory_interface\.h": [ + "mock_peer_connection_factory_interface\\.h": [ "+p2p/base/port_allocator.h", "+rtc_base/rtc_certificate_generator.h", ], - "mock_peerconnectioninterface\.h": [ + "mock_peerconnectioninterface\\.h": [ "+rtc_base/thread.h", ], - "videocodec_test_fixture\.h": [ + "videocodec_test_fixture\\.h": [ "+modules/video_coding/codecs/h264/include/h264_globals.h", ], - "rtc_error_matchers\.h": [ + "rtc_error_matchers\\.h": [ "+test/gmock.h", ], } diff --git a/api/test/fake_frame_decryptor.cc b/api/test/fake_frame_decryptor.cc index 0e1da2c3f20..a57c086ab13 100644 --- a/api/test/fake_frame_decryptor.cc +++ b/api/test/fake_frame_decryptor.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/media_types.h" #include "rtc_base/checks.h" @@ -27,9 +27,9 @@ FakeFrameDecryptor::FakeFrameDecryptor(uint8_t fake_key, FakeFrameDecryptor::Result FakeFrameDecryptor::Decrypt( MediaType /* media_type */, const std::vector& /* csrcs */, - ArrayView /* additional_data */, - ArrayView encrypted_frame, - ArrayView frame) { + std::span /* additional_data */, + std::span encrypted_frame, + std::span frame) { if (fail_decryption_) { return Result(Status::kFailedToDecrypt, 0); } diff --git a/api/test/fake_frame_decryptor.h b/api/test/fake_frame_decryptor.h index b3a79970d2d..018a3e5b81a 100644 --- a/api/test/fake_frame_decryptor.h +++ b/api/test/fake_frame_decryptor.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/media_types.h" @@ -36,9 +36,9 @@ class FakeFrameDecryptor : public FrameDecryptorInterface { // the postfix byte. This will always fail if fail_decryption_ is set to true. Result Decrypt(MediaType media_type, const std::vector& csrcs, - ArrayView additional_data, - ArrayView encrypted_frame, - ArrayView frame) override; + std::span additional_data, + std::span encrypted_frame, + std::span frame) override; // Always returns 1 less than the size of the encrypted frame. size_t GetMaxPlaintextByteSize(MediaType media_type, size_t encrypted_frame_size) override; diff --git a/api/test/fake_frame_encryptor.cc b/api/test/fake_frame_encryptor.cc index d35756e065c..da996c478e9 100644 --- a/api/test/fake_frame_encryptor.cc +++ b/api/test/fake_frame_encryptor.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/media_types.h" #include "rtc_base/checks.h" @@ -24,9 +24,9 @@ FakeFrameEncryptor::FakeFrameEncryptor(uint8_t fake_key, uint8_t postfix_byte) // FrameEncryptorInterface implementation int FakeFrameEncryptor::Encrypt(MediaType /* media_type */, uint32_t /* ssrc */, - ArrayView /* additional_data */, - ArrayView frame, - ArrayView encrypted_frame, + std::span /* additional_data */, + std::span frame, + std::span encrypted_frame, size_t* bytes_written) { if (fail_encryption_) { return static_cast(FakeEncryptionStatus::FORCED_FAILURE); diff --git a/api/test/fake_frame_encryptor.h b/api/test/fake_frame_encryptor.h index eae0e41b3d6..4f8d6c29571 100644 --- a/api/test/fake_frame_encryptor.h +++ b/api/test/fake_frame_encryptor.h @@ -14,7 +14,8 @@ #include #include -#include "api/array_view.h" +#include + #include "api/crypto/frame_encryptor_interface.h" #include "api/media_types.h" #include "rtc_base/ref_counted_object.h" @@ -34,9 +35,9 @@ class FakeFrameEncryptor : public RefCountedObject { // bit to the end. This will always fail if fail_encryption_ is set to true. int Encrypt(MediaType media_type, uint32_t ssrc, - ArrayView additional_data, - ArrayView frame, - ArrayView encrypted_frame, + std::span additional_data, + std::span frame, + std::span encrypted_frame, size_t* bytes_written) override; // Always returns 1 more than the size of the frame. size_t GetMaxCiphertextByteSize(MediaType media_type, diff --git a/api/test/metrics/BUILD.gn b/api/test/metrics/BUILD.gn index abb6453bbef..7834e21c6da 100644 --- a/api/test/metrics/BUILD.gn +++ b/api/test/metrics/BUILD.gn @@ -95,10 +95,7 @@ rtc_library("metrics_accumulator") { rtc_library("metrics_exporter") { visibility = [ "*" ] sources = [ "metrics_exporter.h" ] - deps = [ - ":metric", - "../..:array_view", - ] + deps = [ ":metric" ] } if (!build_with_chromium) { @@ -114,7 +111,6 @@ if (!build_with_chromium) { deps = [ ":metric", ":metrics_exporter", - "../..:array_view", "../../../rtc_base:stringutils", "../../../test:test_flags", "//third_party/abseil-cpp/absl/flags:flag", @@ -133,7 +129,6 @@ rtc_library("chrome_perf_dashboard_metrics_exporter") { deps = [ ":metric", ":metrics_exporter", - "../..:array_view", "../../../test:fileutils", "../../../test:perf_test", "//third_party/abseil-cpp/absl/memory", @@ -160,7 +155,6 @@ rtc_library("metrics_set_proto_file_exporter") { deps = [ ":metric", ":metrics_exporter", - "../..:array_view", "../../../rtc_base:logging", "../../../test:fileutils", "//third_party/abseil-cpp/absl/strings:string_view", @@ -181,7 +175,6 @@ rtc_library("print_result_proxy_metrics_exporter") { deps = [ ":metric", ":metrics_exporter", - "../..:array_view", "../../../test:perf_test", "../../numerics", ] @@ -259,7 +252,6 @@ if (rtc_include_tests) { ":metric", ":metrics_exporter", ":metrics_logger", - "../..:array_view", "../../../system_wrappers", "../../../test:test_support", ] diff --git a/api/test/metrics/DEPS b/api/test/metrics/DEPS index 9edcc370ed0..d7929a5852b 100644 --- a/api/test/metrics/DEPS +++ b/api/test/metrics/DEPS @@ -1,18 +1,18 @@ specific_include_rules = { - "metrics_logger_and_exporter\.h": [ + "metrics_logger_and_exporter\\.h": [ "+rtc_base/synchronization/mutex.h", "+system_wrappers/include/clock.h", ], - "metrics_logger\.h": [ + "metrics_logger\\.h": [ "+rtc_base/synchronization/mutex.h", "+rtc_base/thread_annotations.h", "+system_wrappers/include/clock.h", ], - "metrics_accumulator\.h": [ + "metrics_accumulator\\.h": [ "+rtc_base/synchronization/mutex.h", "+rtc_base/thread_annotations.h", ], - "chrome_perf_dashboard_metrics_exporter_test.cc": [ + "chrome_perf_dashboard_metrics_exporter_test\\.cc": [ "+tracing/tracing/proto/histogram.pb.h" ], } diff --git a/api/test/metrics/chrome_perf_dashboard_metrics_exporter.cc b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.cc index 79d50d6c647..3ba87cb96d9 100644 --- a/api/test/metrics/chrome_perf_dashboard_metrics_exporter.cc +++ b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.cc @@ -11,12 +11,12 @@ #include #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.h" @@ -101,7 +101,7 @@ ChromePerfDashboardMetricsExporter::ChromePerfDashboardMetricsExporter( : export_file_path_(export_file_path) {} bool ChromePerfDashboardMetricsExporter::Export( - ArrayView metrics) { + std::span metrics) { std::unique_ptr writer = absl::WrapUnique(CreateHistogramWriter()); for (const Metric& metric : metrics) { diff --git a/api/test/metrics/chrome_perf_dashboard_metrics_exporter.h b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.h index 10447510a86..03959930cb7 100644 --- a/api/test/metrics/chrome_perf_dashboard_metrics_exporter.h +++ b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.h @@ -11,10 +11,10 @@ #ifndef API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_ #define API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_ +#include #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" @@ -29,7 +29,7 @@ class ChromePerfDashboardMetricsExporter : public MetricsExporter { absl::string_view export_file_path); ~ChromePerfDashboardMetricsExporter() override = default; - bool Export(ArrayView metrics) override; + bool Export(std::span metrics) override; private: const std::string export_file_path_; diff --git a/api/test/metrics/global_metrics_logger_and_exporter_test.cc b/api/test/metrics/global_metrics_logger_and_exporter_test.cc index bf462a6f3f5..4e50a938c51 100644 --- a/api/test/metrics/global_metrics_logger_and_exporter_test.cc +++ b/api/test/metrics/global_metrics_logger_and_exporter_test.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/test/metrics/metric.h" #include "api/test/metrics/metrics_exporter.h" #include "api/test/metrics/metrics_logger.h" @@ -54,7 +54,7 @@ struct TestMetricsExporterFactory { : factory_(factory), export_result_(export_result) {} ~TestMetricsExporter() override = default; - bool Export(ArrayView metrics) override { + bool Export(std::span metrics) override { factory_->exported_metrics = std::vector(metrics.begin(), metrics.end()); return export_result_; diff --git a/api/test/metrics/metrics_exporter.h b/api/test/metrics/metrics_exporter.h index b71d4fbd936..2640fc81252 100644 --- a/api/test/metrics/metrics_exporter.h +++ b/api/test/metrics/metrics_exporter.h @@ -11,7 +11,8 @@ #ifndef API_TEST_METRICS_METRICS_EXPORTER_H_ #define API_TEST_METRICS_METRICS_EXPORTER_H_ -#include "api/array_view.h" +#include + #include "api/test/metrics/metric.h" namespace webrtc { @@ -24,7 +25,7 @@ class MetricsExporter { // Exports specified metrics in a format that depends on the implementation. // Returns true if export succeeded, false otherwise. - virtual bool Export(ArrayView metrics) = 0; + virtual bool Export(std::span metrics) = 0; }; } // namespace test diff --git a/api/test/metrics/metrics_set_proto_file_exporter.cc b/api/test/metrics/metrics_set_proto_file_exporter.cc index c3a75545850..88588e890d3 100644 --- a/api/test/metrics/metrics_set_proto_file_exporter.cc +++ b/api/test/metrics/metrics_set_proto_file_exporter.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/test/metrics/metric.h" #include "rtc_base/logging.h" #include "test/testsupport/file_utils.h" @@ -131,7 +131,7 @@ MetricsSetProtoFileExporter::Options::Options( std::map metadata) : export_file_path(export_file_path), metadata(std::move(metadata)) {} -bool MetricsSetProtoFileExporter::Export(ArrayView metrics) { +bool MetricsSetProtoFileExporter::Export(std::span metrics) { #if WEBRTC_ENABLE_PROTOBUF test_metrics::MetricsSet metrics_set; for (const auto& [key, value] : options_.metadata) { diff --git a/api/test/metrics/metrics_set_proto_file_exporter.h b/api/test/metrics/metrics_set_proto_file_exporter.h index 6e38eb99811..6338527c1b9 100644 --- a/api/test/metrics/metrics_set_proto_file_exporter.h +++ b/api/test/metrics/metrics_set_proto_file_exporter.h @@ -12,10 +12,10 @@ #define API_TEST_METRICS_METRICS_SET_PROTO_FILE_EXPORTER_H_ #include +#include #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" @@ -48,7 +48,7 @@ class MetricsSetProtoFileExporter : public MetricsExporter { MetricsSetProtoFileExporter& operator=(const MetricsSetProtoFileExporter&) = delete; - bool Export(ArrayView metrics) override; + bool Export(std::span metrics) override; private: const Options options_; diff --git a/api/test/metrics/print_result_proxy_metrics_exporter.cc b/api/test/metrics/print_result_proxy_metrics_exporter.cc index cb163facdea..2a168585efd 100644 --- a/api/test/metrics/print_result_proxy_metrics_exporter.cc +++ b/api/test/metrics/print_result_proxy_metrics_exporter.cc @@ -10,10 +10,10 @@ #include "api/test/metrics/print_result_proxy_metrics_exporter.h" #include +#include #include #include -#include "api/array_view.h" #include "api/numerics/samples_stats_counter.h" #include "api/test/metrics/metric.h" #include "test/testsupport/perf_test.h" @@ -78,7 +78,7 @@ bool NameEndsWithConnected(const std::string& name) { } // namespace -bool PrintResultProxyMetricsExporter::Export(ArrayView metrics) { +bool PrintResultProxyMetricsExporter::Export(std::span metrics) { static const std::unordered_set per_call_metrics{ "actual_encode_bitrate", "encode_frame_rate", diff --git a/api/test/metrics/print_result_proxy_metrics_exporter.h b/api/test/metrics/print_result_proxy_metrics_exporter.h index cbc72ab212a..a84304bda59 100644 --- a/api/test/metrics/print_result_proxy_metrics_exporter.h +++ b/api/test/metrics/print_result_proxy_metrics_exporter.h @@ -11,7 +11,8 @@ #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 + #include "api/test/metrics/metric.h" #include "api/test/metrics/metrics_exporter.h" @@ -23,7 +24,7 @@ class PrintResultProxyMetricsExporter : public MetricsExporter { public: ~PrintResultProxyMetricsExporter() override = default; - bool Export(ArrayView metrics) override; + bool Export(std::span metrics) override; }; } // namespace test diff --git a/api/test/metrics/stdout_metrics_exporter.cc b/api/test/metrics/stdout_metrics_exporter.cc index 336817f8da4..f87fe026b4f 100644 --- a/api/test/metrics/stdout_metrics_exporter.cc +++ b/api/test/metrics/stdout_metrics_exporter.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include "absl/flags/flag.h" #include "absl/strings/str_cat.h" -#include "api/array_view.h" #include "api/test/metrics/metric.h" #include "rtc_base/strings/string_builder.h" #include "test/test_flags.h" @@ -89,7 +89,7 @@ std::string TestCaseAndMetadata(const Metric& metric) { StdoutMetricsExporter::StdoutMetricsExporter() : output_(stdout) {} -bool StdoutMetricsExporter::Export(ArrayView metrics) { +bool StdoutMetricsExporter::Export(std::span metrics) { for (const Metric& metric : metrics) { PrintMetric(metric); } diff --git a/api/test/metrics/stdout_metrics_exporter.h b/api/test/metrics/stdout_metrics_exporter.h index 60354b58fd6..bdf473bf6e7 100644 --- a/api/test/metrics/stdout_metrics_exporter.h +++ b/api/test/metrics/stdout_metrics_exporter.h @@ -12,8 +12,8 @@ #define API_TEST_METRICS_STDOUT_METRICS_EXPORTER_H_ #include +#include -#include "api/array_view.h" #include "api/test/metrics/metric.h" #include "api/test/metrics/metrics_exporter.h" @@ -29,7 +29,7 @@ class StdoutMetricsExporter : public MetricsExporter { StdoutMetricsExporter(const StdoutMetricsExporter&) = delete; StdoutMetricsExporter& operator=(const StdoutMetricsExporter&) = delete; - bool Export(ArrayView metrics) override; + bool Export(std::span metrics) override; private: void PrintMetric(const Metric& metric); diff --git a/api/test/mock_datagram_connection.h b/api/test/mock_datagram_connection.h index 128add909da..e31e4fe7b05 100644 --- a/api/test/mock_datagram_connection.h +++ b/api/test/mock_datagram_connection.h @@ -12,11 +12,11 @@ #include #include +#include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/datagram_connection.h" #include "p2p/base/transport_description.h" @@ -45,14 +45,14 @@ class MockDatagramConnection : public DatagramConnection { (override)); MOCK_METHOD(void, SendPackets, - (ArrayView packets), + (std::span packets), (override)); MOCK_METHOD(void, Terminate, (absl::AnyInvocable terminate_complete_callback), (override)); - MOCK_METHOD(std::string_view, IceUsernameFragment, (), (override)); - MOCK_METHOD(std::string_view, IcePassword, (), (override)); + MOCK_METHOD(absl::string_view, IceUsernameFragment, (), (override)); + MOCK_METHOD(absl::string_view, IcePassword, (), (override)); }; static_assert(!std::is_abstract_v>, diff --git a/api/test/mock_datagram_connection_observer.h b/api/test/mock_datagram_connection_observer.h index 985087210d7..c997865890e 100644 --- a/api/test/mock_datagram_connection_observer.h +++ b/api/test/mock_datagram_connection_observer.h @@ -11,8 +11,8 @@ #define API_TEST_MOCK_DATAGRAM_CONNECTION_OBSERVER_H_ #include +#include -#include "api/array_view.h" #include "api/candidate.h" #include "api/datagram_connection.h" #include "test/gmock.h" @@ -27,7 +27,7 @@ class MockDatagramConnectionObserver : public DatagramConnection::Observer { (override)); MOCK_METHOD(void, OnPacketReceived, - (ArrayView data, PacketMetadata metadata), + (std::span data, PacketMetadata metadata), (override)); MOCK_METHOD(void, OnSendOutcome, (SendOutcome send_outcome), (override)); MOCK_METHOD(void, OnConnectionError, (), (override)); diff --git a/api/test/mock_frame_decryptor.h b/api/test/mock_frame_decryptor.h index 191a7dd83a2..93ed397b6f1 100644 --- a/api/test/mock_frame_decryptor.h +++ b/api/test/mock_frame_decryptor.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/media_types.h" #include "test/gmock.h" @@ -28,9 +28,9 @@ class MockFrameDecryptor : public FrameDecryptorInterface { Decrypt, (MediaType, const std::vector&, - ArrayView, - ArrayView, - ArrayView), + std::span, + std::span, + std::span), (override)); MOCK_METHOD(size_t, diff --git a/api/test/mock_frame_encryptor.h b/api/test/mock_frame_encryptor.h index a449a29aee5..85e3507777e 100644 --- a/api/test/mock_frame_encryptor.h +++ b/api/test/mock_frame_encryptor.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/crypto/frame_encryptor_interface.h" #include "api/media_types.h" #include "test/gmock.h" @@ -27,9 +27,9 @@ class MockFrameEncryptor : public FrameEncryptorInterface { Encrypt, (MediaType, uint32_t, - ArrayView, - ArrayView, - ArrayView, + std::span, + std::span, + std::span, size_t*), (override)); diff --git a/api/test/mock_packet_socket_factory.h b/api/test/mock_packet_socket_factory.h index bfd0e878098..46fd1f38a5c 100644 --- a/api/test/mock_packet_socket_factory.h +++ b/api/test/mock_packet_socket_factory.h @@ -45,6 +45,16 @@ class MockPacketSocketFactory : public PacketSocketFactory { CreateAsyncDnsResolver, (), (override)); + + MOCK_METHOD(std::unique_ptr, + CreateClientUdpSocket, + (const Environment&, + const SocketAddress&, + const SocketAddress&, + uint16_t, + uint16_t, + const PacketSocketTcpOptions&), + (override)); }; static_assert(!std::is_abstract_v, ""); diff --git a/api/test/mock_peerconnectioninterface.h b/api/test/mock_peerconnectioninterface.h index 97acb74563a..510dee9bbac 100644 --- a/api/test/mock_peerconnectioninterface.h +++ b/api/test/mock_peerconnectioninterface.h @@ -110,10 +110,13 @@ class MockPeerConnectionInterface : public PeerConnectionInterface { GetTransceivers, (), (const, override)); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" MOCK_METHOD(bool, GetStats, (StatsObserver*, MediaStreamTrackInterface*, StatsOutputLevel), (override)); +#pragma clang diagnostic pop MOCK_METHOD(void, GetStats, (RTCStatsCollectorCallback*), (override)); MOCK_METHOD(void, GetStats, diff --git a/api/test/mock_rtp_transceiver.h b/api/test/mock_rtp_transceiver.h index 1a55efa1e12..25a5c89e99a 100644 --- a/api/test/mock_rtp_transceiver.h +++ b/api/test/mock_rtp_transceiver.h @@ -12,10 +12,10 @@ #define API_TEST_MOCK_RTP_TRANSCEIVER_H_ #include +#include #include #include -#include "api/array_view.h" #include "api/make_ref_counted.h" #include "api/media_types.h" #include "api/rtc_error.h" @@ -68,7 +68,7 @@ class MockRtpTransceiver : public RtpTransceiverInterface { MOCK_METHOD(void, Stop, (), (override)); MOCK_METHOD(RTCError, SetCodecPreferences, - (ArrayView codecs), + (std::span codecs), (override)); MOCK_METHOD(std::vector, codec_preferences, @@ -84,7 +84,7 @@ class MockRtpTransceiver : public RtpTransceiverInterface { (const, override)); MOCK_METHOD(RTCError, SetHeaderExtensionsToNegotiate, - (ArrayView header_extensions), + (std::span header_extensions), (override)); }; diff --git a/api/test/mock_transformable_audio_frame.h b/api/test/mock_transformable_audio_frame.h index 9de3c3fa296..6abb24a547d 100644 --- a/api/test/mock_transformable_audio_frame.h +++ b/api/test/mock_transformable_audio_frame.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -27,8 +27,8 @@ class MockTransformableAudioFrame : public TransformableAudioFrameInterface { public: MockTransformableAudioFrame() : TransformableAudioFrameInterface(Passkey()) {} - MOCK_METHOD(ArrayView, GetData, (), (const, override)); - MOCK_METHOD(void, SetData, (ArrayView), (override)); + MOCK_METHOD(std::span, GetData, (), (const, override)); + MOCK_METHOD(void, SetData, (std::span), (override)); MOCK_METHOD(void, SetRTPTimestamp, (uint32_t), (override)); MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override)); MOCK_METHOD(bool, CanSetPayloadType, (), (const, override)); @@ -36,7 +36,7 @@ class MockTransformableAudioFrame : public TransformableAudioFrameInterface { MOCK_METHOD(uint32_t, GetSsrc, (), (const, override)); MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override)); MOCK_METHOD(std::string, GetMimeType, (), (const, override)); - MOCK_METHOD(ArrayView, + MOCK_METHOD(std::span, GetContributingSources, (), (const, override)); diff --git a/api/test/mock_transformable_frame.h b/api/test/mock_transformable_frame.h index 252622abc41..804901d41b8 100644 --- a/api/test/mock_transformable_frame.h +++ b/api/test/mock_transformable_frame.h @@ -14,10 +14,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -29,8 +29,8 @@ class MockTransformableFrame : public TransformableFrameInterface { public: MockTransformableFrame() : TransformableFrameInterface(Passkey()) {} - MOCK_METHOD(ArrayView, GetData, (), (const, override)); - MOCK_METHOD(void, SetData, (ArrayView), (override)); + MOCK_METHOD(std::span, GetData, (), (const, override)); + MOCK_METHOD(void, SetData, (std::span), (override)); MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override)); MOCK_METHOD(bool, CanSetPayloadType, (), (const, override)); MOCK_METHOD(void, SetPayloadType, (uint8_t), (override)); diff --git a/api/test/mock_transformable_video_frame.h b/api/test/mock_transformable_video_frame.h index 2529dcca663..8f418a32b99 100644 --- a/api/test/mock_transformable_video_frame.h +++ b/api/test/mock_transformable_video_frame.h @@ -13,10 +13,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -28,8 +28,8 @@ namespace webrtc { class MockTransformableVideoFrame : public TransformableVideoFrameInterface { public: MockTransformableVideoFrame() : TransformableVideoFrameInterface(Passkey()) {} - MOCK_METHOD(ArrayView, GetData, (), (const, override)); - MOCK_METHOD(void, SetData, (ArrayView data), (override)); + MOCK_METHOD(std::span, GetData, (), (const, override)); + MOCK_METHOD(void, SetData, (std::span data), (override)); MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override)); MOCK_METHOD(void, SetRTPTimestamp, (uint32_t), (override)); MOCK_METHOD(uint32_t, GetSsrc, (), (const, override)); diff --git a/api/test/mock_video_bitrate_allocator.h b/api/test/mock_video_bitrate_allocator.h index d933cf7727e..557bafce8cf 100644 --- a/api/test/mock_video_bitrate_allocator.h +++ b/api/test/mock_video_bitrate_allocator.h @@ -18,6 +18,7 @@ namespace webrtc { class MockVideoBitrateAllocator : public VideoBitrateAllocator { + public: MOCK_METHOD(VideoBitrateAllocation, Allocate, (VideoBitrateAllocationParameters parameters), diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn index 9d0ec6da4e1..7e9e0ff8af8 100644 --- a/api/test/network_emulation/BUILD.gn +++ b/api/test/network_emulation/BUILD.gn @@ -24,8 +24,8 @@ if (rtc_enable_protobuf) { ] deps = [ ":network_config_schedule_proto", + ":network_queue", "../..:network_emulation_manager_api", - "../../../rtc_base:timeutils", "../../../test/network:schedulable_network_behavior", "../../units:timestamp", "//third_party/abseil-cpp/absl/functional:any_invocable", diff --git a/api/test/network_emulation/schedulable_network_node_builder.cc b/api/test/network_emulation/schedulable_network_node_builder.cc index 8040e2b9035..d959338434d 100644 --- a/api/test/network_emulation/schedulable_network_node_builder.cc +++ b/api/test/network_emulation/schedulable_network_node_builder.cc @@ -15,7 +15,9 @@ #include #include "absl/functional/any_invocable.h" +#include "api/test/network_emulation/leaky_bucket_network_queue.h" #include "api/test/network_emulation/network_config_schedule.pb.h" +#include "api/test/network_emulation/network_queue.h" #include "api/test/network_emulation_manager.h" #include "api/units/timestamp.h" #include "test/network/schedulable_network_behavior.h" @@ -34,6 +36,11 @@ void SchedulableNetworkNodeBuilder::set_start_condition( start_condition_ = std::move(start_condition); } +void SchedulableNetworkNodeBuilder::set_queue_factory( + NetworkQueueFactory& queue_factory) { + queue_factory_ = &queue_factory; +} + EmulatedNetworkNode* SchedulableNetworkNodeBuilder::Build( std::optional random_seed) { uint64_t seed = @@ -41,8 +48,11 @@ EmulatedNetworkNode* SchedulableNetworkNodeBuilder::Build( ? *random_seed : static_cast( net_.time_controller()->GetClock()->CurrentTime().ns()); + std::unique_ptr network_queue = + queue_factory_ ? queue_factory_->CreateQueue() + : std::make_unique(); return net_.CreateEmulatedNode(std::make_unique( std::move(schedule_), seed, *net_.time_controller()->GetClock(), - std::move(start_condition_))); + std::move(start_condition_), std::move(network_queue))); } } // namespace webrtc diff --git a/api/test/network_emulation/schedulable_network_node_builder.h b/api/test/network_emulation/schedulable_network_node_builder.h index ee15215e403..307c7cabb96 100644 --- a/api/test/network_emulation/schedulable_network_node_builder.h +++ b/api/test/network_emulation/schedulable_network_node_builder.h @@ -15,6 +15,7 @@ #include "absl/functional/any_invocable.h" #include "api/test/network_emulation/network_config_schedule.pb.h" +#include "api/test/network_emulation/network_queue.h" #include "api/test/network_emulation_manager.h" #include "api/units/timestamp.h" @@ -31,6 +32,7 @@ class SchedulableNetworkNodeBuilder { // NetworkConfigScheduleItem is used. There is no guarantee on which // thread/task queue that will be used. void set_start_condition(absl::AnyInvocable start_condition); + void set_queue_factory(NetworkQueueFactory& queue_factory); // If no random seed is provided, one will be created. // The random seed is required for loss rate and to delay standard deviation. @@ -41,6 +43,7 @@ class SchedulableNetworkNodeBuilder { NetworkEmulationManager& net_; network_behaviour::NetworkConfigSchedule schedule_; absl::AnyInvocable start_condition_; + NetworkQueueFactory* queue_factory_ = nullptr; }; } // namespace webrtc diff --git a/api/test/network_emulation_manager.cc b/api/test/network_emulation_manager.cc index dc737f8ee7e..e12caf5872f 100644 --- a/api/test/network_emulation_manager.cc +++ b/api/test/network_emulation_manager.cc @@ -146,27 +146,14 @@ NetworkEmulationManager::SimulatedNetworkNode NetworkEmulationManager::SimulatedNetworkNode::Builder::Build( uint64_t random_seed) const { RTC_CHECK(net_); - return Build(net_, random_seed); -} - -NetworkEmulationManager::SimulatedNetworkNode -NetworkEmulationManager::SimulatedNetworkNode::Builder::Build( - NetworkEmulationManager* net, - uint64_t random_seed) const { - RTC_CHECK(net); - RTC_CHECK(net_ == nullptr || net_ == net); - std::unique_ptr network_queue; - if (queue_factory_ != nullptr) { - network_queue = queue_factory_->CreateQueue(); - } else { - network_queue = std::make_unique(); - } - SimulatedNetworkNode res; + std::unique_ptr network_queue = + queue_factory_ ? queue_factory_->CreateQueue() + : std::make_unique(); auto behavior = std::make_unique(config_, random_seed, std::move(network_queue)); - res.simulation = behavior.get(); - res.node = net->CreateEmulatedNode(std::move(behavior)); - return res; + return SimulatedNetworkNode{ + .simulation = behavior.get(), + .node = net_->CreateEmulatedNode(std::move(behavior))}; } std::pair @@ -177,15 +164,10 @@ NetworkEmulationManager::CreateEndpointPairWithTwoWayRoutes( auto* alice_node = CreateEmulatedNode(config); auto* bob_node = CreateEmulatedNode(config); - std::vector alice_endpoints; - for (int i = 0; i < alice_interface_count; i++) { - alice_endpoints.push_back(CreateEndpoint(EmulatedEndpointConfig())); - } - - std::vector bob_endpoints; - for (int i = 0; i < bob_interface_count; i++) { - bob_endpoints.push_back(CreateEndpoint(EmulatedEndpointConfig())); - } + std::vector alice_endpoints( + alice_interface_count, CreateEndpoint(EmulatedEndpointConfig())); + std::vector bob_endpoints( + bob_interface_count, CreateEndpoint(EmulatedEndpointConfig())); for (auto alice_endpoint : alice_endpoints) { for (auto bob_endpoint : bob_endpoints) { diff --git a/api/test/network_emulation_manager.h b/api/test/network_emulation_manager.h index 52adc336af4..de5afe80276 100644 --- a/api/test/network_emulation_manager.h +++ b/api/test/network_emulation_manager.h @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include "absl/base/nullability.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/test/network_emulation/cross_traffic.h" #include "api/test/network_emulation/network_emulation_interfaces.h" @@ -186,7 +186,6 @@ class NetworkEmulationManager { class Builder { public: explicit Builder(NetworkEmulationManager* net) : net_(net) {} - Builder() : net_(nullptr) {} Builder(const Builder&) = default; // Sets the config state, note that this will replace any previously set // values. @@ -204,8 +203,6 @@ class NetworkEmulationManager { Builder& avg_burst_loss_length(int avg_burst_loss_length); Builder& packet_overhead(int packet_overhead); SimulatedNetworkNode Build(uint64_t random_seed = 1) const; - SimulatedNetworkNode Build(NetworkEmulationManager* net, - uint64_t random_seed = 1) const; private: NetworkEmulationManager* const net_; @@ -349,14 +346,14 @@ class NetworkEmulationManager { // `stats_callback`. Callback will be executed on network emulation // internal task queue. virtual void GetStats( - ArrayView endpoints, + std::span endpoints, 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( - ArrayView nodes, + std::span nodes, std::function stats_callback) = 0; // Create a EmulatedTURNServer. diff --git a/api/test/pclf/BUILD.gn b/api/test/pclf/BUILD.gn index 363beddc705..858b09ba0ea 100644 --- a/api/test/pclf/BUILD.gn +++ b/api/test/pclf/BUILD.gn @@ -17,7 +17,6 @@ rtc_library("media_configuration") { ] deps = [ - "../..:array_view", "../..:audio_options_api", "../..:media_stream_interface", "../..:rtp_parameters", diff --git a/api/test/pclf/DEPS b/api/test/pclf/DEPS index ec2451b05bc..088237970ad 100644 --- a/api/test/pclf/DEPS +++ b/api/test/pclf/DEPS @@ -8,7 +8,7 @@ specific_include_rules = { "+rtc_base/socket_factory.h", "+rtc_base/thread.h", ], - "media_quality_test_params\.h": [ + "media_quality_test_params\\.h": [ "+p2p/base/port_allocator.h", "+rtc_base/socket_factory.h", ], diff --git a/api/test/pclf/media_configuration.cc b/api/test/pclf/media_configuration.cc index 679486ded4f..1ce3f2a5e29 100644 --- a/api/test/pclf/media_configuration.cc +++ b/api/test/pclf/media_configuration.cc @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/test/video/video_frame_writer.h" #include "api/units/time_delta.h" #include "rtc_base/checks.h" @@ -229,7 +229,7 @@ VideoCodecConfig::VideoCodecConfig( : name(name), required_params(std::move(required_params)) {} std::optional VideoSubscription::GetMaxResolution( - ArrayView video_configs) { + std::span video_configs) { std::vector resolutions; for (const auto& video_config : video_configs) { resolutions.push_back(video_config.GetResolution()); @@ -238,7 +238,7 @@ std::optional VideoSubscription::GetMaxResolution( } std::optional VideoSubscription::GetMaxResolution( - ArrayView resolutions) { + std::span resolutions) { if (resolutions.empty()) { return std::nullopt; } diff --git a/api/test/pclf/media_configuration.h b/api/test/pclf/media_configuration.h index 5f8e45cb760..c39978aa882 100644 --- a/api/test/pclf/media_configuration.h +++ b/api/test/pclf/media_configuration.h @@ -17,11 +17,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_options.h" #include "api/media_stream_interface.h" #include "api/rtp_parameters.h" @@ -394,9 +394,9 @@ class VideoSubscription { // Returns the resolution constructed as maximum from all resolution // dimensions: width, height and fps. static std::optional GetMaxResolution( - ArrayView video_configs); + std::span video_configs); static std::optional GetMaxResolution( - ArrayView resolutions); + std::span resolutions); bool operator==(const VideoSubscription& other) const; bool operator!=(const VideoSubscription& other) const; diff --git a/api/test/video/DEPS b/api/test/video/DEPS index 2256b34db2b..63d42aab75b 100644 --- a/api/test/video/DEPS +++ b/api/test/video/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - "test_video_track_source\.h": [ + "test_video_track_source\\.h": [ "+rtc_base/thread_annotations.h", "+rtc_base/system/no_unique_address.h", ], diff --git a/api/test/video_quality_analyzer_interface.h b/api/test/video_quality_analyzer_interface.h index 48685b9936c..a06342c3a80 100644 --- a/api/test/video_quality_analyzer_interface.h +++ b/api/test/video_quality_analyzer_interface.h @@ -13,10 +13,10 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/stats/rtc_stats_report.h" #include "api/test/stats_observer_interface.h" @@ -88,7 +88,7 @@ class VideoQualityAnalyzerInterface // thread in each method, but should remember, that it is the same thread, // that is used in video pipeline. virtual void Start(std::string /* test_case_name */, - ArrayView /* peer_names */, + std::span /* peer_names */, int /* max_threads_count */) {} // Will be called when frame was generated from the input stream. @@ -112,8 +112,13 @@ class VideoQualityAnalyzerInterface 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 */, - EncodedImageCallback::DropReason /* reason */) {} + virtual void OnFrameDropped(absl::string_view /* peer_name */) {} + // TODO: webrtc:467444018 - Remove when downstream usage is gone. + [[deprecated("Use callback without DropReason parameter")]] + virtual void OnFrameDropped(absl::string_view peer_name, + EncodedImageCallback::DropReason /* reason */) { + OnFrameDropped(peer_name); + } // Will be called before calling the decoder. // `peer_name` is name of the peer on which side frame was received. virtual void OnFramePreDecode(absl::string_view /* peer_name */, diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn index f643c81e4c2..26737a2717a 100644 --- a/api/transport/BUILD.gn +++ b/api/transport/BUILD.gn @@ -31,6 +31,7 @@ rtc_library("bandwidth_estimation_settings") { rtc_source_set("ecn_marking") { visibility = [ "*" ] sources = [ "ecn_marking.h" ] + deps = [ "//third_party/abseil-cpp/absl/strings:string_view" ] } rtc_source_set("enums") { @@ -64,7 +65,6 @@ rtc_source_set("datagram_transport_interface") { visibility = [ "*" ] sources = [ "data_channel_transport_interface.h" ] deps = [ - "..:array_view", "..:priority", "..:rtc_error", "../../rtc_base:copy_on_write_buffer", @@ -109,7 +109,6 @@ rtc_library("stun_types") { ] deps = [ - "../../api:array_view", "../../rtc_base:byte_buffer", "../../rtc_base:byte_order", "../../rtc_base:checks", @@ -159,13 +158,22 @@ if (rtc_include_tests) { } if (rtc_include_tests) { + rtc_library("ecn_marking_unittest") { + testonly = true + sources = [ "ecn_marking_unittest.cc" ] + deps = [ + ":ecn_marking", + "../../test:test_support", + "//third_party/abseil-cpp/absl/strings", + ] + } + rtc_library("stun_unittest") { visibility = [ "*" ] testonly = true sources = [ "stun_unittest.cc" ] deps = [ ":stun_types", - "..:array_view", "../../rtc_base:byte_buffer", "../../rtc_base:byte_order", "../../rtc_base:ip_address", diff --git a/api/transport/DEPS b/api/transport/DEPS index 2202424f068..7da3887844e 100644 --- a/api/transport/DEPS +++ b/api/transport/DEPS @@ -1,11 +1,11 @@ specific_include_rules = { - "stun\.h": [ + "stun\\.h": [ "+rtc_base/byte_buffer.h", "+rtc_base/ip_address.h", "+rtc_base/net_helpers.h", "+rtc_base/socket_address.h", ], - "data_channel_transport_interface\.h": [ + "data_channel_transport_interface\\.h": [ "+rtc_base/ssl_stream_adapter.h", ], } diff --git a/api/transport/DIR_METADATA b/api/transport/DIR_METADATA new file mode 100644 index 00000000000..783c9b89583 --- /dev/null +++ b/api/transport/DIR_METADATA @@ -0,0 +1,3 @@ +buganizer_public: { + component_id: 1565889 # Network +} \ No newline at end of file diff --git a/api/transport/OWNERS b/api/transport/OWNERS index 5991f6fc56a..eef3b09a0fa 100644 --- a/api/transport/OWNERS +++ b/api/transport/OWNERS @@ -1,2 +1,2 @@ -srte@webrtc.org +perkj@webrtc.org terelius@webrtc.org diff --git a/api/transport/ecn_marking.h b/api/transport/ecn_marking.h index bbcab6eb0fd..9a93353e7d0 100644 --- a/api/transport/ecn_marking.h +++ b/api/transport/ecn_marking.h @@ -11,6 +11,10 @@ #ifndef API_TRANSPORT_ECN_MARKING_H_ #define API_TRANSPORT_ECN_MARKING_H_ +#include + +#include "absl/strings/string_view.h" + namespace webrtc { // TODO: bugs.webrtc.org/42225697 - L4S support is slowly being developed. @@ -20,7 +24,7 @@ namespace webrtc { // https://www.rfc-editor.org/rfc/rfc9331.html ECT stands for ECN-Capable // Transport and CE stands for Congestion Experienced. -// RFC-3168, Section 5 +// https://www.rfc-editor.org/rfc/rfc3168.html#section-5 // +-----+-----+ // | ECN FIELD | // +-----+-----+ @@ -30,13 +34,33 @@ namespace webrtc { // 1 0 ECT(0) // 1 1 CE -enum class EcnMarking { - kNotEct = 0, // Not ECN-Capable Transport - kEct1 = 1, // ECN-Capable Transport - kEct0 = 2, // Not used by L4s (or webrtc.) - kCe = 3, // Congestion experienced +enum class EcnMarking : uint8_t { + kNotEct = 0b00, // Not ECN-Capable Transport + kEct1 = 0b01, // ECN-Capable Transport + kEct0 = 0b10, // Not used by L4S (or webrtc.) + kCe = 0b11, // Congestion experienced }; +inline absl::string_view AsString(EcnMarking marking) { + switch (marking) { + case EcnMarking::kNotEct: + return "none"; + case EcnMarking::kEct1: + return "ect1"; + case EcnMarking::kEct0: + return "ect0"; + case EcnMarking::kCe: + return "ce"; + default: + return "unknown"; + } +} + +template +void AbslStringify(Sink& sink, EcnMarking self) { + sink.Append(AsString(self)); +} + } // namespace webrtc #endif // API_TRANSPORT_ECN_MARKING_H_ diff --git a/api/transport/ecn_marking_unittest.cc b/api/transport/ecn_marking_unittest.cc new file mode 100644 index 00000000000..200c481d8a2 --- /dev/null +++ b/api/transport/ecn_marking_unittest.cc @@ -0,0 +1,43 @@ +/* + * Copyright 2026 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can 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/transport/ecn_marking.h" + +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +using ::testing::AnyOfArray; +using ::testing::Not; + +TEST(EcnMarkingTest, StringifyProducesNonTrivialUniqueValues) { + std::vector all; + for (uint8_t i = 0; i < 4; ++i) { + std::string name = absl::StrCat(static_cast(i)); + + // Check name is not trivial - not empty, and not just the number. + EXPECT_NE(name, ""); + EXPECT_NE(name, absl::StrCat(i)); + + // Check that all values are unique. + EXPECT_THAT(name, Not(AnyOfArray(all))); + all.push_back(name); + } +} + +} // namespace +} // namespace webrtc diff --git a/api/transport/network_types.h b/api/transport/network_types.h index fa0617743a8..831e8a3670e 100644 --- a/api/transport/network_types.h +++ b/api/transport/network_types.h @@ -52,7 +52,7 @@ struct RTC_EXPORT StreamsConfig { // If `enable_repeated_initial_probing` is set to true, Probes are sent // periodically every 1s during the first 5s after the network becomes // available. The probes ignores max_total_allocated_bitrate. - std::optional enable_repeated_initial_probing; + bool enable_repeated_initial_probing = false; std::optional pacing_factor; // TODO(srte): Use BitrateAllocationLimits here. diff --git a/api/transport/rtp/BUILD.gn b/api/transport/rtp/BUILD.gn index fa0dc973791..dc627f8a148 100644 --- a/api/transport/rtp/BUILD.gn +++ b/api/transport/rtp/BUILD.gn @@ -41,7 +41,6 @@ rtc_library("corruption_detection_message") { "corruption_detection_message.h", ] deps = [ - "../../:array_view", "../../../rtc_base:checks", "../../video/corruption_detection:frame_instrumentation_data", "//third_party/abseil-cpp/absl/container:inlined_vector", diff --git a/api/transport/rtp/DEPS b/api/transport/rtp/DEPS index 97daa2bb672..562e1b6deb7 100644 --- a/api/transport/rtp/DEPS +++ b/api/transport/rtp/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - "rtp_source\.h": [ + "rtp_source\\.h": [ "+absl/strings/str_format.h", ], } diff --git a/api/transport/rtp/corruption_detection_message.cc b/api/transport/rtp/corruption_detection_message.cc index d812f9ab165..903b0ca16bd 100644 --- a/api/transport/rtp/corruption_detection_message.cc +++ b/api/transport/rtp/corruption_detection_message.cc @@ -11,9 +11,9 @@ #include "api/transport/rtp/corruption_detection_message.h" #include +#include #include -#include "api/array_view.h" #include "api/video/corruption_detection/frame_instrumentation_data.h" #include "rtc_base/checks.h" @@ -116,8 +116,8 @@ CorruptionDetectionMessage::Builder::WithChromaErrorThreshold( CorruptionDetectionMessage::Builder& CorruptionDetectionMessage::Builder::WithSampleValues( - const ArrayView& sample_values) { - message_.sample_values_.assign(sample_values.cbegin(), sample_values.cend()); + const std::span& sample_values) { + message_.sample_values_.assign(sample_values.begin(), sample_values.end()); return *this; } diff --git a/api/transport/rtp/corruption_detection_message.h b/api/transport/rtp/corruption_detection_message.h index 81ea224c7c2..2765b5145ee 100644 --- a/api/transport/rtp/corruption_detection_message.h +++ b/api/transport/rtp/corruption_detection_message.h @@ -13,9 +13,9 @@ #include #include +#include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/video/corruption_detection/frame_instrumentation_data.h" namespace webrtc { @@ -39,8 +39,8 @@ class CorruptionDetectionMessage { double std_dev() const { return std_dev_; } int luma_error_threshold() const { return luma_error_threshold_; } int chroma_error_threshold() const { return chroma_error_threshold_; } - ArrayView sample_values() const { - return MakeArrayView(sample_values_.data(), sample_values_.size()); + std::span sample_values() const { + return std::span(sample_values_.data(), sample_values_.size()); } static CorruptionDetectionMessage FromFrameInstrumentationData( @@ -95,7 +95,7 @@ class CorruptionDetectionMessage::Builder { Builder& WithStdDev(double std_dev); Builder& WithLumaErrorThreshold(int luma_error_threshold); Builder& WithChromaErrorThreshold(int chroma_error_threshold); - Builder& WithSampleValues(const ArrayView& sample_values); + Builder& WithSampleValues(const std::span& sample_values); private: CorruptionDetectionMessage message_; diff --git a/api/transport/stun.cc b/api/transport/stun.cc index 100b6c7e9b3..2849a5aa0c9 100644 --- a/api/transport/stun.cc +++ b/api/transport/stun.cc @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" @@ -51,8 +51,8 @@ uint32_t ReduceTransactionId(absl::string_view transaction_id) { transaction_id.length() == kStunLegacyTransactionIdLength) << transaction_id.length(); ByteBufferReader reader( - MakeArrayView(reinterpret_cast(transaction_id.data()), - transaction_id.size())); + std::span(reinterpret_cast(transaction_id.data()), + transaction_id.size())); uint32_t result = 0; uint32_t next; while (reader.ReadUInt32(&next)) { @@ -379,8 +379,11 @@ bool StunMessage::ValidateMessageIntegrityOfType(int mi_attr_type, return false; } + std::span data_view(reinterpret_cast(data), + size); + // Getting the message length from the STUN header. - uint16_t msg_length = GetBE16(&data[2]); + uint16_t msg_length = GetBE16(data_view.subspan(2, 2)); if (size != (msg_length + kStunHeaderSize)) { return false; } @@ -391,8 +394,9 @@ bool StunMessage::ValidateMessageIntegrityOfType(int mi_attr_type, while (current_pos + 4 <= size) { uint16_t attr_type, attr_length; // Getting attribute type and length. - attr_type = GetBE16(&data[current_pos]); - attr_length = GetBE16(&data[current_pos + sizeof(attr_type)]); + attr_type = GetBE16(data_view.subspan(current_pos, 2)); + attr_length = + GetBE16(data_view.subspan(current_pos + sizeof(attr_type), 2)); // If M-I, sanity check it, and break out. if (attr_type == mi_attr_type) { @@ -433,7 +437,9 @@ bool StunMessage::ValidateMessageIntegrityOfType(int mi_attr_type, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |0 0| STUN Message Type | Message Length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - SetBE16(temp_data.get() + 2, static_cast(new_adjusted_len)); + SetBE16( + std::span(reinterpret_cast(temp_data.get() + 2), 2), + static_cast(new_adjusted_len)); } char hmac[kStunMessageIntegritySize]; @@ -504,22 +510,26 @@ bool StunMessage::ValidateFingerprint(const char* data, size_t size) { if (size % 4 != 0 || size < kStunHeaderSize + fingerprint_attr_size) return false; + std::span data_view(reinterpret_cast(data), + size); + // Skip the rest if the magic cookie isn't present. - const char* magic_cookie = - data + kStunTransactionIdOffset - kStunMagicCookieLength; - if (GetBE32(magic_cookie) != kStunMagicCookie) + size_t magic_cookie_offset = + kStunTransactionIdOffset - kStunMagicCookieLength; + if (GetBE32(data_view.subspan(magic_cookie_offset, 4)) != kStunMagicCookie) return false; // Check the fingerprint type and length. - const char* fingerprint_attr_data = data + size - fingerprint_attr_size; - if (GetBE16(fingerprint_attr_data) != STUN_ATTR_FINGERPRINT || - GetBE16(fingerprint_attr_data + sizeof(uint16_t)) != - StunUInt32Attribute::SIZE) + size_t fingerprint_attr_offset = size - fingerprint_attr_size; + if (GetBE16(data_view.subspan(fingerprint_attr_offset, 2)) != + STUN_ATTR_FINGERPRINT || + GetBE16(data_view.subspan(fingerprint_attr_offset + sizeof(uint16_t), + 2)) != StunUInt32Attribute::SIZE) return false; // Check the fingerprint value. - uint32_t fingerprint = - GetBE32(fingerprint_attr_data + kStunAttributeHeaderSize); + uint32_t fingerprint = GetBE32( + data_view.subspan(fingerprint_attr_offset + kStunAttributeHeaderSize, 4)); return ((fingerprint ^ STUN_FINGERPRINT_XOR_VALUE) == ComputeCrc32(data, size - fingerprint_attr_size)); } @@ -529,20 +539,23 @@ std::string StunMessage::GenerateTransactionId() { return CreateRandomString(kStunTransactionIdLength); } -bool StunMessage::IsStunMethod(ArrayView methods, +bool StunMessage::IsStunMethod(std::span methods, const char* data, size_t size) { // Check the message length. if (size % 4 != 0 || size < kStunHeaderSize) return false; + std::span data_view(reinterpret_cast(data), + size); + // Skip the rest if the magic cookie isn't present. - const char* magic_cookie = - data + kStunTransactionIdOffset - kStunMagicCookieLength; - if (GetBE32(magic_cookie) != kStunMagicCookie) + size_t magic_cookie_offset = + kStunTransactionIdOffset - kStunMagicCookieLength; + if (GetBE32(data_view.subspan(magic_cookie_offset, 4)) != kStunMagicCookie) return false; - int method = GetBE16(data); + int method = GetBE16(data_view); for (int m : methods) { if (m == method) { return true; @@ -803,7 +816,7 @@ void StunAttribute::WritePadding(ByteBufferWriter* buf) const { int remainder = length_ % 4; if (remainder > 0) { uint8_t zeroes[4] = {0}; - buf->Write(ArrayView(zeroes, 4 - remainder)); + buf->Write(std::span(zeroes, 4 - remainder)); } } @@ -902,8 +915,8 @@ bool StunAddressAttribute::Read(ByteBufferReader* buf) { if (length() != SIZE_IP4) { return false; } - if (!buf->ReadBytes(MakeArrayView(reinterpret_cast(&v4addr), - sizeof(v4addr)))) { + if (!buf->ReadBytes( + std::span(reinterpret_cast(&v4addr), sizeof(v4addr)))) { return false; } IPAddress ipaddr(v4addr); @@ -913,8 +926,8 @@ bool StunAddressAttribute::Read(ByteBufferReader* buf) { if (length() != SIZE_IP6) { return false; } - if (!buf->ReadBytes(MakeArrayView(reinterpret_cast(&v6addr), - sizeof(v6addr)))) { + if (!buf->ReadBytes( + std::span(reinterpret_cast(&v6addr), sizeof(v6addr)))) { return false; } IPAddress ipaddr(v6addr); @@ -937,13 +950,13 @@ bool StunAddressAttribute::Write(ByteBufferWriter* buf) const { switch (address_.family()) { case AF_INET: { in_addr v4addr = address_.ipaddr().ipv4_address(); - buf->Write(ArrayView(reinterpret_cast(&v4addr), + buf->Write(std::span(reinterpret_cast(&v4addr), sizeof(v4addr))); break; } case AF_INET6: { in6_addr v6addr = address_.ipaddr().ipv6_address(); - buf->Write(ArrayView(reinterpret_cast(&v6addr), + buf->Write(std::span(reinterpret_cast(&v6addr), sizeof(v6addr))); break; } @@ -1027,13 +1040,13 @@ bool StunXorAddressAttribute::Write(ByteBufferWriter* buf) const { switch (xored_ip.family()) { case AF_INET: { in_addr v4addr = xored_ip.ipv4_address(); - buf->Write(ArrayView( + buf->Write(std::span( reinterpret_cast(&v4addr), sizeof(v4addr))); break; } case AF_INET6: { in6_addr v6addr = xored_ip.ipv6_address(); - buf->Write(ArrayView( + buf->Write(std::span( reinterpret_cast(&v6addr), sizeof(v6addr))); break; } @@ -1172,7 +1185,7 @@ void StunByteStringAttribute::SetByte(size_t index, uint8_t value) { bool StunByteStringAttribute::Read(ByteBufferReader* buf) { bytes_ = new uint8_t[length()]; - if (!buf->ReadBytes(ArrayView(bytes_, length()))) { + if (!buf->ReadBytes(std::span(bytes_, length()))) { return false; } @@ -1185,7 +1198,7 @@ bool StunByteStringAttribute::Write(ByteBufferWriter* buf) const { if (!LengthValid(type(), length())) { return false; } - buf->Write(ArrayView(bytes_, length())); + buf->Write(std::span(bytes_, length())); WritePadding(buf); return true; } diff --git a/api/transport/stun.h b/api/transport/stun.h index 9066a5c6549..f9e0a8f1044 100644 --- a/api/transport/stun.h +++ b/api/transport/stun.h @@ -19,11 +19,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/checks.h" #include "rtc_base/ip_address.h" @@ -255,7 +255,7 @@ class StunMessage { // Verify that a buffer has stun magic cookie and one of the specified // methods. Note that it does not check for the existance of FINGERPRINT. - static bool IsStunMethod(ArrayView methods, + static bool IsStunMethod(std::span methods, const char* data, size_t size); @@ -523,9 +523,7 @@ class StunByteStringAttribute : public StunAttribute { } // Returns the attribute value as an uint8_t view. // Use this function for values that are not text. - ArrayView array_view() const { - return MakeArrayView(bytes_, length()); - } + std::span array_view() const { return std::span(bytes_, length()); } [[deprecated]] std::string GetString() const { return std::string(reinterpret_cast(bytes_), length()); diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc index 542a5bfd24d..21d26ee7d62 100644 --- a/api/transport/stun_unittest.cc +++ b/api/transport/stun_unittest.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/byte_order.h" #include "rtc_base/ip_address.h" @@ -212,6 +212,10 @@ constexpr uint8_t kRtcpPacket[] = { 0x00, 0x03, 0x73, 0x50, }; +const std::span kZeroLenView(kStunMessageWithZeroLength); +const std::span kExcessLenView(kStunMessageWithExcessLength); +const std::span kSmallLenView(kStunMessageWithSmallLength); + // RFC5769 Test Vectors // Software name (request): "STUN test client" (without quotes) @@ -542,7 +546,7 @@ class StunTest : public ::testing::Test { size_t ReadStunMessageTestCase(StunMessage* msg, const uint8_t* testcase, size_t size) { - ByteBufferReader buf(MakeArrayView(testcase, size)); + ByteBufferReader buf(std::span(testcase, size)); if (msg->Read(&buf)) { // Returns the size the stun message should report itself as being return (size - 20); @@ -1129,7 +1133,7 @@ TEST_F(StunTest, WriteMessageWithAUInt16ListAttribute) { // Test that we fail to read messages with invalid lengths. void CheckFailureToRead(const uint8_t* testcase, size_t length) { StunMessage msg; - ByteBufferReader buf(MakeArrayView(testcase, length)); + ByteBufferReader buf(std::span(testcase, length)); ASSERT_FALSE(msg.Read(&buf)); } @@ -1197,15 +1201,15 @@ TEST_F(StunTest, ValidateMessageIntegrity) { // Again, but with the lengths matching what is claimed in the headers. EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting( reinterpret_cast(kStunMessageWithZeroLength), - kStunHeaderSize + GetBE16(&kStunMessageWithZeroLength[2]), + kStunHeaderSize + GetBE16(kZeroLenView.subspan(2, 2)), kRfc5769SampleMsgPassword)); EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting( reinterpret_cast(kStunMessageWithExcessLength), - kStunHeaderSize + GetBE16(&kStunMessageWithExcessLength[2]), + kStunHeaderSize + GetBE16(kExcessLenView.subspan(2, 2)), kRfc5769SampleMsgPassword)); EXPECT_FALSE(StunMessage::ValidateMessageIntegrityForTesting( reinterpret_cast(kStunMessageWithSmallLength), - kStunHeaderSize + GetBE16(&kStunMessageWithSmallLength[2]), + kStunHeaderSize + GetBE16(kSmallLenView.subspan(2, 2)), kRfc5769SampleMsgPassword)); // Check that a too-short HMAC doesn't cause buffer overflow. @@ -1288,15 +1292,15 @@ TEST_F(StunTest, ValidateMessageIntegrity32) { // Again, but with the lengths matching what is claimed in the headers. EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting( reinterpret_cast(kStunMessageWithZeroLength), - kStunHeaderSize + GetBE16(&kStunMessageWithZeroLength[2]), + kStunHeaderSize + GetBE16(kZeroLenView.subspan(2, 2)), kRfc5769SampleMsgPassword)); EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting( reinterpret_cast(kStunMessageWithExcessLength), - kStunHeaderSize + GetBE16(&kStunMessageWithExcessLength[2]), + kStunHeaderSize + GetBE16(kExcessLenView.subspan(2, 2)), kRfc5769SampleMsgPassword)); EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32ForTesting( reinterpret_cast(kStunMessageWithSmallLength), - kStunHeaderSize + GetBE16(&kStunMessageWithSmallLength[2]), + kStunHeaderSize + GetBE16(kSmallLenView.subspan(2, 2)), kRfc5769SampleMsgPassword)); // Check that a too-short HMAC doesn't cause buffer overflow. diff --git a/api/units/BUILD.gn b/api/units/BUILD.gn index 85d6d1d4e92..0c54b3e14e6 100644 --- a/api/units/BUILD.gn +++ b/api/units/BUILD.gn @@ -19,7 +19,6 @@ rtc_library("data_rate") { ":data_size", ":frequency", ":time_delta", - "..:array_view", "../../rtc_base:checks", "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", @@ -35,7 +34,6 @@ rtc_library("data_size") { ] deps = [ - "..:array_view", "../../rtc_base:checks", "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", @@ -51,7 +49,6 @@ rtc_library("time_delta") { ] deps = [ - "..:array_view", "../../rtc_base:checks", "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", @@ -68,7 +65,6 @@ rtc_library("frequency") { deps = [ ":time_delta", - "..:array_view", "../../rtc_base:checks", "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", @@ -85,7 +81,6 @@ rtc_library("timestamp") { deps = [ ":time_delta", - "..:array_view", "../../rtc_base:checks", "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", @@ -112,6 +107,7 @@ if (rtc_include_tests) { "../../rtc_base:checks", "../../rtc_base:logging", "../../test:test_support", + "//third_party/abseil-cpp/absl/strings", ] } } diff --git a/api/units/data_rate.cc b/api/units/data_rate.cc index 7f99a172edc..5b3838f80f5 100644 --- a/api/units/data_rate.cc +++ b/api/units/data_rate.cc @@ -12,7 +12,6 @@ #include -#include "api/array_view.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { diff --git a/api/units/data_size.cc b/api/units/data_size.cc index abe4906c92c..24bb83c8663 100644 --- a/api/units/data_size.cc +++ b/api/units/data_size.cc @@ -12,7 +12,6 @@ #include -#include "api/array_view.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { diff --git a/api/units/time_delta.cc b/api/units/time_delta.cc index efd5a2a518c..9e1521bb968 100644 --- a/api/units/time_delta.cc +++ b/api/units/time_delta.cc @@ -12,7 +12,6 @@ #include -#include "api/array_view.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { diff --git a/api/units/timestamp.cc b/api/units/timestamp.cc index 38b0d1157db..6dd64ab061f 100644 --- a/api/units/timestamp.cc +++ b/api/units/timestamp.cc @@ -12,7 +12,6 @@ #include -#include "api/array_view.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { diff --git a/api/units/timestamp.h b/api/units/timestamp.h index 0317e9faef4..df9101680f4 100644 --- a/api/units/timestamp.h +++ b/api/units/timestamp.h @@ -123,7 +123,7 @@ class Timestamp final : public rtc_units_impl::UnitBase { private: friend class rtc_units_impl::UnitBase; using UnitBase::UnitBase; - static constexpr bool one_sided = true; + static constexpr bool one_sided = false; }; RTC_EXPORT std::string ToString(Timestamp value); diff --git a/api/units/timestamp_unittest.cc b/api/units/timestamp_unittest.cc index c9254c25f0b..c7ef10d7fc4 100644 --- a/api/units/timestamp_unittest.cc +++ b/api/units/timestamp_unittest.cc @@ -13,6 +13,7 @@ #include #include +#include "absl/strings/str_cat.h" #include "api/units/time_delta.h" #include "test/gtest.h" @@ -101,6 +102,11 @@ TEST(TimestampTest, CanBeInititializedFromLargeInt) { static_cast(kMaxInt) * 1000); } +TEST(TimestampTest, CanBeNegative) { + EXPECT_EQ(Timestamp::Micros(-1).us(), -1); + EXPECT_EQ(absl::StrCat(Timestamp::Millis(-100)), "-100 ms"); +} + TEST(TimestampTest, ConvertsToAndFromDouble) { const int64_t kMicros = 17017; const double kMicrosDouble = kMicros; diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn index 3eca4bacab3..6c6c1b5b941 100644 --- a/api/video/BUILD.gn +++ b/api/video/BUILD.gn @@ -27,7 +27,6 @@ rtc_library("video_rtp_headers") { ] deps = [ - "..:array_view", "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:safe_conversions", @@ -61,7 +60,6 @@ rtc_library("video_frame") { deps = [ ":video_rtp_headers", - "..:array_view", "..:make_ref_counted", "..:ref_count", "..:rtp_packet_info", @@ -130,7 +128,6 @@ rtc_source_set("recordable_encoded_frame") { ":encoded_image", ":video_frame", ":video_rtp_headers", - "..:array_view", "..:make_ref_counted", "..:scoped_refptr", "../units:timestamp", @@ -219,7 +216,6 @@ rtc_library("rtp_video_frame_assembler") { ":encoded_image", ":video_frame_type", ":video_rtp_headers", - "..:array_view", "..:rtp_packet_info", "..:scoped_refptr", "../../modules/rtp_rtcp", @@ -244,7 +240,6 @@ rtc_library("rtp_video_frame_assembler_unittests") { ":rtp_video_frame_assembler", ":video_frame", ":video_frame_type", - "..:array_view", "../../modules/rtp_rtcp", "../../modules/rtp_rtcp:rtp_packetizer_av1_test_helper", "../../modules/rtp_rtcp:rtp_rtcp_format", @@ -266,7 +261,6 @@ if (rtc_use_h265) { ":rtp_video_frame_assembler", ":video_frame", ":video_frame_type", - "..:array_view", "../../modules/rtp_rtcp", "../../modules/rtp_rtcp:rtp_rtcp_format", "../../modules/rtp_rtcp:rtp_video_header", @@ -362,6 +356,7 @@ rtc_source_set("video_stream_encoder") { "../adaptation:resource_adaptation_api", "../units:data_rate", "../video_codecs:video_codecs_api", + "//third_party/abseil-cpp/absl/functional:any_invocable", ] } @@ -375,7 +370,6 @@ rtc_library("video_frame_metadata") { ":video_frame", ":video_frame_type", ":video_rtp_headers", - "..:array_view", "../../modules/video_coding:codec_globals_headers", "../../rtc_base/system:rtc_export", "../transport/rtp:dependency_descriptor", @@ -412,7 +406,6 @@ rtc_library("frame_buffer") { "frame_buffer.h", ] deps = [ - "..:array_view", "../../api:field_trials_view", "../../api/units:timestamp", "../../api/video:encoded_frame", diff --git a/api/video/DEPS b/api/video/DEPS index fd3ad708628..741e79a0e58 100644 --- a/api/video/DEPS +++ b/api/video/DEPS @@ -1,77 +1,77 @@ specific_include_rules = { - "encoded_frame.h" : [ + "encoded_frame\\.h" : [ "+modules/rtp_rtcp/source/rtp_video_header.h", "+modules/video_coding/include/video_codec_interface.h", "+modules/video_coding/include/video_coding_defines.h", "+common_video/frame_instrumentation_data.h", ], - "encoded_image\.h" : [ + "encoded_image\\.h" : [ "+rtc_base/buffer.h", "+rtc_base/ref_count.h", ], - "i010_buffer\.h": [ + "i010_buffer\\.h": [ "+rtc_base/memory/aligned_malloc.h", ], - "i210_buffer\.h": [ + "i210_buffer\\.h": [ "+rtc_base/memory/aligned_malloc.h", ], - "i410_buffer\.h": [ + "i410_buffer\\.h": [ "+rtc_base/memory/aligned_malloc.h", ], - "i420_buffer\.h": [ + "i420_buffer\\.h": [ "+rtc_base/memory/aligned_malloc.h", ], - "i422_buffer\.h": [ + "i422_buffer\\.h": [ "+rtc_base/memory/aligned_malloc.h", ], - "i444_buffer\.h": [ + "i444_buffer\\.h": [ "+rtc_base/memory/aligned_malloc.h", ], - "nv12_buffer\.h": [ + "nv12_buffer\\.h": [ "+rtc_base/memory/aligned_malloc.h", ], - "recordable_encoded_frame\.h": [ + "recordable_encoded_frame\\.h": [ "+rtc_base/ref_count.h", ], - "video_frame\.h": [ + "video_frame\\.h": [ ], - "video_frame_buffer\.h": [ + "video_frame_buffer\\.h": [ "+rtc_base/ref_count.h", ], - "video_frame_metadata\.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_stream_decoder_create\\.cc": [ "+video/video_stream_decoder_impl.h", ], - "video_stream_encoder_create.cc": [ + "video_stream_encoder_create\\.cc": [ "+video/video_stream_encoder.h", ], - "rtp_video_frame_assembler.h": [ + "rtp_video_frame_assembler\\.h": [ "+modules/rtp_rtcp/source/rtp_packet_received.h", ], - "frame_buffer.h": [ + "frame_buffer\\.h": [ "+modules/video_coding/utility/decoded_frames_history.h", ], - "video_frame_matchers\.h": [ + "video_frame_matchers\\.h": [ "+test/gmock.h", ], } diff --git a/api/video/OWNERS b/api/video/OWNERS index a30dab09035..071178a41ed 100644 --- a/api/video/OWNERS +++ b/api/video/OWNERS @@ -1,5 +1,4 @@ brandtr@webrtc.org -magjed@webrtc.org philipel@webrtc.org sprang@webrtc.org diff --git a/api/video/corruption_detection/frame_instrumentation_data.cc b/api/video/corruption_detection/frame_instrumentation_data.cc index ecc076ad4c9..d54aa4b7c13 100644 --- a/api/video/corruption_detection/frame_instrumentation_data.cc +++ b/api/video/corruption_detection/frame_instrumentation_data.cc @@ -10,11 +10,10 @@ #include "api/video/corruption_detection/frame_instrumentation_data.h" +#include #include #include -#include "api/array_view.h" - namespace webrtc { constexpr int kMaxSequenceIndex = (1 << 14) - 1; @@ -61,7 +60,7 @@ bool FrameInstrumentationData::SetChromaErrorThreshold(int threshold) { } bool FrameInstrumentationData::SetSampleValues( - webrtc::ArrayView samples) { + std::span samples) { for (double sample_value : samples) { if (sample_value < 0.0 || sample_value > 255.0) { return false; diff --git a/api/video/corruption_detection/frame_instrumentation_data.h b/api/video/corruption_detection/frame_instrumentation_data.h index 8d4dc41f9cc..923a981539f 100644 --- a/api/video/corruption_detection/frame_instrumentation_data.h +++ b/api/video/corruption_detection/frame_instrumentation_data.h @@ -11,10 +11,9 @@ #ifndef API_VIDEO_CORRUPTION_DETECTION_FRAME_INSTRUMENTATION_DATA_H_ #define API_VIDEO_CORRUPTION_DETECTION_FRAME_INSTRUMENTATION_DATA_H_ +#include #include -#include "api/array_view.h" - namespace webrtc { class FrameInstrumentationData { @@ -26,14 +25,14 @@ class FrameInstrumentationData { double std_dev() const { return std_dev_; } int luma_error_threshold() const { return luma_error_threshold_; } int chroma_error_threshold() const { return chroma_error_threshold_; } - ArrayView sample_values() const { return sample_values_; } + std::span sample_values() const { return sample_values_; } bool SetSequenceIndex(int index); void set_droppable(bool droppable) { droppable_ = droppable; } bool SetStdDev(double std_dev); bool SetLumaErrorThreshold(int threshold); bool SetChromaErrorThreshold(int threshold); - bool SetSampleValues(ArrayView samples); + bool SetSampleValues(std::span samples); bool SetSampleValues(std::vector&& samples); // Convenience methods.. diff --git a/api/video/corruption_detection/frame_instrumentation_data_reader.cc b/api/video/corruption_detection/frame_instrumentation_data_reader.cc index 760feadbbde..b025d25f307 100644 --- a/api/video/corruption_detection/frame_instrumentation_data_reader.cc +++ b/api/video/corruption_detection/frame_instrumentation_data_reader.cc @@ -11,8 +11,8 @@ #include "api/video/corruption_detection/frame_instrumentation_data_reader.h" #include +#include -#include "api/array_view.h" #include "api/transport/rtp/corruption_detection_message.h" #include "api/video/corruption_detection/frame_instrumentation_data.h" #include "rtc_base/logging.h" @@ -45,7 +45,7 @@ FrameInstrumentationDataReader::ParseMessage( // The sequence index field of the message refers to the halton sequence index // for the first sample in the message. In order to figure out the next // expected sequence index we must increment it by the number of samples. - ArrayView sample_values = message.sample_values(); + std::span sample_values = message.sample_values(); last_seen_sequence_index_ = data.sequence_index() + sample_values.size(); if (!sample_values.empty()) { diff --git a/api/video/corruption_detection/frame_instrumentation_evaluation.cc b/api/video/corruption_detection/frame_instrumentation_evaluation.cc index a3e271d4555..d1beb7cac59 100644 --- a/api/video/corruption_detection/frame_instrumentation_evaluation.cc +++ b/api/video/corruption_detection/frame_instrumentation_evaluation.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/video/corruption_detection/frame_instrumentation_data.h" #include "api/video/video_content_type.h" #include "api/video/video_frame.h" @@ -28,8 +28,8 @@ namespace webrtc { namespace { std::vector ConvertSampleValuesToFilteredSamples( - ArrayView values, - ArrayView samples) { + std::span values, + std::span samples) { RTC_CHECK_EQ(values.size(), samples.size()) << "values and samples must have the same size"; std::vector filtered_samples; diff --git a/api/video/corruption_detection/frame_instrumentation_generator.h b/api/video/corruption_detection/frame_instrumentation_generator.h index f4ce90546eb..d5833d3579d 100644 --- a/api/video/corruption_detection/frame_instrumentation_generator.h +++ b/api/video/corruption_detection/frame_instrumentation_generator.h @@ -11,6 +11,7 @@ #ifndef API_VIDEO_CORRUPTION_DETECTION_FRAME_INSTRUMENTATION_GENERATOR_H_ #define API_VIDEO_CORRUPTION_DETECTION_FRAME_INSTRUMENTATION_GENERATOR_H_ +#include #include #include @@ -38,6 +39,8 @@ class FrameInstrumentationGenerator { virtual void OnCapturedFrame(VideoFrame frame) = 0; virtual std::optional OnEncodedImage( const EncodedImage& encoded_image) = 0; + // Indicates that all encoding operations for this frame has been completed. + virtual void OnFrameReleased(uint32_t rtp_timestamp) = 0; // Returns `std::nullopt` if there is no context for the given layer. // The layer id is the simulcast id or SVC spatial layer id depending on diff --git a/api/video/encoded_image.h b/api/video/encoded_image.h index de88f454ced..a934347c29f 100644 --- a/api/video/encoded_image.h +++ b/api/video/encoded_image.h @@ -52,6 +52,8 @@ class EncodedImageBufferInterface : public RefCountInterface { // Basic implementation of EncodedImageBufferInterface. class RTC_EXPORT EncodedImageBuffer : public EncodedImageBufferInterface { public: + using iterator = Buffer::iterator; + static scoped_refptr Create() { return Create(0); } static scoped_refptr Create(size_t size); static scoped_refptr Create(const uint8_t* data, @@ -63,6 +65,9 @@ class RTC_EXPORT EncodedImageBuffer : public EncodedImageBufferInterface { size_t size() const override; void Realloc(size_t t); + iterator begin() { return buffer_.begin(); } + iterator end() { return buffer_.end(); } + protected: explicit EncodedImageBuffer(size_t size); EncodedImageBuffer(const uint8_t* data, size_t size); @@ -224,9 +229,22 @@ class RTC_EXPORT EncodedImage { is_steady_state_refresh_frame_ = refresh_frame; } + // TODO: webrtc:472264461 - Switch downstream projects to frame_type() and + // remove this getter. VideoFrameType FrameType() const { return _frameType; } + // TODO: webrtc:472264461 - Switch downstream projects to set_frame_type() and + // remove this setter. void SetFrameType(VideoFrameType frame_type) { _frameType = frame_type; } + + VideoFrameType frame_type() const { return _frameType; } + void set_frame_type(VideoFrameType frame_type) { _frameType = frame_type; } + + bool IsKey() const { return _frameType == VideoFrameType::kVideoFrameKey; } + bool IsDelta() const { + return _frameType == VideoFrameType::kVideoFrameDelta; + } + VideoContentType contentType() const { return content_type_; } VideoRotation rotation() const { return rotation_; } diff --git a/api/video/frame_buffer.cc b/api/video/frame_buffer.cc index 78426db4f63..fcff81f41cc 100644 --- a/api/video/frame_buffer.cc +++ b/api/video/frame_buffer.cc @@ -16,11 +16,11 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/video/encoded_frame.h" #include "rtc_base/logging.h" @@ -46,7 +46,7 @@ bool ValidReferences(const EncodedFrame& frame) { // Since FrameBuffer::FrameInfo is private it can't be used in the function // signature, hence the FrameIteratorT type. template -ArrayView GetReferences(const FrameIteratorT& it) { +std::span GetReferences(const FrameIteratorT& it) { return {it->second.encoded_frame->references, std::min(it->second.encoded_frame->num_references, EncodedFrame::kMaxFrameReferences)}; diff --git a/api/video/rtp_video_frame_assembler.cc b/api/video/rtp_video_frame_assembler.cc index e7bc37aee5c..821e5292687 100644 --- a/api/video/rtp_video_frame_assembler.cc +++ b/api/video/rtp_video_frame_assembler.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/rtp_packet_infos.h" #include "api/scoped_refptr.h" #include "api/transport/rtp/dependency_descriptor.h" @@ -161,7 +161,7 @@ RtpVideoFrameAssembler::Impl::RtpFrameVector RtpVideoFrameAssembler::Impl::AssembleFrames( video_coding::PacketBuffer::InsertResult insert_result) { video_coding::PacketBuffer::Packet* first_packet = nullptr; - std::vector> payloads; + std::vector> payloads; RtpFrameVector result; for (auto& packet : insert_result.packets) { diff --git a/api/video/rtp_video_frame_assembler_unittests.cc b/api/video/rtp_video_frame_assembler_unittests.cc index 826ae8ae3b5..e5c0dba2ea5 100644 --- a/api/video/rtp_video_frame_assembler_unittests.cc +++ b/api/video/rtp_video_frame_assembler_unittests.cc @@ -11,9 +11,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "api/video/encoded_frame.h" #include "api/video/rtp_video_frame_assembler.h" @@ -54,7 +54,7 @@ class PacketBuilder { return *this; } - PacketBuilder& WithPayload(ArrayView payload) { + PacketBuilder& WithPayload(std::span payload) { payload_.assign(payload.begin(), payload.end()); return *this; } @@ -131,11 +131,11 @@ void AppendFrames(RtpVideoFrameAssembler::FrameVector from, std::make_move_iterator(from.end())); } -ArrayView References(const std::unique_ptr& frame) { - return MakeArrayView(frame->references, frame->num_references); +std::span References(const std::unique_ptr& frame) { + return std::span(frame->references, frame->num_references); } -ArrayView Payload(const std::unique_ptr& frame) { +std::span Payload(const std::unique_ptr& frame) { return *frame->GetEncodedData(); } diff --git a/api/video/rtp_video_frame_h265_assembler_unittests.cc b/api/video/rtp_video_frame_h265_assembler_unittests.cc index aff5c8a74d1..db823076d48 100644 --- a/api/video/rtp_video_frame_h265_assembler_unittests.cc +++ b/api/video/rtp_video_frame_h265_assembler_unittests.cc @@ -11,10 +11,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/video/encoded_frame.h" #include "api/video/rtp_video_frame_assembler.h" #include "api/video/video_frame_type.h" @@ -44,7 +44,7 @@ class PacketBuilder { return *this; } - PacketBuilder& WithPayload(ArrayView payload) { + PacketBuilder& WithPayload(std::span payload) { payload_.assign(payload.begin(), payload.end()); return *this; } @@ -87,11 +87,11 @@ void AppendFrames(RtpVideoFrameAssembler::FrameVector&& from, std::make_move_iterator(from.end())); } -ArrayView References(const std::unique_ptr& frame) { - return MakeArrayView(frame->references, frame->num_references); +std::span References(const std::unique_ptr& frame) { + return std::span(frame->references, frame->num_references); } -ArrayView Payload(const std::unique_ptr& frame) { +std::span Payload(const std::unique_ptr& frame) { return *frame->GetEncodedData(); } diff --git a/api/video/video_bitrate_allocation.cc b/api/video/video_bitrate_allocation.cc index 6e477776212..98735cb0964 100644 --- a/api/video/video_bitrate_allocation.cc +++ b/api/video/video_bitrate_allocation.cc @@ -119,7 +119,7 @@ VideoBitrateAllocation::GetSimulcastAllocations() const { std::optional layer_bitrate; if (IsSpatialLayerUsed(si)) { layer_bitrate = VideoBitrateAllocation(); - for (int tl = 0; tl < kMaxTemporalStreams; ++tl) { + for (size_t tl = 0; tl < kMaxTemporalStreams; ++tl) { if (HasBitrate(si, tl)) layer_bitrate->SetBitrate(0, tl, GetBitrate(si, tl)); } diff --git a/api/video/video_codec_constants.h b/api/video/video_codec_constants.h index 5859f9b4cff..0f849688790 100644 --- a/api/video/video_codec_constants.h +++ b/api/video/video_codec_constants.h @@ -11,13 +11,15 @@ #ifndef API_VIDEO_VIDEO_CODEC_CONSTANTS_H_ #define API_VIDEO_VIDEO_CODEC_CONSTANTS_H_ +#include + namespace webrtc { -enum : int { kMaxEncoderBuffers = 8 }; -enum : int { kMaxSimulcastStreams = 3 }; -enum : int { kMaxSpatialLayers = 5 }; -enum : int { kMaxTemporalStreams = 4 }; -enum : int { kMaxPreferredPixelFormats = 5 }; +inline constexpr size_t kMaxEncoderBuffers = 8; +inline constexpr size_t kMaxSimulcastStreams = 3; +inline constexpr size_t kMaxSpatialLayers = 5; +inline constexpr size_t kMaxTemporalStreams = 4; +inline constexpr size_t kMaxPreferredPixelFormats = 5; } // namespace webrtc diff --git a/api/video/video_frame.cc b/api/video/video_frame.cc index 85816ed9302..ec97aff57b2 100644 --- a/api/video/video_frame.cc +++ b/api/video/video_frame.cc @@ -19,6 +19,7 @@ #include "api/scoped_refptr.h" #include "api/units/timestamp.h" #include "api/video/color_space.h" +#include "api/video/video_content_type.h" #include "api/video/video_frame_buffer.h" #include "api/video/video_rotation.h" #include "rtc_base/checks.h" @@ -174,7 +175,8 @@ VideoFrame VideoFrame::Builder::build() { return VideoFrame(id_, video_frame_buffer_, timestamp_us_, presentation_timestamp_, reference_time_, timestamp_rtp_, ntp_time_ms_, rotation_, color_space_, render_parameters_, - update_rect_, packet_infos_, is_repeat_frame_); + update_rect_, packet_infos_, is_repeat_frame_, + content_type_); } VideoFrame::Builder& VideoFrame::Builder::set_video_frame_buffer( @@ -270,6 +272,12 @@ VideoFrame::Builder& VideoFrame::Builder::set_is_repeat_frame( return *this; } +VideoFrame::Builder& VideoFrame::Builder::set_content_type( + VideoContentType content_type) { + content_type_ = content_type; + return *this; +} + VideoFrame::VideoFrame(const scoped_refptr& buffer, VideoRotation rotation, int64_t timestamp_us) diff --git a/api/video/video_frame.h b/api/video/video_frame.h index ef054ba4078..268d5e7e0be 100644 --- a/api/video/video_frame.h +++ b/api/video/video_frame.h @@ -21,6 +21,7 @@ #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "api/video/color_space.h" +#include "api/video/video_content_type.h" #include "api/video/video_frame_buffer.h" #include "api/video/video_rotation.h" #include "rtc_base/checks.h" @@ -125,6 +126,7 @@ class RTC_EXPORT VideoFrame { Builder& set_update_rect(const std::optional& update_rect); Builder& set_packet_infos(RtpPacketInfos packet_infos); Builder& set_is_repeat_frame(bool is_repeat_frame); + Builder& set_content_type(VideoContentType content_type); private: uint16_t id_ = kNotSetId; @@ -140,6 +142,7 @@ class RTC_EXPORT VideoFrame { std::optional update_rect_; RtpPacketInfos packet_infos_; bool is_repeat_frame_ = false; + std::optional content_type_; }; // To be deprecated. Migrate all use to Builder. @@ -291,6 +294,11 @@ class RTC_EXPORT VideoFrame { is_repeat_frame_ = is_repeat_frame; } + std::optional content_type() const { return content_type_; } + void set_content_type(std::optional content_type) { + content_type_ = content_type; + } + private: VideoFrame(uint16_t id, const scoped_refptr& buffer, @@ -304,7 +312,8 @@ class RTC_EXPORT VideoFrame { const RenderParameters& render_parameters, const std::optional& update_rect, RtpPacketInfos packet_infos, - bool is_repeat_frame) + bool is_repeat_frame, + std::optional content_type) : id_(id), video_frame_buffer_(buffer), timestamp_rtp_(timestamp_rtp), @@ -317,7 +326,8 @@ class RTC_EXPORT VideoFrame { render_parameters_(render_parameters), update_rect_(update_rect), packet_infos_(std::move(packet_infos)), - is_repeat_frame_(is_repeat_frame) {} + is_repeat_frame_(is_repeat_frame), + content_type_(content_type) {} uint16_t id_; // An opaque reference counted handle that stores the pixel data. @@ -355,6 +365,9 @@ class RTC_EXPORT VideoFrame { // in cases where a capturer is using a variable frame rate and stops // producing frames when nothing has changed. bool is_repeat_frame_; + // The content type of the video frame. This represents the mode in which + // the video frame was encoded by the remote peer (if signaled). + std::optional content_type_; }; } // namespace webrtc diff --git a/api/video/video_frame_buffer.cc b/api/video/video_frame_buffer.cc index 7b45658791c..131aa3e6474 100644 --- a/api/video/video_frame_buffer.cc +++ b/api/video/video_frame_buffer.cc @@ -11,9 +11,9 @@ #include "api/video/video_frame_buffer.h" #include +#include #include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/video/i420_buffer.h" #include "api/video/i422_buffer.h" @@ -89,7 +89,7 @@ const NV12BufferInterface* VideoFrameBuffer::GetNV12() const { } scoped_refptr VideoFrameBuffer::GetMappedFrameBuffer( - ArrayView /* types */) { + std::span /* types */) { RTC_CHECK(type() == Type::kNative); return nullptr; } diff --git a/api/video/video_frame_buffer.h b/api/video/video_frame_buffer.h index b160ab8bb70..5545239458e 100644 --- a/api/video/video_frame_buffer.h +++ b/api/video/video_frame_buffer.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/ref_count.h" #include "api/scoped_refptr.h" #include "rtc_base/system/rtc_export.h" @@ -129,7 +129,7 @@ class RTC_EXPORT VideoFrameBuffer : public RefCountInterface { // frame type is not supported, mapping is not possible, or if the kNative // frame has not implemented this method. Only callable if type() is kNative. virtual scoped_refptr GetMappedFrameBuffer( - ArrayView types); + std::span types); // For logging: returns a textual representation of the storage. virtual std::string storage_representation() const; diff --git a/api/video/video_frame_metadata.cc b/api/video/video_frame_metadata.cc index f265fc335a6..4441b98fac0 100644 --- a/api/video/video_frame_metadata.cc +++ b/api/video/video_frame_metadata.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include -#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" @@ -90,23 +90,23 @@ void VideoFrameMetadata::SetTemporalIndex(int temporal_index) { temporal_index_ = temporal_index; } -ArrayView VideoFrameMetadata::GetFrameDependencies() const { +std::span VideoFrameMetadata::GetFrameDependencies() const { return frame_dependencies_; } void VideoFrameMetadata::SetFrameDependencies( - ArrayView frame_dependencies) { + std::span frame_dependencies) { frame_dependencies_.assign(frame_dependencies.begin(), frame_dependencies.end()); } -ArrayView +std::span VideoFrameMetadata::GetDecodeTargetIndications() const { return decode_target_indications_; } void VideoFrameMetadata::SetDecodeTargetIndications( - ArrayView decode_target_indications) { + std::span decode_target_indications) { decode_target_indications_.assign(decode_target_indications.begin(), decode_target_indications.end()); } diff --git a/api/video/video_frame_metadata.h b/api/video/video_frame_metadata.h index 342d69b4f9b..1dfd1bb62d1 100644 --- a/api/video/video_frame_metadata.h +++ b/api/video/video_frame_metadata.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/container/inlined_vector.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" @@ -67,12 +67,12 @@ class RTC_EXPORT VideoFrameMetadata { int GetTemporalIndex() const; void SetTemporalIndex(int temporal_index); - ArrayView GetFrameDependencies() const; - void SetFrameDependencies(ArrayView frame_dependencies); + std::span GetFrameDependencies() const; + void SetFrameDependencies(std::span frame_dependencies); - ArrayView GetDecodeTargetIndications() const; + std::span GetDecodeTargetIndications() const; void SetDecodeTargetIndications( - ArrayView decode_target_indications); + std::span decode_target_indications); bool GetIsLastFrameInPicture() const; void SetIsLastFrameInPicture(bool is_last_frame_in_picture); diff --git a/api/video/video_stream_encoder_settings.h b/api/video/video_stream_encoder_settings.h index d02bcbe739d..5936dc786c4 100644 --- a/api/video/video_stream_encoder_settings.h +++ b/api/video/video_stream_encoder_settings.h @@ -11,6 +11,9 @@ #ifndef API_VIDEO_VIDEO_STREAM_ENCODER_SETTINGS_H_ #define API_VIDEO_VIDEO_STREAM_ENCODER_SETTINGS_H_ +#include + +#include "absl/functional/any_invocable.h" #include "api/video/video_bitrate_allocator_factory.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_encoder.h" @@ -18,18 +21,13 @@ namespace webrtc { -class EncoderSwitchRequestCallback { - public: - virtual ~EncoderSwitchRequestCallback() {} - - // Requests switch to next negotiated encoder. - virtual void RequestEncoderFallback() = 0; - - // Requests switch to a specific encoder. If the encoder is not available and - // `allow_default_fallback` is `true` the default fallback is invoked. - virtual void RequestEncoderSwitch(const SdpVideoFormat& format, - bool allow_default_fallback) = 0; -}; +// Requests switch to a specific encoder. If `format` is nullopt, a fallback +// to the next negotiated encoder is requested. If the requested encoder is +// not available and `allow_default_fallback` is `true`, the default fallback +// is invoked. +using EncoderSwitchRequestCallback = + absl::AnyInvocable format, + bool allow_default_fallback)>; struct VideoStreamEncoderSettings { explicit VideoStreamEncoderSettings( @@ -43,9 +41,6 @@ struct VideoStreamEncoderSettings { // Ownership stays with WebrtcVideoEngine (delegated from PeerConnection). VideoEncoderFactory* encoder_factory = nullptr; - // Requests the WebRtcVideoChannel to perform a codec switch. - EncoderSwitchRequestCallback* encoder_switch_request_callback = nullptr; - // Ownership stays with WebrtcVideoEngine (delegated from PeerConnection). VideoBitrateAllocatorFactory* bitrate_allocator_factory = nullptr; diff --git a/api/video/video_timing.cc b/api/video/video_timing.cc index a5b7f78dc97..bc9ee808ad5 100644 --- a/api/video/video_timing.cc +++ b/api/video/video_timing.cc @@ -14,7 +14,6 @@ #include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn index 7106dc197d6..1ec3dff92c5 100644 --- a/api/video_codecs/BUILD.gn +++ b/api/video_codecs/BUILD.gn @@ -81,7 +81,6 @@ rtc_library("video_codecs_api") { ":scalability_mode", "..:fec_controller_api", "..:scoped_refptr", - "../../api:array_view", "../../api:rtp_parameters", "../../media:media_constants", "../../modules/video_coding:codec_globals_headers", @@ -122,7 +121,7 @@ rtc_library("encoder_speed_controller_factory") { rtc_source_set("bitstream_parser_api") { visibility = [ "*" ] sources = [ "bitstream_parser.h" ] - deps = [ "..:array_view" ] + deps = [] } rtc_library("builtin_video_decoder_factory") { @@ -169,7 +168,6 @@ rtc_source_set("video_encoder_factory_template") { deps = [ ":scalability_mode", ":video_codecs_api", - "..:array_view", "../../modules/video_coding/svc:scalability_mode_util", "../environment", "//third_party/abseil-cpp/absl/algorithm:container", @@ -240,7 +238,6 @@ rtc_source_set("video_decoder_factory_template") { deps = [ ":video_codecs_api", - "..:array_view", "../environment", "//third_party/abseil-cpp/absl/algorithm:container", ] @@ -301,7 +298,6 @@ rtc_source_set("video_encoder_interface") { deps = [ ":video_encoding_general", - "..:array_view", "..:scoped_refptr", "../../api/units:data_rate", "../../api/units:data_size", @@ -338,7 +334,6 @@ rtc_library("simple_encoder_wrapper") { deps = [ ":video_encoder_factory_interface", ":video_encoder_interface", - "..:array_view", "..:scoped_refptr", "../../api/units:data_rate", "../../api/video_codecs:scalability_mode", @@ -385,23 +380,12 @@ rtc_library("libaom_av1_encoder_factory") { ] deps = [ - ":video_codecs_api", ":video_encoder_factory_interface", ":video_encoder_interface", ":video_encoding_general", - "..:array_view", - "..:scoped_refptr", - "../../api/units:time_delta", - "../../rtc_base:checks", - "../../rtc_base:logging", + "../../api/video:video_frame", + "../../modules/video_coding/codecs/av1:libaom_av1_encoder_v2", "../../rtc_base:rtc_numerics", - "../../rtc_base:stringutils", - "../units:data_rate", - "../units:data_size", - "../video:resolution", - "../video:video_frame", - "//third_party/abseil-cpp/absl/algorithm:container", - "//third_party/abseil-cpp/absl/cleanup", "//third_party/libaom", ] } @@ -416,7 +400,6 @@ rtc_library("libaom_av1_encoder_factory_test") { ":video_encoder_factory_interface", ":video_encoder_interface", ":video_encoding_general", - "..:array_view", "..:scoped_refptr", "../../api/video:video_frame", "../../api/video_codecs:video_codecs_api", diff --git a/api/video_codecs/OWNERS b/api/video_codecs/OWNERS index f73b04f8295..6766cdf9b9c 100644 --- a/api/video_codecs/OWNERS +++ b/api/video_codecs/OWNERS @@ -1,4 +1,3 @@ -magjed@webrtc.org sprang@webrtc.org brandtr@webrtc.org philipel@webrtc.org diff --git a/api/video_codecs/bitstream_parser.h b/api/video_codecs/bitstream_parser.h index a7dd998f5e7..85587c61dd4 100644 --- a/api/video_codecs/bitstream_parser.h +++ b/api/video_codecs/bitstream_parser.h @@ -15,8 +15,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -26,7 +25,7 @@ class BitstreamParser { virtual ~BitstreamParser() = default; // Parse an additional chunk of the bitstream. - virtual void ParseBitstream(ArrayView bitstream) = 0; + virtual void ParseBitstream(std::span bitstream) = 0; // Get the last extracted QP value from the parsed bitstream. If no QP // value could be parsed, returns std::nullopt. diff --git a/api/video_codecs/encoder_speed_controller.h b/api/video_codecs/encoder_speed_controller.h index 87ceaef31aa..37c7d922aeb 100644 --- a/api/video_codecs/encoder_speed_controller.h +++ b/api/video_codecs/encoder_speed_controller.h @@ -17,6 +17,7 @@ #include #include "api/units/time_delta.h" +#include "api/units/timestamp.h" namespace webrtc { @@ -40,6 +41,28 @@ class EncoderSpeedController { kNoneReference // A frame not used as reference sub subsequent frames. }; struct Config { + struct PsnrProbingSettings { + enum class Mode { + // Sample one base layer frame every `sampling_interval`, and sample + // both alternatives when doing PSNR probing. + kRegularBaseLayerSampling, + // Only perform sampling of a base-layer frame when a PSNR probe is + // needed. + kOnlyWhenProbing, + }; + Mode mode; + + // Detfault time between frames that should be sampled for PSNR. + TimeDelta sampling_interval; + // The expected ratio of base-layer to non-base-layer frames. E.g. for + // L1T3 this will be 0.25; + double average_base_layer_ratio = 1.0; + }; + // The PSNR settings to used. If not set, PSNR gain levels must not be + // present in the speed levels. Do not populate if the encoder does not + // support calculating PSNR. + std::optional psnr_probing_settings; + // Represents an assignable speed level, with specific speeds for one or // more temporal layers. struct SpeedLevel { @@ -53,6 +76,19 @@ class EncoderSpeedController { // Don't use this speed level if the average QP is lower than `min_qp`. std::optional min_qp; + // Minimum PSNR gain required to go from the previous speed level to this + // one, or nullopt if no PSNR calculation is required. This value must + // not be set unless the encoder is capable of encoding a frame twice. + struct PsnrComparison { + // The baseline (faster) speed to compare the new `base_layer_speed` + // speed with. + int baseline_speed; + // The min PSNR gain required to move to this speed level, where the + // PSNR for `alternate_base_layer_speed` is expected to be lower than + // the PSNR for `base_layer_speed`. + double psnr_threshold; + }; + std::optional min_psnr_gain; }; // Ordered vector of speed levels, start with the slowest speed (lower // effort) and the increasing the average speed for each entry. @@ -69,6 +105,10 @@ class EncoderSpeedController { // True iff the frame is a repeat of the previous frame (e.g. the frames // used during quality convergence of a variable fps screenshare feed). bool is_repeat_frame; + // The capture time of the frame. + // TODO: webrtc:443906251 - Remove default value once downstream usage + // is updated. + Timestamp timestamp = Timestamp::MinusInfinity(); }; // Output from the controller, indicates which speed the encoder should be @@ -76,6 +116,13 @@ class EncoderSpeedController { struct EncodeSettings { // Speed the encoder should use for this frame. int speed; + // If set, the encoder should encode this frame twice. FIRST with a speed of + // `baseline_comparison_speed` and SECONDLY at speed `speed`. The two + // results should then both be provided in `OnEncodedFrame()`. + std::optional baseline_comparison_speed; + // If true, the encoder should calculate the PSNR for this frame - including + // the second encoding if `baseline_comparison_speed` is set. + bool calculate_psnr; }; // Data the controller should be fed with after a frame has been encoded, @@ -87,6 +134,8 @@ class EncoderSpeedController { TimeDelta encode_time; // The _average_ frame QP of the encoded frame. int qp; + // If set, the PSNR of the reconstructed frame vs the original raw frame. + std::optional psnr; // The frame encoding info - same as what was originally given as argument // to `GetEncodingSettings()`. FrameEncodingInfo frame_info; @@ -109,8 +158,19 @@ class EncoderSpeedController { // thereafter be configured with requested settings. virtual EncodeSettings GetEncodeSettings(FrameEncodingInfo frame_info) = 0; - // Should be called after each frame has completed encoding. - virtual void OnEncodedFrame(EncodeResults results) = 0; + // TODO: webrtc:443906251 - Remove once downstream usage is gone. + [[deprecated( + "Use OnEncodedFrame(EncodeResults, std::optional)")]] + virtual void OnEncodedFrame(EncodeResults results) { + return OnEncodedFrame(results, std::nullopt); + } + + // Should be called after each frame has completed encoding. If a baseline + // comparison speed was set in the `EncodeSettings`, the `baseline_results` + // parameter should be set with the results corresponding to those settings. + virtual void OnEncodedFrame( + EncodeResults results, + std::optional baseline_results) = 0; }; } // namespace webrtc diff --git a/api/video_codecs/h264_profile_level_id.cc b/api/video_codecs/h264_profile_level_id.cc index fc8555a0f2c..ad305988530 100644 --- a/api/video_codecs/h264_profile_level_id.cc +++ b/api/video_codecs/h264_profile_level_id.cc @@ -320,4 +320,46 @@ bool H264IsSameProfileAndLevel(const CodecParameterMap& params1, profile_level_id->level == other_profile_level_id->level; } +bool H264IsProfileSubsetOf(const CodecParameterMap& subset, + const CodecParameterMap& superset) { + const std::optional subset_profile_level_id = + ParseSdpForH264ProfileLevelId(subset); + const std::optional superset_profile_level_id = + ParseSdpForH264ProfileLevelId(superset); + + if (!subset_profile_level_id || !superset_profile_level_id) { + return false; + } + + const H264Profile subset_profile = subset_profile_level_id->profile; + const H264Profile superset_profile = superset_profile_level_id->profile; + + if (subset_profile == superset_profile) { + return true; + } + + // Evaluates whether the `subset` profile is a valid subset of the `superset` + // profile according to ITU-T H.264 Annex A.2. + switch (superset_profile) { + case H264Profile::kProfileConstrainedBaseline: + return false; + case H264Profile::kProfileBaseline: + case H264Profile::kProfileMain: + return subset_profile == H264Profile::kProfileConstrainedBaseline; + case H264Profile::kProfileConstrainedHigh: + return subset_profile == H264Profile::kProfileConstrainedBaseline; + case H264Profile::kProfileHigh: + return subset_profile == H264Profile::kProfileConstrainedBaseline || + subset_profile == H264Profile::kProfileMain || + subset_profile == H264Profile::kProfileConstrainedHigh; + case H264Profile::kProfilePredictiveHigh444: + return subset_profile == H264Profile::kProfileConstrainedBaseline || + subset_profile == H264Profile::kProfileMain || + subset_profile == H264Profile::kProfileConstrainedHigh || + subset_profile == H264Profile::kProfileHigh; + default: + return false; + } +} + } // namespace webrtc diff --git a/api/video_codecs/h264_profile_level_id.h b/api/video_codecs/h264_profile_level_id.h index 1b21f18834b..8e77c55deeb 100644 --- a/api/video_codecs/h264_profile_level_id.h +++ b/api/video_codecs/h264_profile_level_id.h @@ -91,6 +91,11 @@ RTC_EXPORT bool H264IsSameProfile(const CodecParameterMap& params1, RTC_EXPORT bool H264IsSameProfileAndLevel(const CodecParameterMap& params1, const CodecParameterMap& params2); +// Returns true if the profile defined in `subset` is a valid subset of the +// profile defined in `superset` according to ITU-T H.264 Annex A.2. +RTC_EXPORT bool H264IsProfileSubsetOf(const CodecParameterMap& subset, + const CodecParameterMap& superset); + } // namespace webrtc #endif // API_VIDEO_CODECS_H264_PROFILE_LEVEL_ID_H_ diff --git a/api/video_codecs/libaom_av1_encoder_factory.cc b/api/video_codecs/libaom_av1_encoder_factory.cc index 207b0b142c7..2fa1e278619 100644 --- a/api/video_codecs/libaom_av1_encoder_factory.cc +++ b/api/video_codecs/libaom_av1_encoder_factory.cc @@ -11,810 +11,19 @@ #include "api/video_codecs/libaom_av1_encoder_factory.h" #include -#include -#include -#include #include #include -#include #include -#include -#include -#include -#include "absl/algorithm/container.h" -#include "absl/cleanup/cleanup.h" -#include "api/array_view.h" -#include "api/scoped_refptr.h" -#include "api/units/data_rate.h" -#include "api/units/data_size.h" -#include "api/units/time_delta.h" -#include "api/video/resolution.h" #include "api/video/video_frame_buffer.h" -#include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_encoder_factory_interface.h" #include "api/video_codecs/video_encoder_interface.h" #include "api/video_codecs/video_encoding_general.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" +#include "modules/video_coding/codecs/av1/libaom_av1_encoder_v2.h" #include "rtc_base/numerics/rational.h" -#include "rtc_base/strings/string_builder.h" -#include "third_party/libaom/source/libaom/aom/aom_codec.h" -#include "third_party/libaom/source/libaom/aom/aom_encoder.h" -#include "third_party/libaom/source/libaom/aom/aom_image.h" -#include "third_party/libaom/source/libaom/aom/aomcx.h" - -#define SET_OR_RETURN(param_id, param_value) \ - do { \ - if (!SetEncoderControlParameters(&ctx_, param_id, param_value)) { \ - return; \ - } \ - } while (0) - -#define SET_OR_RETURN_FALSE(param_id, param_value) \ - do { \ - if (!SetEncoderControlParameters(&ctx_, param_id, param_value)) { \ - return false; \ - } \ - } while (0) namespace webrtc { -using FrameEncodeSettings = VideoEncoderInterface::FrameEncodeSettings; -using Cbr = FrameEncodeSettings::Cbr; -using Cqp = FrameEncodeSettings::Cqp; -using aom_img_ptr = std::unique_ptr; - -namespace { -// MaxQp defined here: -// http://google3/third_party/libaom/git_root/av1/av1_cx_iface.c;l=3510;rcl=527067478 -constexpr int kMaxQp = 63; -constexpr int kNumBuffers = 8; -constexpr int kMaxReferences = 3; -constexpr int kMinEffortLevel = -2; -constexpr int kMaxEffortLevel = 2; -constexpr int kMaxSpatialLayersWtf = 4; -constexpr int kMaxTemporalLayers = 4; -constexpr int kRtpTicksPerSecond = 90000; -constexpr std::array kSupportedInputFormats = { - VideoFrameBuffer::Type::kI420, VideoFrameBuffer::Type::kNV12}; - -constexpr std::array kSupportedScalingFactors = { - {{.numerator = 8, .denominator = 1}, - {.numerator = 4, .denominator = 1}, - {.numerator = 2, .denominator = 1}, - {.numerator = 1, .denominator = 1}, - {.numerator = 1, .denominator = 2}, - {.numerator = 1, .denominator = 4}, - {.numerator = 1, .denominator = 8}}}; - -std::optional GetScalingFactor(const Resolution& from, - const Resolution& to) { - auto it = absl::c_find_if(kSupportedScalingFactors, [&](const Rational& r) { - return (from.width * r.numerator / r.denominator) == to.width && - (from.height * r.numerator / r.denominator) == to.height; - }); - - if (it != kSupportedScalingFactors.end()) { - return *it; - } - - return {}; -} - -class LibaomAv1Encoder : public VideoEncoderInterface { - public: - LibaomAv1Encoder() = default; - ~LibaomAv1Encoder() override; - - bool InitEncode( - const VideoEncoderFactoryInterface::StaticEncoderSettings& settings, - const std::map& encoder_specific_settings); - - void Encode(scoped_refptr frame_buffer, - const TemporalUnitSettings& tu_settings, - std::vector frame_settings) override; - - private: - aom_img_ptr image_to_encode_ = aom_img_ptr(nullptr, aom_img_free); - aom_codec_ctx_t ctx_; - aom_codec_enc_cfg_t cfg_; - - std::optional current_content_type_; - std::array, kMaxSpatialLayersWtf> current_effort_level_; - int max_number_of_threads_; - std::array, 8> last_resolution_in_buffer_; -}; - -template -bool SetEncoderControlParameters(aom_codec_ctx_t* ctx, int id, T value) { - aom_codec_err_t error_code = aom_codec_control(ctx, id, value); - if (error_code != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "aom_codec_control returned " << error_code - << " with id: " << id << "."; - } - return error_code == AOM_CODEC_OK; -} - -LibaomAv1Encoder::~LibaomAv1Encoder() { - aom_codec_destroy(&ctx_); -} - -bool LibaomAv1Encoder::InitEncode( - const VideoEncoderFactoryInterface::StaticEncoderSettings& settings, - const std::map& encoder_specific_settings) { - if (!encoder_specific_settings.empty()) { - RTC_LOG(LS_ERROR) - << "libaom av1 encoder accepts no encoder specific settings"; - return false; - } - - if (aom_codec_err_t ret = aom_codec_enc_config_default( - aom_codec_av1_cx(), &cfg_, AOM_USAGE_REALTIME); - ret != AOM_CODEC_OK) { - RTC_LOG(LS_ERROR) << "aom_codec_enc_config_default returned " << ret; - return false; - } - - max_number_of_threads_ = settings.max_number_of_threads; - - // The encode resolution is set dynamically for each call to `Encode`, but for - // `aom_codec_enc_init` to not fail we set it here as well. - cfg_.g_w = settings.max_encode_dimensions.width; - cfg_.g_h = settings.max_encode_dimensions.height; - cfg_.g_timebase.num = 1; - // TD: does 90khz timebase make sense, use microseconds instead maybe? - cfg_.g_timebase.den = kRtpTicksPerSecond; - cfg_.g_input_bit_depth = settings.encoding_format.bit_depth; - cfg_.kf_mode = AOM_KF_DISABLED; - // TD: rc_undershoot_pct and rc_overshoot_pct should probably be removed. - cfg_.rc_undershoot_pct = 50; - cfg_.rc_overshoot_pct = 50; - auto* cbr = - std::get_if( - &settings.rc_mode); - cfg_.rc_buf_initial_sz = cbr ? cbr->target_buffer_size.ms() : 600; - cfg_.rc_buf_optimal_sz = cbr ? cbr->target_buffer_size.ms() : 600; - cfg_.rc_buf_sz = cbr ? cbr->max_buffer_size.ms() : 1000; - cfg_.g_usage = AOM_USAGE_REALTIME; - cfg_.g_pass = AOM_RC_ONE_PASS; - cfg_.g_lag_in_frames = 0; - cfg_.g_error_resilient = 0; - cfg_.rc_end_usage = cbr ? AOM_CBR : AOM_Q; - - if (aom_codec_err_t ret = - aom_codec_enc_init(&ctx_, aom_codec_av1_cx(), &cfg_, /*flags=*/0); - ret != AOM_CODEC_OK) { - RTC_LOG(LS_ERROR) << "aom_codec_enc_init returned " << ret; - return false; - } - - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_CDEF, 1); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_TPL_MODEL, 0); - SET_OR_RETURN_FALSE(AV1E_SET_DELTAQ_MODE, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_ORDER_HINT, 0); - SET_OR_RETURN_FALSE(AV1E_SET_AQ_MODE, 3); - SET_OR_RETURN_FALSE(AOME_SET_MAX_INTRA_BITRATE_PCT, 300); - SET_OR_RETURN_FALSE(AV1E_SET_COEFF_COST_UPD_FREQ, 3); - SET_OR_RETURN_FALSE(AV1E_SET_MODE_COST_UPD_FREQ, 3); - SET_OR_RETURN_FALSE(AV1E_SET_MV_COST_UPD_FREQ, 3); - SET_OR_RETURN_FALSE(AV1E_SET_ROW_MT, 1); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_OBMC, 0); - SET_OR_RETURN_FALSE(AV1E_SET_NOISE_SENSITIVITY, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_WARPED_MOTION, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_GLOBAL_MOTION, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_REF_FRAME_MVS, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_CFL_INTRA, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_SMOOTH_INTRA, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_ANGLE_DELTA, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_FILTER_INTRA, 0); - SET_OR_RETURN_FALSE(AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1); - SET_OR_RETURN_FALSE(AV1E_SET_DISABLE_TRELLIS_QUANT, 1); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_DIST_WTD_COMP, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_DIFF_WTD_COMP, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_DUAL_FILTER, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTERINTRA_COMP, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTRABC, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_MASKED_COMP, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_PAETH_INTRA, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_QM, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_RECT_PARTITIONS, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_RESTORATION, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0); - SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_TX64, 0); - SET_OR_RETURN_FALSE(AV1E_SET_MAX_REFERENCE_FRAMES, 3); - - return true; -} - -struct ThreadTilesAndSuperblockSizeInfo { - int num_threads; - int exp_tile_rows; - int exp_tile_colums; - aom_superblock_size_t superblock_size; -}; - -ThreadTilesAndSuperblockSizeInfo GetThreadingTilesAndSuperblockSize( - int width, - int height, - int max_number_of_threads) { - ThreadTilesAndSuperblockSizeInfo res; - const int num_pixels = width * height; - if (num_pixels >= 1920 * 1080 && max_number_of_threads > 8) { - res.num_threads = 8; - res.exp_tile_rows = 2; - res.exp_tile_colums = 1; - } else if (num_pixels >= 640 * 360 && max_number_of_threads > 4) { - res.num_threads = 4; - res.exp_tile_rows = 1; - res.exp_tile_colums = 1; - } else if (num_pixels >= 320 * 180 && max_number_of_threads > 2) { - res.num_threads = 2; - res.exp_tile_rows = 1; - res.exp_tile_colums = 0; - } else { - res.num_threads = 1; - res.exp_tile_rows = 0; - res.exp_tile_colums = 0; - } - - if (res.num_threads > 4 && num_pixels >= 960 * 540) { - res.superblock_size = AOM_SUPERBLOCK_SIZE_64X64; - } else { - res.superblock_size = AOM_SUPERBLOCK_SIZE_DYNAMIC; - } - - RTC_LOG(LS_WARNING) << __FUNCTION__ << " res.num_threads=" << res.num_threads - << " res.exp_tile_rows=" << res.exp_tile_rows - << " res.exp_tile_colums=" << res.exp_tile_colums - << " res.superblock_size=" << res.superblock_size; - - return res; -} - -bool ValidateEncodeParams( - const VideoFrameBuffer& /* frame_buffer */, - const VideoEncoderInterface::TemporalUnitSettings& /* tu_settings */, - const std::vector& - frame_settings, - const std::array, 8>& last_resolution_in_buffer, - aom_rc_mode rc_mode) { - if (frame_settings.empty()) { - RTC_LOG(LS_ERROR) << "No frame settings provided."; - return false; - } - - auto in_range = [](int low, int high, int val) { - return low <= val && val < high; - }; - - for (size_t i = 0; i < frame_settings.size(); ++i) { - const VideoEncoderInterface::FrameEncodeSettings& settings = - frame_settings[i]; - - if (!settings.frame_output) { - RTC_LOG(LS_ERROR) << "No frame output provided."; - return false; - } - - if (!in_range(kMinEffortLevel, kMaxEffortLevel + 1, - settings.effort_level)) { - RTC_LOG(LS_ERROR) << "Unsupported effort level " << settings.effort_level; - return false; - } - - if (!in_range(0, kMaxSpatialLayersWtf, settings.spatial_id)) { - RTC_LOG(LS_ERROR) << "invalid spatial id " << settings.spatial_id; - return false; - } - - if (!in_range(0, kMaxTemporalLayers, settings.temporal_id)) { - RTC_LOG(LS_ERROR) << "invalid temporal id " << settings.temporal_id; - return false; - } - - if ((settings.frame_type == FrameType::kKeyframe || - settings.frame_type == FrameType::kStartFrame) && - !settings.reference_buffers.empty()) { - RTC_LOG(LS_ERROR) << "Reference buffers can not be used for keyframes."; - return false; - } - - if ((settings.frame_type == FrameType::kKeyframe || - settings.frame_type == FrameType::kStartFrame) && - !settings.update_buffer) { - RTC_LOG(LS_ERROR) - << "Buffer to update must be specified for keyframe/startframe"; - return false; - } - - if (settings.update_buffer && - !in_range(0, kNumBuffers, *settings.update_buffer)) { - RTC_LOG(LS_ERROR) << "Invalid update buffer id."; - return false; - } - - if (settings.reference_buffers.size() > kMaxReferences) { - RTC_LOG(LS_ERROR) << "Too many referenced buffers."; - return false; - } - - for (size_t j = 0; j < settings.reference_buffers.size(); ++j) { - if (!in_range(0, kNumBuffers, settings.reference_buffers[j])) { - RTC_LOG(LS_ERROR) << "Invalid reference buffer id."; - return false; - } - - // Figure out which frame resolution a certain buffer will hold when the - // frame described by `settings` is encoded. - std::optional referenced_resolution; - bool keyframe_on_previous_layer = false; - - // Will some other frame in this temporal unit update the buffer? - for (size_t k = 0; k < i; ++k) { - if (frame_settings[k].frame_type == FrameType::kKeyframe) { - keyframe_on_previous_layer = true; - referenced_resolution.reset(); - } - if (frame_settings[k].update_buffer == settings.reference_buffers[j]) { - referenced_resolution = frame_settings[k].resolution; - } - } - - // Not updated by another frame in the temporal unit, what is the - // resolution of the last frame stored into that buffer? - if (!referenced_resolution && !keyframe_on_previous_layer) { - referenced_resolution = - last_resolution_in_buffer[settings.reference_buffers[j]]; - } - - if (!referenced_resolution) { - RTC_LOG(LS_ERROR) << "Referenced buffer holds no frame."; - return false; - } - - if (!GetScalingFactor(*referenced_resolution, settings.resolution)) { - RTC_LOG(LS_ERROR) - << "Required resolution scaling factor not supported."; - return false; - } - - for (size_t l = i + 1; l < settings.reference_buffers.size(); ++l) { - if (settings.reference_buffers[i] == settings.reference_buffers[l]) { - RTC_LOG(LS_ERROR) << "Duplicate reference buffer specified."; - return false; - } - } - } - - if ((rc_mode == AOM_CBR && - std::holds_alternative(settings.rate_options)) || - (rc_mode == AOM_Q && - std::holds_alternative(settings.rate_options))) { - RTC_LOG(LS_ERROR) << "Invalid rate options, encoder configured with " - << (rc_mode == AOM_CBR ? "AOM_CBR" : "AOM_Q"); - return false; - } - - for (size_t j = i + 1; j < frame_settings.size(); ++j) { - if (settings.spatial_id >= frame_settings[j].spatial_id) { - RTC_LOG(LS_ERROR) << "Frame spatial id specified out of order."; - return false; - } - } - } - - return true; -} - -void PrepareInputImage(const VideoFrameBuffer& input_buffer, - aom_img_ptr& out_aom_image) { - aom_img_fmt_t input_format; - switch (input_buffer.type()) { - case VideoFrameBuffer::Type::kI420: - input_format = AOM_IMG_FMT_I420; - break; - case VideoFrameBuffer::Type::kNV12: - input_format = AOM_IMG_FMT_NV12; - break; - default: - RTC_CHECK_NOTREACHED(); - return; - } - - if (!out_aom_image || out_aom_image->fmt != input_format || - static_cast(out_aom_image->w) != input_buffer.width() || - static_cast(out_aom_image->h) != input_buffer.height()) { - out_aom_image.reset( - aom_img_wrap(/*img=*/nullptr, input_format, input_buffer.width(), - input_buffer.height(), /*align=*/1, /*img_data=*/nullptr)); - - RTC_LOG(LS_WARNING) << __FUNCTION__ << " input_format=" << input_format - << " input_buffer.width()=" << input_buffer.width() - << " input_buffer.height()=" << input_buffer.height() - << " w=" << out_aom_image->w - << " h=" << out_aom_image->h - << " d_w=" << out_aom_image->d_w - << " d_h=" << out_aom_image->d_h - << " r_w=" << out_aom_image->r_w - << " r_h=" << out_aom_image->r_h; - } - - if (input_format == AOM_IMG_FMT_I420) { - const I420BufferInterface* i420_buffer = input_buffer.GetI420(); - RTC_DCHECK(i420_buffer); - out_aom_image->planes[AOM_PLANE_Y] = - const_cast(i420_buffer->DataY()); - out_aom_image->planes[AOM_PLANE_U] = - const_cast(i420_buffer->DataU()); - out_aom_image->planes[AOM_PLANE_V] = - const_cast(i420_buffer->DataV()); - out_aom_image->stride[AOM_PLANE_Y] = i420_buffer->StrideY(); - out_aom_image->stride[AOM_PLANE_U] = i420_buffer->StrideU(); - out_aom_image->stride[AOM_PLANE_V] = i420_buffer->StrideV(); - } else { - const NV12BufferInterface* nv12_buffer = input_buffer.GetNV12(); - RTC_DCHECK(nv12_buffer); - out_aom_image->planes[AOM_PLANE_Y] = - const_cast(nv12_buffer->DataY()); - out_aom_image->planes[AOM_PLANE_U] = - const_cast(nv12_buffer->DataUV()); - out_aom_image->planes[AOM_PLANE_V] = nullptr; - out_aom_image->stride[AOM_PLANE_Y] = nv12_buffer->StrideY(); - out_aom_image->stride[AOM_PLANE_U] = nv12_buffer->StrideUV(); - out_aom_image->stride[AOM_PLANE_V] = 0; - } -} - -aom_svc_ref_frame_config_t GetSvcRefFrameConfig( - const VideoEncoderInterface::FrameEncodeSettings& settings) { - // Buffer alias to use for each position. In particular when there are two - // buffers being used, prefer to alias them as LAST and GOLDEN, since the AV1 - // bitstream format has dedicated fields for them. See last_frame_idx and - // golden_frame_idx in the av1 spec - // https://aomediacodec.github.io/av1-spec/av1-spec.pdf. - - // Libaom is also compiled for RTC, which limits the number of references to - // at most three, and they must be aliased as LAST, GOLDEN and ALTREF. Also - // note that libaom favors LAST the most, and GOLDEN second most, so buffers - // should be specified in order of how useful they are for prediction. Libaom - // could be updated to make LAST, GOLDEN and ALTREF equivalent, but that is - // not a priority for now. All aliases can be used to update buffers. - // TD: Automatically select LAST, GOLDEN and ALTREF depending on previous - // buffer usage. - static constexpr int kPreferedAlias[] = {0, // LAST - 3, // GOLDEN - 6, // ALTREF - 1, 2, 4, 5}; - - aom_svc_ref_frame_config_t ref_frame_config = {}; - - int alias_index = 0; - if (!settings.reference_buffers.empty()) { - for (size_t i = 0; i < settings.reference_buffers.size(); ++i) { - ref_frame_config.ref_idx[kPreferedAlias[alias_index]] = - settings.reference_buffers[i]; - ref_frame_config.reference[kPreferedAlias[alias_index]] = 1; - alias_index++; - } - - // Delta frames must not alias unused buffers, and since start frames only - // update some buffers it is not safe to leave unused aliases to simply - // point to buffer 0. - for (size_t i = settings.reference_buffers.size(); - i < std::size(ref_frame_config.ref_idx); ++i) { - ref_frame_config.ref_idx[kPreferedAlias[i]] = - settings.reference_buffers.back(); - } - } - - if (settings.update_buffer) { - if (!absl::c_linear_search(settings.reference_buffers, - *settings.update_buffer)) { - ref_frame_config.ref_idx[kPreferedAlias[alias_index]] = - *settings.update_buffer; - alias_index++; - } - ref_frame_config.refresh[*settings.update_buffer] = 1; - } - - char buf[256]; - SimpleStringBuilder sb(buf); - sb << " spatial_id=" << settings.spatial_id; - sb << " ref_idx=[ "; - for (auto r : ref_frame_config.ref_idx) { - sb << r << " "; - } - sb << "] reference=[ "; - for (auto r : ref_frame_config.reference) { - sb << r << " "; - } - sb << "] refresh=[ "; - for (auto r : ref_frame_config.refresh) { - sb << r << " "; - } - sb << "]"; - - RTC_LOG(LS_WARNING) << __FUNCTION__ << sb.str(); - - return ref_frame_config; -} - -aom_svc_params_t GetSvcParams( - const VideoFrameBuffer& frame_buffer, - const std::vector& - frame_settings) { - aom_svc_params_t svc_params = {}; - svc_params.number_spatial_layers = frame_settings.back().spatial_id + 1; - svc_params.number_temporal_layers = kMaxTemporalLayers; - - // TD: What about svc_params.framerate_factor? - // If `framerate_factors` are left at 0 then configured bitrate values will - // not be picked up by libaom. - for (int tid = 0; tid < svc_params.number_temporal_layers; ++tid) { - svc_params.framerate_factor[tid] = 1; - } - - // If the scaling factor is left at zero for unused layers a division by zero - // will happen inside libaom, default all layers to one. - for (int sid = 0; sid < svc_params.number_spatial_layers; ++sid) { - svc_params.scaling_factor_num[sid] = 1; - svc_params.scaling_factor_den[sid] = 1; - } - - for (const VideoEncoderInterface::FrameEncodeSettings& settings : - frame_settings) { - std::optional scaling_factor = GetScalingFactor( - {.width = frame_buffer.width(), .height = frame_buffer.height()}, - settings.resolution); - RTC_CHECK(scaling_factor); - svc_params.scaling_factor_num[settings.spatial_id] = - scaling_factor->numerator; - svc_params.scaling_factor_den[settings.spatial_id] = - scaling_factor->denominator; - - const int flat_layer_id = - settings.spatial_id * svc_params.number_temporal_layers + - settings.temporal_id; - - RTC_LOG(LS_WARNING) << __FUNCTION__ << " flat_layer_id=" << flat_layer_id - << " num=" - << svc_params.scaling_factor_num[settings.spatial_id] - << " den=" - << svc_params.scaling_factor_den[settings.spatial_id]; - - std::visit( - [&](auto&& arg) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - // Libaom calculates the total bitrate across all spatial layers by - // summing the bitrate of the last temporal layer in each spatial - // layer. This means the bitrate for the top temporal layer always - // has to be set even if that temporal layer is not being encoded. - const int last_temporal_layer_in_spatial_layer_id = - settings.spatial_id * svc_params.number_temporal_layers + - (kMaxTemporalLayers - 1); - svc_params - .layer_target_bitrate[last_temporal_layer_in_spatial_layer_id] = - arg.target_bitrate.kbps(); - - svc_params.layer_target_bitrate[flat_layer_id] = - arg.target_bitrate.kbps(); - // When libaom is configured with `AOM_CBR` it will still limit QP - // to stay between `min_quantizers` and `max_quantizers'. Set - // `max_quantizers` to max QP to avoid the encoder overshooting. - svc_params.max_quantizers[flat_layer_id] = kMaxQp; - svc_params.min_quantizers[flat_layer_id] = 0; - } else if constexpr (std::is_same_v) { - // When libaom is configured with `AOM_Q` it will still look at the - // `layer_target_bitrate` to determine whether the layer is disabled - // or not. Set `layer_target_bitrate` to 1 so that libaom knows the - // layer is active. - svc_params.layer_target_bitrate[flat_layer_id] = 1; - svc_params.max_quantizers[flat_layer_id] = arg.target_qp; - svc_params.min_quantizers[flat_layer_id] = arg.target_qp; - RTC_LOG(LS_WARNING) << __FUNCTION__ << " svc_params.qp[" - << flat_layer_id << "]=" << arg.target_qp; - // TD: Does libaom look at both max and min? Shouldn't it just be - // one of them - } - }, - settings.rate_options); - } - - char buf[512]; - SimpleStringBuilder sb(buf); - sb << "GetSvcParams" << " layer bitrates kbps"; - for (int s = 0; s < svc_params.number_spatial_layers; ++s) { - sb << " S" << s << "=[ "; - for (int t = 0; t < svc_params.number_temporal_layers; ++t) { - int id = s * svc_params.number_temporal_layers + t; - sb << "T" << t << "=" << svc_params.layer_target_bitrate[id] << " "; - } - sb << "]"; - } - - RTC_LOG(LS_WARNING) << sb.str(); - - return svc_params; -} - -void LibaomAv1Encoder::Encode(scoped_refptr frame_buffer, - const TemporalUnitSettings& tu_settings, - std::vector frame_settings) { - absl::Cleanup on_return = [&] { - // On return call `EncodeComplete` with EncodingError result unless they - // were already called with an EncodedData result. - for (FrameEncodeSettings& settings : frame_settings) { - if (settings.frame_output) { - settings.frame_output->EncodeComplete(EncodingError()); - } - } - }; - - if (!ValidateEncodeParams(*frame_buffer, tu_settings, frame_settings, - last_resolution_in_buffer_, cfg_.rc_end_usage)) { - return; - } - - if (current_content_type_ != tu_settings.content_hint) { - if (tu_settings.content_hint == VideoCodecMode::kScreensharing) { - // TD: Set speed 11? - SET_OR_RETURN(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN); - SET_OR_RETURN(AV1E_SET_ENABLE_PALETTE, 1); - } else { - SET_OR_RETURN(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_DEFAULT); - SET_OR_RETURN(AV1E_SET_ENABLE_PALETTE, 0); - } - current_content_type_ = tu_settings.content_hint; - } - - if (cfg_.rc_end_usage == AOM_CBR) { - DataRate accum_rate = DataRate::Zero(); - for (const FrameEncodeSettings& settings : frame_settings) { - accum_rate += std::get(settings.rate_options).target_bitrate; - } - cfg_.rc_target_bitrate = accum_rate.kbps(); - RTC_LOG(LS_WARNING) << __FUNCTION__ - << " cfg_.rc_target_bitrate=" << cfg_.rc_target_bitrate; - } - - if (static_cast(cfg_.g_w) != frame_buffer->width() || - static_cast(cfg_.g_h) != frame_buffer->height()) { - RTC_LOG(LS_WARNING) << __FUNCTION__ << " resolution changed from " - << cfg_.g_w << "x" << cfg_.g_h << " to " - << frame_buffer->width() << "x" - << frame_buffer->height(); - ThreadTilesAndSuperblockSizeInfo ttsbi = GetThreadingTilesAndSuperblockSize( - frame_buffer->width(), frame_buffer->height(), max_number_of_threads_); - SET_OR_RETURN(AV1E_SET_SUPERBLOCK_SIZE, ttsbi.superblock_size); - SET_OR_RETURN(AV1E_SET_TILE_ROWS, ttsbi.exp_tile_rows); - SET_OR_RETURN(AV1E_SET_TILE_COLUMNS, ttsbi.exp_tile_colums); - cfg_.g_threads = ttsbi.num_threads; - cfg_.g_w = frame_buffer->width(); - cfg_.g_h = frame_buffer->height(); - } - - PrepareInputImage(*frame_buffer, image_to_encode_); - - // The bitrates caluclated internally in libaom when `AV1E_SET_SVC_PARAMS` is - // called depends on the currently configured `cfg_.rc_target_bitrate`. If the - // total target bitrate is not updated first a division by zero could happen. - if (aom_codec_err_t ret = aom_codec_enc_config_set(&ctx_, &cfg_); - ret != AOM_CODEC_OK) { - RTC_LOG(LS_ERROR) << "aom_codec_enc_config_set returned " << ret; - return; - } - aom_svc_params_t svc_params = GetSvcParams(*frame_buffer, frame_settings); - SET_OR_RETURN(AV1E_SET_SVC_PARAMS, &svc_params); - - // The libaom AV1 encoder requires that `aom_codec_encode` is called for - // every spatial layer, even if no frame should be encoded for that layer. - std::array - settings_for_spatial_id; - settings_for_spatial_id.fill(nullptr); - FrameEncodeSettings settings_for_unused_layer; - for (FrameEncodeSettings& settings : frame_settings) { - settings_for_spatial_id[settings.spatial_id] = &settings; - } - - for (int sid = frame_settings[0].spatial_id; - sid < svc_params.number_spatial_layers; ++sid) { - const bool layer_enabled = settings_for_spatial_id[sid] != nullptr; - FrameEncodeSettings& settings = layer_enabled - ? *settings_for_spatial_id[sid] - : settings_for_unused_layer; - - aom_svc_layer_id_t layer_id = { - .spatial_layer_id = sid, - .temporal_layer_id = settings.temporal_id, - }; - SET_OR_RETURN(AV1E_SET_SVC_LAYER_ID, &layer_id); - aom_svc_ref_frame_config_t ref_config = GetSvcRefFrameConfig(settings); - SET_OR_RETURN(AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_config); - - // TD: Duration can't be zero, what does it matter when the layer is - // not being encoded? - TimeDelta duration = TimeDelta::Millis(1); - if (layer_enabled) { - if (const Cbr* cbr = std::get_if(&settings.rate_options)) { - duration = cbr->duration; - } else { - // TD: What should duration be when Cqp is used? - duration = TimeDelta::Millis(1); - } - - if (settings.effort_level != current_effort_level_[settings.spatial_id]) { - // For RTC we use speed level 6 to 10, with 8 being the default. Note - // that low effort means higher speed. - SET_OR_RETURN(AOME_SET_CPUUSED, 8 - settings.effort_level); - current_effort_level_[settings.spatial_id] = settings.effort_level; - } - } - - RTC_LOG(LS_WARNING) - << __FUNCTION__ << " timestamp=" - << (tu_settings.presentation_timestamp.ms() * kRtpTicksPerSecond / 1000) - << " duration=" << (duration.ms() * kRtpTicksPerSecond / 1000) - << " type=" - << (settings.frame_type == FrameType::kKeyframe ? "key" : "delta"); - aom_codec_err_t ret = aom_codec_encode( - &ctx_, &*image_to_encode_, tu_settings.presentation_timestamp.ms() * 90, - duration.ms() * 90, - settings.frame_type == FrameType::kKeyframe ? AOM_EFLAG_FORCE_KF : 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "aom_codec_encode returned " << ret; - return; - } - - if (!layer_enabled) { - continue; - } - - if (settings.frame_type == FrameType::kKeyframe) { - last_resolution_in_buffer_ = {}; - } - - if (settings.update_buffer) { - last_resolution_in_buffer_[*settings.update_buffer] = settings.resolution; - } - - EncodedData result; - aom_codec_iter_t iter = nullptr; - bool bitstream_produced = false; - while (const aom_codec_cx_pkt_t* pkt = - aom_codec_get_cx_data(&ctx_, &iter)) { - if (pkt->kind == AOM_CODEC_CX_FRAME_PKT && pkt->data.frame.sz > 0) { - SET_OR_RETURN(AOME_GET_LAST_QUANTIZER_64, &result.encoded_qp); - result.frame_type = pkt->data.frame.flags & AOM_FRAME_IS_KEY - ? FrameType::kKeyframe - : FrameType::kDeltaFrame; - ArrayView output_buffer = - settings.frame_output->GetBitstreamOutputBuffer( - DataSize::Bytes(pkt->data.frame.sz)); - if (output_buffer.size() != pkt->data.frame.sz) { - return; - } - memcpy(output_buffer.data(), pkt->data.frame.buf, pkt->data.frame.sz); - bitstream_produced = true; - break; - } - } - - if (!bitstream_produced) { - return; - } else { - RTC_CHECK(settings.frame_output); - settings.frame_output->EncodeComplete(result); - // To avoid invoking any callback more than once. - settings.frame_output = nullptr; - } - } -} -} // namespace - std::string LibaomAv1EncoderFactory::CodecName() const { return "AV1"; } @@ -832,6 +41,26 @@ std::map LibaomAv1EncoderFactory::CodecSpecifics() // The formater and cpplint have conflicting ideas. VideoEncoderFactoryInterface::Capabilities LibaomAv1EncoderFactory::GetEncoderCapabilities() const { + constexpr int kMaxQp = 63; + constexpr int kNumBuffers = 8; + constexpr int kMaxReferences = 3; + constexpr int kMinEffortLevel = -2; + constexpr int kMaxEffortLevel = 2; + constexpr int kMaxSpatialLayersLimit = 4; + constexpr int kMaxTemporalLayers = 4; + + constexpr std::array kSupportedInputFormats = { + VideoFrameBuffer::Type::kI420, VideoFrameBuffer::Type::kNV12}; + + constexpr std::array kSupportedScalingFactors = { + {{.numerator = 8, .denominator = 1}, + {.numerator = 4, .denominator = 1}, + {.numerator = 2, .denominator = 1}, + {.numerator = 1, .denominator = 1}, + {.numerator = 1, .denominator = 2}, + {.numerator = 1, .denominator = 4}, + {.numerator = 1, .denominator = 8}}}; + return { .prediction_constraints = { .num_buffers = kNumBuffers, @@ -839,7 +68,7 @@ LibaomAv1EncoderFactory::GetEncoderCapabilities() const { .max_temporal_layers = kMaxTemporalLayers, .buffer_space_type = VideoEncoderFactoryInterface::Capabilities:: PredictionConstraints::BufferSpaceType::kSingleKeyframe, - .max_spatial_layers = kMaxSpatialLayersWtf, + .max_spatial_layers = kMaxSpatialLayersLimit, .scaling_factors = {kSupportedScalingFactors.begin(), kSupportedScalingFactors.end()}, .supported_frame_types = {FrameType::kKeyframe, @@ -868,7 +97,7 @@ LibaomAv1EncoderFactory::GetEncoderCapabilities() const { std::unique_ptr LibaomAv1EncoderFactory::CreateEncoder( const StaticEncoderSettings& settings, const std::map& encoder_specific_settings) { - auto encoder = std::make_unique(); + auto encoder = std::make_unique(); if (!encoder->InitEncode(settings, encoder_specific_settings)) { return nullptr; } diff --git a/api/video_codecs/libaom_av1_encoder_factory_test.cc b/api/video_codecs/libaom_av1_encoder_factory_test.cc index 27189fc9e71..8d58b5184da 100644 --- a/api/video_codecs/libaom_av1_encoder_factory_test.cc +++ b/api/video_codecs/libaom_av1_encoder_factory_test.cc @@ -15,12 +15,12 @@ #include #include #include +#include #include #include #include #include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" @@ -98,7 +98,7 @@ class Av1Decoder : public DecodedImageCallback { return 0; } - VideoFrame Decode(ArrayView bitstream_data) { + VideoFrame Decode(std::span bitstream_data) { EncodedImage img; img.SetEncodedData(EncodedImageBuffer::Create(bitstream_data.data(), bitstream_data.size())); @@ -127,7 +127,7 @@ class FrameEncoderSettingsBuilder { FrameEncoderSettingsBuilder() { class IgnoredOutput : public VideoEncoderInterface::FrameOutput { public: - ArrayView GetBitstreamOutputBuffer(DataSize size) override { + std::span GetBitstreamOutputBuffer(DataSize size) override { unread_.resize(size.bytes()); return unread_; } @@ -203,9 +203,9 @@ class FrameEncoderSettingsBuilder { private: struct FrameOut : public VideoEncoderInterface::FrameOutput { explicit FrameOut(EncOut& e) : eo(e) {} - ArrayView GetBitstreamOutputBuffer(DataSize size) override { + std::span GetBitstreamOutputBuffer(DataSize size) override { eo.bitstream.resize(size.bytes()); - return ArrayView(eo.bitstream); + return std::span(eo.bitstream); } void EncodeComplete(const EncodeResult& encode_result) override { eo.res = encode_result; diff --git a/api/video_codecs/sdp_video_format.cc b/api/video_codecs/sdp_video_format.cc index dcc966e6aff..57cdbec1d10 100644 --- a/api/video_codecs/sdp_video_format.cc +++ b/api/video_codecs/sdp_video_format.cc @@ -11,11 +11,11 @@ #include "api/video_codecs/sdp_video_format.h" #include +#include #include #include "absl/container/inlined_vector.h" #include "absl/strings/match.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "api/video/video_codec_type.h" #include "api/video_codecs/av1_profile.h" @@ -172,7 +172,7 @@ bool SdpVideoFormat::IsSameCodec(const SdpVideoFormat& other) const { } bool SdpVideoFormat::IsCodecInList( - ArrayView formats) const { + std::span formats) const { for (const auto& format : formats) { if (IsSameCodec(format)) { return true; @@ -245,7 +245,7 @@ const SdpVideoFormat SdpVideoFormat::AV1Profile1() { } std::optional FuzzyMatchSdpVideoFormat( - ArrayView supported_formats, + std::span supported_formats, const SdpVideoFormat& format) { std::optional res; int best_parameter_match = 0; diff --git a/api/video_codecs/sdp_video_format.h b/api/video_codecs/sdp_video_format.h index 494d1df856a..37f03aad5c9 100644 --- a/api/video_codecs/sdp_video_format.h +++ b/api/video_codecs/sdp_video_format.h @@ -13,10 +13,10 @@ #include #include +#include #include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "api/video_codecs/scalability_mode.h" #include "rtc_base/system/rtc_export.h" @@ -55,7 +55,7 @@ struct RTC_EXPORT SdpVideoFormat { // specific parameters. Please note that two SdpVideoFormats can represent the // same codec even though not all parameters are the same. bool IsSameCodec(const SdpVideoFormat& other) const; - bool IsCodecInList(ArrayView formats) const; + bool IsCodecInList(std::span formats) const; std::string ToString() const; @@ -92,7 +92,7 @@ struct RTC_EXPORT SdpVideoFormat { // anymore. Until we stop misusing SdpVideoFormats provide this convenience // function to perform fuzzy matching. std::optional FuzzyMatchSdpVideoFormat( - ArrayView supported_formats, + std::span supported_formats, const SdpVideoFormat& format); } // namespace webrtc diff --git a/api/video_codecs/simple_encoder_wrapper.cc b/api/video_codecs/simple_encoder_wrapper.cc index 90fb4ce7409..cb5ddced44f 100644 --- a/api/video_codecs/simple_encoder_wrapper.cc +++ b/api/video_codecs/simple_encoder_wrapper.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/units/data_size.h" #include "api/units/frequency.h" @@ -192,7 +192,7 @@ void SimpleEncoderWrapper::Encode(scoped_refptr frame_buffer, } struct FrameOut : public VideoEncoderInterface::FrameOutput { - ArrayView GetBitstreamOutputBuffer(DataSize size) override { + std::span GetBitstreamOutputBuffer(DataSize size) override { bitstream.resize(size.bytes()); return bitstream; } diff --git a/api/video_codecs/test/BUILD.gn b/api/video_codecs/test/BUILD.gn index d905633eee8..f1861c7f993 100644 --- a/api/video_codecs/test/BUILD.gn +++ b/api/video_codecs/test/BUILD.gn @@ -41,13 +41,13 @@ if (rtc_include_tests) { "../../../modules/video_coding:webrtc_vp8", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_tests_utils", + "../../../test:create_test_environment", "../../../test:create_test_field_trials", "../../../test:fake_video_codecs", "../../../test:test_support", "../../../test:video_test_common", "../../environment", "../../environment:environment_factory", - "../../units:timestamp", "../../video:encoded_image", "../../video:resolution", "../../video:video_bitrate_allocation", @@ -56,6 +56,7 @@ if (rtc_include_tests) { "../../video:video_frame_type", "../../video:video_rtp_headers", "//testing/gtest", + "//third_party/abseil-cpp/absl/strings:string_view", ] } diff --git a/api/video_codecs/test/h264_profile_level_id_unittest.cc b/api/video_codecs/test/h264_profile_level_id_unittest.cc index 044765b44e1..68768f45e58 100644 --- a/api/video_codecs/test/h264_profile_level_id_unittest.cc +++ b/api/video_codecs/test/h264_profile_level_id_unittest.cc @@ -168,4 +168,57 @@ TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdInvalid) { EXPECT_FALSE(ParseSdpForH264ProfileLevelId(params)); } +TEST(H264ProfileLevelId, TestIsProfileSubsetOf) { + CodecParameterMap params_cb; + params_cb["profile-level-id"] = "42e01f"; + CodecParameterMap params_baseline; + params_baseline["profile-level-id"] = "42001f"; + CodecParameterMap params_main; + params_main["profile-level-id"] = "4d001f"; + CodecParameterMap params_constrained_high; + params_constrained_high["profile-level-id"] = "640c1f"; + CodecParameterMap params_high; + params_high["profile-level-id"] = "64001f"; + CodecParameterMap params_high444; + params_high444["profile-level-id"] = "f4001f"; + + // Same profile. + EXPECT_TRUE(H264IsProfileSubsetOf(params_cb, params_cb)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_baseline, params_baseline)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_main, params_main)); + + // Constrained Baseline is a subset of almost everything. + EXPECT_TRUE(H264IsProfileSubsetOf(params_cb, params_baseline)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_cb, params_main)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_cb, params_constrained_high)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_cb, params_high)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_cb, params_high444)); + + // Main is a subset of High and High 4:4:4 profiles. + EXPECT_TRUE(H264IsProfileSubsetOf(params_main, params_high)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_main, params_high444)); + + // Constrained High is a subset of High and High 4:4:4. + EXPECT_TRUE(H264IsProfileSubsetOf(params_constrained_high, params_high)); + EXPECT_TRUE(H264IsProfileSubsetOf(params_constrained_high, params_high444)); + + // High is a subset of High 4:4:4. + EXPECT_TRUE(H264IsProfileSubsetOf(params_high, params_high444)); + + // Standard Baseline is not a subset of Main/High. + EXPECT_FALSE(H264IsProfileSubsetOf(params_baseline, params_main)); + EXPECT_FALSE(H264IsProfileSubsetOf(params_baseline, params_constrained_high)); + EXPECT_FALSE(H264IsProfileSubsetOf(params_baseline, params_high)); + EXPECT_FALSE(H264IsProfileSubsetOf(params_baseline, params_high444)); + + // Main allows interlaced video, Constrained High does not. + EXPECT_FALSE(H264IsProfileSubsetOf(params_main, params_constrained_high)); + + // Higher profiles are not subsets of lower profiles. + EXPECT_FALSE(H264IsProfileSubsetOf(params_baseline, params_cb)); + EXPECT_FALSE(H264IsProfileSubsetOf(params_main, params_cb)); + EXPECT_FALSE(H264IsProfileSubsetOf(params_high, params_main)); + EXPECT_FALSE(H264IsProfileSubsetOf(params_high444, params_high)); +} + } // namespace webrtc diff --git a/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc b/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc index 493dc1fc925..e78629119a3 100644 --- a/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc +++ b/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc @@ -90,7 +90,7 @@ TEST_F(VideoDecoderSoftwareFallbackWrapperTest, InitializesDecoder) { EXPECT_EQ(1, fake_decoder_->configure_count_); EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); fallback_wrapper_->Decode(encoded_image, -1); EXPECT_EQ(1, fake_decoder_->configure_count_) << "Initialized decoder should not be reinitialized."; @@ -104,7 +104,7 @@ TEST_F(VideoDecoderSoftwareFallbackWrapperTest, EXPECT_EQ(1, fake_decoder_->configure_count_); EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); fallback_wrapper_->Decode(encoded_image, -1); EXPECT_EQ(1, fake_decoder_->configure_count_) << "Should not have attempted reinitializing the fallback decoder on " @@ -124,7 +124,7 @@ TEST_F(VideoDecoderSoftwareFallbackWrapperTest, IsSoftwareFallbackSticky) { EXPECT_EQ(1, fake_decoder_->decode_count_); // Software fallback should be sticky, fake_decoder_ shouldn't be used. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); fallback_wrapper_->Decode(encoded_image, -1); EXPECT_EQ(1, fake_decoder_->decode_count_) << "Decoder shouldn't be used after failure."; @@ -220,7 +220,7 @@ TEST_F(VideoDecoderSoftwareFallbackWrapperTest, FallbacksOnTooManyErrors) { fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR; EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); // Doesn't fallback from a single error. fallback_wrapper_->Decode(encoded_image, -1); EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName()); @@ -243,7 +243,7 @@ TEST_F(VideoDecoderSoftwareFallbackWrapperTest, fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR; EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); // Many decoded frames with the same error const int kNumFramesToEncode = 10; @@ -260,7 +260,7 @@ TEST_F(VideoDecoderSoftwareFallbackWrapperTest, fallback_wrapper_->Configure({}); EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); const int kNumFramesToEncode = 10; for (int i = 0; i < kNumFramesToEncode; ++i) { @@ -295,7 +295,7 @@ TEST_F(ForcedSoftwareDecoderFallbackTest, UsesForcedFallback) { EXPECT_EQ(1, sw_fallback_decoder_->configure_count_); EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); fallback_wrapper_->Decode(encoded_image, -1); EXPECT_EQ(1, sw_fallback_decoder_->configure_count_); EXPECT_EQ(1, sw_fallback_decoder_->decode_count_); diff --git a/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc b/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc index bdb388e6ecc..e69c9c17939 100644 --- a/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc +++ b/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc @@ -18,12 +18,11 @@ #include #include +#include "absl/strings/string_view.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/fec_controller_override.h" #include "api/scoped_refptr.h" #include "api/test/mock_video_encoder.h" -#include "api/units/timestamp.h" #include "api/video/encoded_image.h" #include "api/video/i420_buffer.h" #include "api/video/video_bitrate_allocator.h" @@ -38,8 +37,7 @@ #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/include/video_error_codes.h" #include "modules/video_coding/utility/simulcast_rate_allocator.h" -#include "rtc_base/fake_clock.h" -#include "test/create_test_field_trials.h" +#include "test/create_test_environment.h" #include "test/fake_encoder.h" #include "test/fake_texture_frame.h" #include "test/gmock.h" @@ -90,6 +88,10 @@ class FakeEncodedImageCallback : public EncodedImageCallback { ++callback_count_; return Result(Result::OK, callback_count_); } + + void OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) override {} int callback_count_ = 0; }; @@ -201,7 +203,7 @@ class VideoEncoderSoftwareFallbackWrapperTest explicit VideoEncoderSoftwareFallbackWrapperTest( CountingFakeEncoder* fake_sw_encoder) : VideoEncoderSoftwareFallbackWrapperTestBase( - CreateEnvironment(), + CreateTestEnvironment(), std::unique_ptr(fake_sw_encoder)), fake_sw_encoder_(fake_sw_encoder) { fake_sw_encoder_->implementation_name_ = "fake_sw_encoder"; @@ -491,17 +493,15 @@ TEST_F(VideoEncoderSoftwareFallbackWrapperTest, class ForcedFallbackTest : public VideoEncoderSoftwareFallbackWrapperTestBase { public: - explicit ForcedFallbackTest(const Environment& env) - : VideoEncoderSoftwareFallbackWrapperTestBase(env, - CreateVp8Encoder(env)) {} + explicit ForcedFallbackTest(absl::string_view field_trials) + : VideoEncoderSoftwareFallbackWrapperTestBase( + CreateTestEnvironment({.field_trials = field_trials}), + CreateVp8Encoder( + CreateTestEnvironment({.field_trials = field_trials}))) {} ~ForcedFallbackTest() override {} - protected: - void SetUp() override { - clock_.SetTime(Timestamp::Micros(1234)); - ConfigureVp8Codec(); - } + void SetUp() override { ConfigureVp8Codec(); } void TearDown() override { if (wrapper_initialized_) { @@ -549,24 +549,20 @@ class ForcedFallbackTest : public VideoEncoderSoftwareFallbackWrapperTestBase { EncodeFrame(expected_ret); CheckLastEncoderName(expected_name); } - - ScopedFakeClock clock_; }; class ForcedFallbackTestEnabled : public ForcedFallbackTest { public: ForcedFallbackTestEnabled() - : ForcedFallbackTest(CreateEnvironment(CreateTestFieldTrialsPtr( - std::string(kFieldTrial) + "/Enabled-" + - std::to_string(kMinPixelsPerFrame) + "," + - std::to_string(kWidth * kHeight) + ",30000/"))) {} + : ForcedFallbackTest(std::string(kFieldTrial) + "/Enabled-" + + std::to_string(kMinPixelsPerFrame) + "," + + std::to_string(kWidth * kHeight) + ",30000/") {} }; class ForcedFallbackTestDisabled : public ForcedFallbackTest { public: ForcedFallbackTestDisabled() - : ForcedFallbackTest(CreateEnvironment(CreateTestFieldTrialsPtr( - std::string(kFieldTrial) + "/Disabled/"))) {} + : ForcedFallbackTest(std::string(kFieldTrial) + "/Disabled/") {} }; TEST_F(ForcedFallbackTestDisabled, NoFallbackWithoutFieldTrial) { @@ -712,7 +708,7 @@ TEST(SoftwareFallbackEncoderTest, BothRateControllersNotTrusted) { std::unique_ptr wrapper = CreateVideoEncoderSoftwareFallbackWrapper( - CreateEnvironment(), std::unique_ptr(sw_encoder), + CreateTestEnvironment(), std::unique_ptr(sw_encoder), std::unique_ptr(hw_encoder), /*prefer_temporal_support=*/false); EXPECT_FALSE(wrapper->GetEncoderInfo().has_trusted_rate_controller); @@ -728,7 +724,7 @@ TEST(SoftwareFallbackEncoderTest, SwRateControllerTrusted) { std::unique_ptr wrapper = CreateVideoEncoderSoftwareFallbackWrapper( - CreateEnvironment(), std::unique_ptr(sw_encoder), + CreateTestEnvironment(), std::unique_ptr(sw_encoder), std::unique_ptr(hw_encoder), /*prefer_temporal_support=*/false); EXPECT_FALSE(wrapper->GetEncoderInfo().has_trusted_rate_controller); @@ -744,7 +740,7 @@ TEST(SoftwareFallbackEncoderTest, HwRateControllerTrusted) { std::unique_ptr wrapper = CreateVideoEncoderSoftwareFallbackWrapper( - CreateEnvironment(), std::unique_ptr(sw_encoder), + CreateTestEnvironment(), std::unique_ptr(sw_encoder), std::unique_ptr(hw_encoder), /*prefer_temporal_support=*/false); EXPECT_TRUE(wrapper->GetEncoderInfo().has_trusted_rate_controller); @@ -775,7 +771,7 @@ TEST(SoftwareFallbackEncoderTest, BothRateControllersTrusted) { std::unique_ptr wrapper = CreateVideoEncoderSoftwareFallbackWrapper( - CreateEnvironment(), std::unique_ptr(sw_encoder), + CreateTestEnvironment(), std::unique_ptr(sw_encoder), std::unique_ptr(hw_encoder), /*prefer_temporal_support=*/false); EXPECT_TRUE(wrapper->GetEncoderInfo().has_trusted_rate_controller); @@ -791,7 +787,7 @@ TEST(SoftwareFallbackEncoderTest, ReportsHardwareAccelerated) { std::unique_ptr wrapper = CreateVideoEncoderSoftwareFallbackWrapper( - CreateEnvironment(), std::unique_ptr(sw_encoder), + CreateTestEnvironment(), std::unique_ptr(sw_encoder), std::unique_ptr(hw_encoder), /*prefer_temporal_support=*/false); EXPECT_TRUE(wrapper->GetEncoderInfo().is_hardware_accelerated); @@ -821,7 +817,7 @@ TEST(SoftwareFallbackEncoderTest, ConfigureHardwareOnSecondAttempt) { std::unique_ptr wrapper = CreateVideoEncoderSoftwareFallbackWrapper( - CreateEnvironment(), std::unique_ptr(sw_encoder), + CreateTestEnvironment(), std::unique_ptr(sw_encoder), std::unique_ptr(hw_encoder), /*prefer_temporal_support=*/false); EXPECT_TRUE(wrapper->GetEncoderInfo().is_hardware_accelerated); @@ -864,7 +860,7 @@ class PreferTemporalLayersFallbackTest : public ::testing::Test { .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); wrapper_ = CreateVideoEncoderSoftwareFallbackWrapper( - CreateEnvironment(), std::unique_ptr(sw_), + CreateTestEnvironment(), std::unique_ptr(sw_), std::unique_ptr(hw_), /*prefer_temporal_support=*/true); @@ -1139,7 +1135,7 @@ INSTANTIATE_TEST_SUITE_P( TEST_P(ResolutionBasedFallbackTest, VerifyForcedEncoderFallback) { const ResolutionBasedFallbackTestParams& params = GetParam(); const Environment env = - CreateEnvironment(CreateTestFieldTrialsPtr(params.field_trials)); + CreateTestEnvironment({.field_trials = params.field_trials}); auto primary = std::make_unique(env); primary->SetImplementationName("primary"); auto fallback = std::make_unique(env); diff --git a/api/video_codecs/video_decoder_factory_template.h b/api/video_codecs/video_decoder_factory_template.h index fba0cec496b..45eeb0f8e2e 100644 --- a/api/video_codecs/video_decoder_factory_template.h +++ b/api/video_codecs/video_decoder_factory_template.h @@ -12,11 +12,11 @@ #define API_VIDEO_CODECS_VIDEO_DECODER_FACTORY_TEMPLATE_H_ #include +#include #include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_decoder.h" @@ -53,7 +53,7 @@ class VideoDecoderFactoryTemplate : public VideoDecoderFactory { private: bool IsFormatInList(const SdpVideoFormat& format, - ArrayView supported_formats) const { + std::span supported_formats) const { return absl::c_any_of( supported_formats, [&](const SdpVideoFormat& supported_format) { return supported_format.name == format.name && diff --git a/api/video_codecs/video_decoder_software_fallback_wrapper.cc b/api/video_codecs/video_decoder_software_fallback_wrapper.cc index ffb151e2ed3..59e24521195 100644 --- a/api/video_codecs/video_decoder_software_fallback_wrapper.cc +++ b/api/video_codecs/video_decoder_software_fallback_wrapper.cc @@ -20,7 +20,6 @@ #include "api/field_trials_view.h" #include "api/video/encoded_image.h" #include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" #include "api/video_codecs/video_decoder.h" #include "modules/video_coding/include/video_error_codes.h" #include "rtc_base/checks.h" @@ -197,7 +196,7 @@ int32_t VideoDecoderSoftwareFallbackWrapper::Decode( hw_consequtive_generic_errors_ = 0; return ret; } - if (input_image._frameType == VideoFrameType::kVideoFrameKey) { + if (input_image.IsKey()) { // Only count errors on key-frames, since generic errors can happen // with hw decoder due to many arbitrary reasons. // However, requesting a key-frame is supposed to fix the issue. diff --git a/api/video_codecs/video_encoder.cc b/api/video_codecs/video_encoder.cc index 0c25a2f6d7e..e4a2177079d 100644 --- a/api/video_codecs/video_encoder.cc +++ b/api/video_codecs/video_encoder.cc @@ -102,6 +102,7 @@ VideoEncoder::EncoderInfo::EncoderInfo() implementation_name("unknown"), has_trusted_rate_controller(false), is_hardware_accelerated(true), + enable_cpu_overuse_detection(true), fps_allocation{absl::InlinedVector( 1, kMaxFramerateFraction)}, @@ -133,6 +134,7 @@ std::string VideoEncoder::EncoderInfo::ToString() const { ", has_trusted_rate_controller = " << has_trusted_rate_controller << ", is_hardware_accelerated = " << is_hardware_accelerated + << ", enable_cpu_overuse_detection = " << enable_cpu_overuse_detection << ", fps_allocation = ["; size_t num_spatial_layer_with_fps_allocation = 0; for (size_t i = 0; i < kMaxSpatialLayers; ++i) { @@ -217,7 +219,8 @@ bool VideoEncoder::EncoderInfo::operator==(const EncoderInfo& rhs) const { if (supports_native_handle != rhs.supports_native_handle || implementation_name != rhs.implementation_name || has_trusted_rate_controller != rhs.has_trusted_rate_controller || - is_hardware_accelerated != rhs.is_hardware_accelerated) { + is_hardware_accelerated != rhs.is_hardware_accelerated || + enable_cpu_overuse_detection != rhs.enable_cpu_overuse_detection) { return false; } diff --git a/api/video_codecs/video_encoder.h b/api/video_codecs/video_encoder.h index d8f4b35d5f2..1110b44e518 100644 --- a/api/video_codecs/video_encoder.h +++ b/api/video_codecs/video_encoder.h @@ -230,6 +230,9 @@ class RTC_EXPORT VideoEncoder { // thresholds will be used in CPU adaptation. bool is_hardware_accelerated; + // If this field is true, this encoder opts into CPU overuse detection. + bool enable_cpu_overuse_detection; + // For each spatial layer (simulcast stream or SVC layer), represented as an // element in `fps_allocation` a vector indicates how many temporal layers // the encoder is using for that spatial layer. diff --git a/api/video_codecs/video_encoder_factory_template.h b/api/video_codecs/video_encoder_factory_template.h index f58a36ed871..938a73b8444 100644 --- a/api/video_codecs/video_encoder_factory_template.h +++ b/api/video_codecs/video_encoder_factory_template.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/sdp_video_format.h" @@ -76,7 +76,7 @@ class VideoEncoderFactoryTemplate : public VideoEncoderFactory { private: bool IsFormatInList(const SdpVideoFormat& format, - ArrayView supported_formats) const { + std::span supported_formats) const { return absl::c_any_of( supported_formats, [&](const SdpVideoFormat& supported_format) { return supported_format.name == format.name && diff --git a/api/video_codecs/video_encoder_interface.h b/api/video_codecs/video_encoder_interface.h index 2e99d507703..5a3cb77217c 100644 --- a/api/video_codecs/video_encoder_interface.h +++ b/api/video_codecs/video_encoder_interface.h @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" @@ -43,7 +43,7 @@ class VideoEncoderInterface { struct FrameOutput { virtual ~FrameOutput() = default; - virtual ArrayView GetBitstreamOutputBuffer(DataSize size) = 0; + virtual std::span GetBitstreamOutputBuffer(DataSize size) = 0; virtual void EncodeComplete(const EncodeResult& encode_result) = 0; }; diff --git a/api/voip/BUILD.gn b/api/voip/BUILD.gn index b0ae871f419..0d3e7b7da84 100644 --- a/api/voip/BUILD.gn +++ b/api/voip/BUILD.gn @@ -20,7 +20,6 @@ rtc_source_set("voip_api") { "voip_volume_control.h", ] deps = [ - "..:array_view", "../audio_codecs:audio_codecs_api", "../neteq:neteq_api", "//third_party/abseil-cpp/absl/base:core_headers", @@ -56,7 +55,6 @@ if (rtc_include_tests) { sources = [ "test/mock_voip_engine.h" ] deps = [ ":voip_api", - "..:array_view", "../../test:test_support", "../audio_codecs:audio_codecs_api", ] diff --git a/api/voip/DEPS b/api/voip/DEPS index a36df41a9c7..3994acffd84 100644 --- a/api/voip/DEPS +++ b/api/voip/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - "voip_engine_factory.h": [ + "voip_engine_factory\\.h": [ "+modules/audio_device/include/audio_device.h", "+modules/audio_processing/include/audio_processing.h", ], diff --git a/api/voip/test/mock_voip_engine.h b/api/voip/test/mock_voip_engine.h index 97006a5928d..779824e28a1 100644 --- a/api/voip/test/mock_voip_engine.h +++ b/api/voip/test/mock_voip_engine.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/voip/voip_base.h" #include "api/voip/voip_codec.h" @@ -69,11 +69,11 @@ class MockVoipNetwork : public VoipNetwork { public: MOCK_METHOD(VoipResult, ReceivedRTPPacket, - (ChannelId channel_id, ArrayView rtp_packet), + (ChannelId channel_id, std::span rtp_packet), (override)); MOCK_METHOD(VoipResult, ReceivedRTCPPacket, - (ChannelId channel_id, ArrayView rtcp_packet), + (ChannelId channel_id, std::span rtcp_packet), (override)); }; diff --git a/api/voip/voip_network.h b/api/voip/voip_network.h index b239c7eaf88..7760891d784 100644 --- a/api/voip/voip_network.h +++ b/api/voip/voip_network.h @@ -12,8 +12,8 @@ #define API_VOIP_VOIP_NETWORK_H_ #include +#include -#include "api/array_view.h" #include "api/voip/voip_base.h" namespace webrtc { @@ -28,7 +28,7 @@ class VoipNetwork { // kOk - received RTP packet is processed. // kInvalidArgument - `channel_id` is invalid. virtual VoipResult ReceivedRTPPacket(ChannelId channel_id, - ArrayView rtp_packet) = 0; + std::span rtp_packet) = 0; // The data received from the network including RTCP header is passed here. // Returns following VoipResult; @@ -36,7 +36,7 @@ class VoipNetwork { // kInvalidArgument - `channel_id` is invalid. virtual VoipResult ReceivedRTCPPacket( ChannelId channel_id, - ArrayView rtcp_packet) = 0; + std::span rtcp_packet) = 0; protected: virtual ~VoipNetwork() = default; diff --git a/api/webrtc_sdp.cc b/api/webrtc_sdp.cc index ca180ed0ad7..67eee808013 100644 --- a/api/webrtc_sdp.cc +++ b/api/webrtc_sdp.cc @@ -33,6 +33,7 @@ #include "api/candidate.h" #include "api/jsep.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" @@ -192,7 +193,7 @@ const char kDefaultSctpmapProtocol[] = "webrtc-datachannel"; // RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload // types. -const int kWildcardPayloadType = -1; +constexpr PayloadType kWildcardPayloadType = PayloadType::NotSet(); // Check if passed character is a "token-char" from RFC 4566. // https://datatracker.ietf.org/doc/html/rfc4566#section-9 @@ -1013,7 +1014,7 @@ bool GetParameter(const std::string& name, return true; } -void WriteFmtpHeader(int payload_type, StringBuilder* os) { +void WriteFmtpHeader(PayloadType payload_type, StringBuilder* os) { // fmtp header: a=fmtp:`payload_type` // Add a=fmtp InitAttrLine(kAttributeFmtp, os); @@ -1021,7 +1022,7 @@ void WriteFmtpHeader(int payload_type, StringBuilder* os) { *os << kSdpDelimiterColon << payload_type; } -void WritePacketizationHeader(int payload_type, StringBuilder* os) { +void WritePacketizationHeader(PayloadType payload_type, StringBuilder* os) { // packetization header: a=packetization:`payload_type` // Add a=packetization InitAttrLine(kAttributePacketization, os); @@ -1029,7 +1030,7 @@ void WritePacketizationHeader(int payload_type, StringBuilder* os) { *os << kSdpDelimiterColon << payload_type; } -void WriteRtcpFbHeader(int payload_type, StringBuilder* os) { +void WriteRtcpFbHeader(PayloadType payload_type, StringBuilder* os) { // rtcp-fb header: a=rtcp-fb:`payload_type` // /> // Add a=rtcp-fb @@ -1117,12 +1118,21 @@ void AddOrReplaceCodec(MediaContentDescription* content_desc, void BuildRtpmap(const MediaContentDescription* media_desc, const MediaType media_type, + bool use_wildcard, std::string* message) { RTC_DCHECK(message != nullptr); RTC_DCHECK(media_desc != nullptr); StringBuilder os; if (media_type == MediaType::VIDEO) { - for (const Codec& codec : media_desc->codecs()) { + for (Codec codec : media_desc->codecs()) { + // Include transport-wide codec parameters. + // TODO: issues.webrtc.org/489794442 - Remove when wildcards are + // OK to send. + if (!use_wildcard) { + if (media_desc->rtcp_fb_ack_ccfb()) { + codec.feedback_params.Add({"ack", "ccfb"}); + } + } // RFC 4566 // a=rtpmap: / // [/] @@ -1140,8 +1150,16 @@ void BuildRtpmap(const MediaContentDescription* media_desc, std::vector ptimes; std::vector maxptimes; int max_minptime = 0; - for (const Codec& codec : media_desc->codecs()) { + for (Codec codec : media_desc->codecs()) { RTC_DCHECK(!codec.name.empty()); + // Include transport-wide codec parameters. + // TODO: issues.webrtc.org/489794442 - Remove when wildcards are + // OK to send. + if (!use_wildcard) { + if (media_desc->rtcp_fb_ack_ccfb()) { + codec.feedback_params.Add({"ack", "ccfb"}); + } + } // RFC 4566 // a=rtpmap: / // [/] @@ -1183,18 +1201,21 @@ void BuildRtpmap(const MediaContentDescription* media_desc, AddAttributeLine(kCodecParamPTime, ptime, message); } } - if (media_desc->rtcp_fb_ack_ccfb()) { - // RFC 8888 section 6 - InitAttrLine(kAttributeRtcpFb, &os); - os << kSdpDelimiterColon; - os << "* ack ccfb"; - AddLine(os.str(), message); + if (use_wildcard) { + if (media_desc->rtcp_fb_ack_ccfb()) { + // RFC 8888 section 6 + InitAttrLine(kAttributeRtcpFb, &os); + os << kSdpDelimiterColon; + os << "* ack ccfb"; + AddLine(os.str(), message); + } } } void BuildRtpContentAttributes(const MediaContentDescription* media_desc, const MediaType media_type, int msid_signaling, + bool use_wildcard, std::string* message) { SimulcastSdpSerializer serializer; StringBuilder os; @@ -1289,7 +1310,7 @@ void BuildRtpContentAttributes(const MediaContentDescription* media_desc, // RFC 4566 // a=rtpmap: / // [/] - BuildRtpmap(media_desc, media_type, message); + BuildRtpmap(media_desc, media_type, use_wildcard, message); for (const StreamParams& track : media_desc->streams()) { // Build the ssrc-group lines. @@ -1449,7 +1470,9 @@ void BuildMediaDescription(const ContentInfo* content_info, if (IsDtlsSctp(media_desc->protocol())) { BuildSctpContentAttributes(media_desc, message); } else if (IsRtpProtocol(media_desc->protocol())) { - BuildRtpContentAttributes(media_desc, media_type, msid_signaling, message); + // TODO: issues.webrtc.org/48979442 - Make this field trial controlled + BuildRtpContentAttributes(media_desc, media_type, msid_signaling, + /* use_wildcard= */ false, message); } } @@ -2013,7 +2036,7 @@ void BackfillCodecParameters(std::vector& codecs) { // with that payload type. Codec GetCodecWithPayloadType(MediaType type, const std::vector& codecs, - int payload_type) { + PayloadType payload_type) { const Codec* codec = FindCodecById(codecs, payload_type); if (codec) return *codec; @@ -2028,7 +2051,7 @@ Codec GetCodecWithPayloadType(MediaType type, // Adds or updates existing codec corresponding to `payload_type` according // to `parameters`. void UpdateCodec(MediaContentDescription* content_desc, - int payload_type, + PayloadType payload_type, const CodecParameterMap& parameters) { // Codec might already have been populated (from rtpmap). Codec new_codec = GetCodecWithPayloadType( @@ -2040,7 +2063,7 @@ void UpdateCodec(MediaContentDescription* content_desc, // Adds or updates existing codec corresponding to `payload_type` according // to `feedback_param`. void UpdateCodec(MediaContentDescription* content_desc, - int payload_type, + PayloadType payload_type, const FeedbackParam& feedback_param) { // Codec might already have been populated (from rtpmap). Codec new_codec = GetCodecWithPayloadType( @@ -2076,11 +2099,12 @@ bool ParseFmtpAttributes(absl::string_view line, return false; } - int payload_type = 0; - if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type, + int pt_int = 0; + if (!GetPayloadTypeFromString(line_payload, payload_type_str, &pt_int, error)) { return false; } + PayloadType payload_type = PayloadType(pt_int); // Parse out format specific parameters. CodecParameterMap codec_params; @@ -2202,7 +2226,7 @@ bool ParseSsrcAttribute(absl::string_view line, // Updates or creates a new codec entry in the audio description with according // to `name`, `clockrate`, `bitrate`, and `channels`. -void UpdateCodec(int payload_type, +void UpdateCodec(PayloadType payload_type, absl::string_view name, int clockrate, int bitrate, @@ -2221,7 +2245,7 @@ void UpdateCodec(int payload_type, // Updates or creates a new codec entry in the video description according to // `name`, `width`, `height`, and `framerate`. -void UpdateCodec(int payload_type, +void UpdateCodec(PayloadType payload_type, absl::string_view name, MediaContentDescription* desc) { // Codec may already be populated with (only) optional parameters @@ -2336,7 +2360,7 @@ bool ParseRtpmapAttribute(absl::string_view line, // Adds or updates existing video codec corresponding to `payload_type` // according to `packetization`. void UpdateVideoCodecPacketization(MediaContentDescription* desc, - int payload_type, + PayloadType payload_type, absl::string_view packetization) { if (packetization != kPacketizationParamRaw) { // Ignore unsupported packetization attribute. @@ -2367,11 +2391,11 @@ bool ParsePacketizationAttribute(absl::string_view line, &payload_type_string, error)) { return false; } - int payload_type; - if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type, - error)) { + int pt_int; + if (!GetPayloadTypeFromString(line, payload_type_string, &pt_int, error)) { return false; } + PayloadType payload_type = PayloadType(pt_int); absl::string_view packetization = packetization_fields[1]; UpdateVideoCodecPacketization(media_desc, payload_type, packetization); return true; @@ -2394,12 +2418,13 @@ bool ParseRtcpFbAttribute(absl::string_view line, error)) { return false; } - int payload_type = kWildcardPayloadType; + PayloadType payload_type = kWildcardPayloadType; if (payload_type_string != "*") { - if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type, - error)) { + int pt_int; + if (!GetPayloadTypeFromString(line, payload_type_string, &pt_int, error)) { return false; } + payload_type = PayloadType(pt_int); } absl::string_view id = rtcp_fb_fields[1]; std::string param = ""; @@ -2437,11 +2462,26 @@ void UpdateFromWildcardCodecs(MediaContentDescription* desc) { for (auto& codec : codecs) { AddFeedbackParameters(wildcard_codec->feedback_params, &codec); } - // Special treatment for transport-wide feedback params. - if (wildcard_codec->feedback_params.Has({"ack", "ccfb"})) { + desc->set_codecs(codecs); +} + +// Update settings that are only valid if media-section-wide, but +// are represented as per-codecs settings. +// Note that this is done after UpdateFromWildcardCodecs, so settings +// that are set with wildcard will also be picked up here. +void UpdateFromMediaSectionWideSettings(MediaContentDescription* desc) { + if (desc->codecs().empty()) { + return; + } + bool all_codecs_have_ack_ccfb = true; + for (const auto& codec : desc->codecs()) { + if (!codec.feedback_params.Has({"ack", "ccfb"})) { + all_codecs_have_ack_ccfb = false; + } + } + if (all_codecs_have_ack_ccfb) { desc->set_rtcp_fb_ack_ccfb(true); } - desc->set_codecs(codecs); } void AddAudioAttribute(const std::string& name, @@ -2862,6 +2902,7 @@ bool ParseContent(absl::string_view message, } UpdateFromWildcardCodecs(media_desc); + UpdateFromMediaSectionWideSettings(media_desc); // Codec has not been populated correctly unless the name has been set. This // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't // have a corresponding "rtpmap" line. This should lead to a parse error. diff --git a/api/webrtc_sdp_unittest.cc b/api/webrtc_sdp_unittest.cc index 8cdf977f925..d121d8d36f6 100644 --- a/api/webrtc_sdp_unittest.cc +++ b/api/webrtc_sdp_unittest.cc @@ -25,7 +25,6 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/jsep.h" #include "api/media_types.h" @@ -1927,7 +1926,8 @@ class WebRtcSdpTest : public ::testing::Test { void TestDeserializeRtcpFb( std::unique_ptr& jdesc_output, - bool use_wildcard) { + bool use_wildcard, + bool use_ccfb) { std::string sdp_session_and_audio = "v=0\r\n" "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" @@ -1949,8 +1949,14 @@ class WebRtcSdpTest : public ::testing::Test { std::ostringstream os; os << sdp_session_and_audio; os << "a=rtcp-fb:" << (use_wildcard ? "*" : "111") << " nack\r\n"; + if (use_ccfb) { + os << "a=rtcp-fb:" << (use_wildcard ? "*" : "111") << " ack ccfb\r\n"; + } os << sdp_video; os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") << " ccm fir\r\n"; + if (use_ccfb) { + os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") << " ack ccfb\r\n"; + } std::string sdp = os.str(); // Deserialize SdpParseError error; @@ -1982,6 +1988,21 @@ class WebRtcSdpTest : public ::testing::Test { FeedbackParam(kRtcpFbParamRemb, kParamValueEmpty))); EXPECT_TRUE(vp8.HasFeedbackParam( FeedbackParam(kRtcpFbParamCcm, kRtcpFbCcmParamFir))); + MediaContentDescription* video_media = + GetFirstMediaContent(jdesc_output->description(), MediaType::VIDEO) + ->media_description(); + MediaContentDescription* audio_media = + GetFirstMediaContent(jdesc_output->description(), MediaType::AUDIO) + ->media_description(); + if (use_ccfb) { + EXPECT_TRUE(video_media->rtcp_fb_ack_ccfb()) + << "Wildcard: " << use_wildcard; + EXPECT_TRUE(audio_media->rtcp_fb_ack_ccfb()) + << "Wildcard: " << use_wildcard; + } else { + EXPECT_FALSE(video_media->rtcp_fb_ack_ccfb()); + EXPECT_FALSE(audio_media->rtcp_fb_ack_ccfb()); + } } // Two SDP messages can mean the same thing but be different strings, e.g. @@ -3225,14 +3246,24 @@ TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) { TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) { const bool kUseWildcard = false; std::unique_ptr jdesc_output; - TestDeserializeRtcpFb(jdesc_output, kUseWildcard); + TestDeserializeRtcpFb(jdesc_output, kUseWildcard, /* use_ccfb= */ false); TestSerialize(jdesc_output); } TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) { const bool kUseWildcard = true; std::unique_ptr jdesc_output; - TestDeserializeRtcpFb(jdesc_output, kUseWildcard); + TestDeserializeRtcpFb(jdesc_output, kUseWildcard, /* use_ccfb= */ false); + TestSerialize(jdesc_output); +} + +TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpWithCcfb) { + std::unique_ptr jdesc_output; + TestDeserializeRtcpFb(jdesc_output, /* use_wildcard= */ false, + /* use_ccfb= */ true); + TestSerialize(jdesc_output); + TestDeserializeRtcpFb(jdesc_output, /* use_wildcard= */ true, + /* use_ccfb= */ true); TestSerialize(jdesc_output); } diff --git a/audio/BUILD.gn b/audio/BUILD.gn index 6ee329d1464..c710be09b92 100644 --- a/audio/BUILD.gn +++ b/audio/BUILD.gn @@ -40,7 +40,6 @@ rtc_library("audio") { ] deps = [ - "../api:array_view", "../api:bitrate_allocation", "../api:call_api", "../api:field_trials_view", @@ -81,8 +80,10 @@ rtc_library("audio") { "../common_audio", "../common_audio:common_audio_c", "../logging:rtc_event_audio", + "../logging:rtc_event_rtp_rtcp", "../logging:rtc_stream_config", "../media:media_channel", + "../media:media_channel_impl", "../media:media_constants", "../modules:module_api_public", "../modules/async_audio_processing", @@ -162,7 +163,7 @@ if (rtc_include_tests) { deps = [ ":audio", ":audio_end_to_end_test", - "../api:array_view", + ":channel_receive_unittest", "../api:bitrate_allocation", "../api:call_api", "../api:field_trials", @@ -249,7 +250,6 @@ if (rtc_include_tests) { sources = [ "channel_receive_unittest.cc" ] deps = [ ":audio", - "../api:array_view", "../api:make_ref_counted", "../api:mock_frame_transformer", "../api:scoped_refptr", @@ -261,6 +261,7 @@ if (rtc_include_tests) { "../api/environment:environment_factory", "../api/units:time_delta", "../api/units:timestamp", + "../logging:mocks", "../modules/audio_device:mock_audio_device", "../modules/rtp_rtcp:ntp_time_util", "../modules/rtp_rtcp:rtp_rtcp_format", diff --git a/audio/DEPS b/audio/DEPS index 7a0c7e7ce62..43d71c48540 100644 --- a/audio/DEPS +++ b/audio/DEPS @@ -18,10 +18,10 @@ include_rules = [ ] specific_include_rules = { - "audio_send_stream.cc": [ + "audio_send_stream\\.cc": [ "+modules/audio_coding/codecs/cng/audio_encoder_cng.h", ], - "audio_transport_impl.h": [ + "audio_transport_impl\\.h": [ "+modules/audio_processing/typing_detection.h", ] } diff --git a/audio/OWNERS b/audio/OWNERS index e629bc1815f..547018deb70 100644 --- a/audio/OWNERS +++ b/audio/OWNERS @@ -1,4 +1,3 @@ -alessiob@webrtc.org gustaf@webrtc.org henrik.lundin@webrtc.org jakobi@webrtc.org diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc index 741f7e9f801..4f052c6a211 100644 --- a/audio/audio_receive_stream.cc +++ b/audio/audio_receive_stream.cc @@ -15,12 +15,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_format.h" @@ -55,7 +55,6 @@ std::string AudioReceiveStreamInterface::Config::Rtp::ToString() const { char ss_buf[1024]; SimpleStringBuilder ss(ss_buf); ss << "{remote_ssrc: " << remote_ssrc; - ss << ", local_ssrc: " << local_ssrc; ss << ", nack: " << nack.ToString(); ss << ", rtcp: " << (rtcp_mode == RtcpMode::kCompound @@ -89,7 +88,7 @@ std::unique_ptr CreateChannelReceive( static_cast(audio_state); return voe::CreateChannelReceive( env, neteq_factory, internal_audio_state->audio_device_module(), - config.rtcp_send_transport, config.rtp.local_ssrc, config.rtp.remote_ssrc, + config.rtcp_send_transport, config.rtp.remote_ssrc, config.jitter_buffer_max_packets, config.jitter_buffer_fast_accelerate, config.jitter_buffer_min_delay_ms, config.enable_non_sender_rtt, config.decoder_factory, std::move(config.frame_decryptor), @@ -168,7 +167,6 @@ void AudioReceiveStreamImpl::ReconfigureForTesting( // SSRC can't be changed mid-stream. RTC_DCHECK_EQ(remote_ssrc(), config.rtp.remote_ssrc); - RTC_DCHECK_EQ(local_ssrc(), config.rtp.local_ssrc); // Configuration parameters which cannot be changed. RTC_DCHECK_EQ(config_.rtcp_send_transport, config.rtcp_send_transport); @@ -448,7 +446,7 @@ bool AudioReceiveStreamImpl::SetMinimumPlayoutDelay(TimeDelta delay) { return channel_receive_->SetMinimumPlayoutDelay(delay); } -void AudioReceiveStreamImpl::DeliverRtcp(ArrayView packet) { +void AudioReceiveStreamImpl::DeliverRtcp(std::span packet) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); channel_receive_->ReceivedRTCPPacket(packet.data(), packet.size()); } @@ -458,18 +456,6 @@ void AudioReceiveStreamImpl::SetSyncGroup(absl::string_view sync_group) { config_.sync_group = std::string(sync_group); } -void AudioReceiveStreamImpl::SetLocalSsrc(uint32_t local_ssrc) { - RTC_DCHECK_RUN_ON(&packet_sequence_checker_); - // TODO(tommi): Consider storing local_ssrc in one place. - config_.rtp.local_ssrc = local_ssrc; - channel_receive_->OnLocalSsrcChange(local_ssrc); -} - -uint32_t AudioReceiveStreamImpl::local_ssrc() const { - RTC_DCHECK_RUN_ON(&packet_sequence_checker_); - return config_.rtp.local_ssrc; -} - const std::string& AudioReceiveStreamImpl::sync_group() const { RTC_DCHECK_RUN_ON(&packet_sequence_checker_); return config_.sync_group; diff --git a/audio/audio_receive_stream.h b/audio/audio_receive_stream.h index cba510ad5d9..41a470f8b82 100644 --- a/audio/audio_receive_stream.h +++ b/audio/audio_receive_stream.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_format.h" @@ -128,14 +128,10 @@ class AudioReceiveStreamImpl final : public webrtc::AudioReceiveStreamInterface, Timestamp time) override; bool SetMinimumPlayoutDelay(TimeDelta delay) override; - void DeliverRtcp(ArrayView packet); + void DeliverRtcp(std::span packet); void SetSyncGroup(absl::string_view sync_group); - void SetLocalSsrc(uint32_t local_ssrc); - - uint32_t local_ssrc() const; - uint32_t remote_ssrc() const override { // The remote_ssrc member variable of config_ will never change and can be // considered const. diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc index 652224db584..84b5407c824 100644 --- a/audio/audio_receive_stream_unittest.cc +++ b/audio/audio_receive_stream_unittest.cc @@ -148,7 +148,6 @@ struct ConfigHelper { EXPECT_THAT(codecs, ::testing::IsEmpty()); }); - stream_config_.rtp.local_ssrc = kLocalSsrc; stream_config_.rtp.remote_ssrc = kRemoteSsrc; stream_config_.rtp.nack.rtp_history_ms = 300; stream_config_.rtcp_send_transport = &rtcp_send_transport_; @@ -219,10 +218,9 @@ std::vector CreateRtcpSenderReport() { TEST(AudioReceiveStreamTest, ConfigToString) { AudioReceiveStreamInterface::Config config; config.rtp.remote_ssrc = kRemoteSsrc; - config.rtp.local_ssrc = kLocalSsrc; config.rtp.rtcp_mode = RtcpMode::kOff; EXPECT_EQ( - "{rtp: {remote_ssrc: 1234, local_ssrc: 5678, nack: " + "{rtp: {remote_ssrc: 1234, nack: " "{rtp_history_ms: 0}, rtcp: off}, " "rtcp_send_transport: null}", config.ToString()); diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc index c114502c8e1..d2296bc3c5f 100644 --- a/audio/audio_send_stream.cc +++ b/audio/audio_send_stream.cc @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_processing.h" #include "api/audio_codecs/audio_encoder.h" @@ -293,10 +293,13 @@ void AudioSendStream::ConfigureStream( channel_send_->ResetSenderCongestionControlObjects(); } + absl::string_view uri = TransportSequenceNumber::Uri(); + rtp_rtcp_module_->DeregisterSendRtpHeaderExtension(uri); + if (!allocate_audio_without_feedback_ && new_ids.transport_sequence_number != 0) { rtp_rtcp_module_->RegisterRtpHeaderExtension( - TransportSequenceNumber::Uri(), new_ids.transport_sequence_number); + uri, new_ids.transport_sequence_number); // Probing in application limited region is only used in combination with // send side congestion control, wich depends on feedback packets which // requires transport sequence numbers to be enabled. @@ -497,7 +500,7 @@ webrtc::AudioSendStream::Stats AudioSendStream::GetStats( return stats; } -void AudioSendStream::DeliverRtcp(ArrayView packet) { +void AudioSendStream::DeliverRtcp(std::span packet) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); channel_send_->ReceivedRTCPPacket(packet.data(), packet.size()); // Poll if overhead has changed, which it can do if ack triggers us to stop diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h index 7d5ba3ceeff..7dbb97170fa 100644 --- a/audio/audio_send_stream.h +++ b/audio/audio_send_stream.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/call/bitrate_allocation.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" @@ -104,7 +104,7 @@ class AudioSendStream final : public webrtc::AudioSendStream, webrtc::AudioSendStream::Stats GetStats( bool has_remote_tracks) const override; - void DeliverRtcp(ArrayView packet); + void DeliverRtcp(std::span packet); // Implements BitrateAllocatorObserver. uint32_t OnBitrateUpdated(BitrateAllocationUpdate update) override; diff --git a/audio/audio_send_stream_tests.cc b/audio/audio_send_stream_tests.cc index 6c754a18783..5379026c1d0 100644 --- a/audio/audio_send_stream_tests.cc +++ b/audio/audio_send_stream_tests.cc @@ -10,10 +10,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/rtp_parameters.h" #include "call/audio_receive_stream.h" @@ -56,7 +56,7 @@ TEST_F(AudioSendStreamCallTest, SupportsCName) { CNameObserver() = default; private: - Action OnSendRtcp(ArrayView packet) override { + Action OnSendRtcp(std::span packet) override { RtcpPacketParser parser; EXPECT_TRUE(parser.Parse(packet)); if (parser.sdes()->num_packets() > 0) { @@ -89,7 +89,7 @@ TEST_F(AudioSendStreamCallTest, NoExtensionsByDefault) { NoExtensionsObserver() = default; private: - Action OnSendRtp(ArrayView packet) override { + Action OnSendRtp(std::span packet) override { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); // rtp packet is valid. EXPECT_EQ(packet[0] & 0b0001'0000, 0); // extension bit not set. @@ -119,7 +119,7 @@ TEST_F(AudioSendStreamCallTest, SupportsAudioLevel) { extensions_.Register(kAudioLevelExtensionId); } - Action OnSendRtp(ArrayView packet) override { + Action OnSendRtp(std::span packet) override { RtpPacket rtp_packet(&extensions_); EXPECT_TRUE(rtp_packet.Parse(packet)); @@ -164,7 +164,7 @@ class TransportWideSequenceNumberObserver : public AudioSendTest { } private: - Action OnSendRtp(ArrayView packet) override { + Action OnSendRtp(std::span packet) override { RtpPacket rtp_packet(&extensions_); EXPECT_TRUE(rtp_packet.Parse(packet)); @@ -210,7 +210,7 @@ TEST_F(AudioSendStreamCallTest, SendDtmf) { DtmfObserver() = default; private: - Action OnSendRtp(ArrayView packet) override { + Action OnSendRtp(std::span packet) override { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc index 29281bf915d..e756588ac46 100644 --- a/audio/audio_send_stream_unittest.cc +++ b/audio/audio_send_stream_unittest.cc @@ -831,6 +831,50 @@ TEST(AudioSendStreamTest, ReconfigureTransportCcResetsFirst) { } } +TEST(AudioSendStreamTest, ReconfigureTransportCcDeregistersExtension) { + for (bool use_null_audio_processing : {false, true}) { + ConfigHelper helper(false, true, use_null_audio_processing); + helper.config().rtp.extensions.push_back(RtpExtension( + RtpExtension::kTransportSequenceNumberUri, kTransportSequenceNumberId)); + + EXPECT_CALL(*helper.rtp_rtcp(), + RegisterRtpHeaderExtension(TransportSequenceNumber::Uri(), + kTransportSequenceNumberId)) + .Times(1); + auto send_stream = helper.CreateAudioSendStream(); + + // Reconfigure with a different transport-cc extension ID. + constexpr int kNewTransportSequenceNumberId = + kTransportSequenceNumberId + 1; + auto new_config = helper.config(); + new_config.rtp.extensions.clear(); + new_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kAudioLevelUri, kAudioLevelId)); + new_config.rtp.extensions.push_back( + RtpExtension(RtpExtension::kTransportSequenceNumberUri, + kNewTransportSequenceNumberId)); + + // Expect deregistration before re-registration with the new ID. + EXPECT_CALL(*helper.rtp_rtcp(), DeregisterSendRtpHeaderExtension( + TransportSequenceNumber::Uri())) + .Times(1); + EXPECT_CALL(*helper.rtp_rtcp(), + RegisterRtpHeaderExtension(TransportSequenceNumber::Uri(), + kNewTransportSequenceNumberId)) + .Times(1); + { + ::testing::InSequence seq; + EXPECT_CALL(*helper.channel_send(), ResetSenderCongestionControlObjects()) + .Times(1); + EXPECT_CALL(*helper.channel_send(), + RegisterSenderCongestionControlObjects(helper.transport())) + .Times(1); + } + + send_stream->Reconfigure(new_config, nullptr); + } +} + TEST(AudioSendStreamTest, OnTransportOverheadChanged) { for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(false, true, use_null_audio_processing); diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc index 5c710c6ee7e..97ba70f2c56 100644 --- a/audio/channel_receive.cc +++ b/audio/channel_receive.cc @@ -17,11 +17,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_decoder_factory.h" @@ -55,6 +55,7 @@ #include "call/syncable.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h" #include "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" #include "modules/audio_coding/acm2/acm_resampler.h" #include "modules/audio_coding/acm2/call_statistics.h" #include "modules/audio_coding/include/audio_coding_module_typedefs.h" @@ -120,7 +121,6 @@ class ChannelReceive : public ChannelReceiveInterface, NetEqFactory* neteq_factory, AudioDeviceModule* audio_device_module, Transport* rtcp_send_transport, - uint32_t local_ssrc, uint32_t remote_ssrc, size_t jitter_buffer_max_packets, bool jitter_buffer_fast_playout, @@ -206,8 +206,6 @@ class ChannelReceive : public ChannelReceiveInterface, void SetFrameDecryptor( scoped_refptr frame_decryptor) override; - void OnLocalSsrcChange(uint32_t local_ssrc) override; - void RtcpPacketTypesCounterUpdated( uint32_t ssrc, const RtcpPacketTypeCounter& packet_counter) override; @@ -222,8 +220,8 @@ class ChannelReceive : public ChannelReceiveInterface, int GetRtpTimestampRateHz() const; - void OnReceivedPayloadData(ArrayView payload, - const RTPHeader& rtpHeader, + void OnReceivedPayloadData(std::span payload, + const RTPHeader& header, Timestamp receive_time) RTC_RUN_ON(worker_thread_checker_); @@ -336,8 +334,8 @@ class ChannelReceive : public ChannelReceiveInterface, RTC_GUARDED_BY(worker_thread_checker_); }; -void ChannelReceive::OnReceivedPayloadData(ArrayView payload, - const RTPHeader& rtpHeader, +void ChannelReceive::OnReceivedPayloadData(std::span payload, + const RTPHeader& header, Timestamp receive_time) { if (!playing_) { // Avoid inserting into NetEQ when we are not playing. Count the @@ -352,35 +350,23 @@ void ChannelReceive::OnReceivedPayloadData(ArrayView payload, // that muting playout also stops the SourceTracker from updating RtpSource // information. RtpPacketInfos::vector_type packet_vector = { - RtpPacketInfo(rtpHeader, receive_time)}; + RtpPacketInfo(header, receive_time)}; source_tracker_.OnFrameDelivered(RtpPacketInfos(packet_vector), env_.clock().CurrentTime()); return; } - // Push the incoming payload (parsed and ready for decoding) into NetEq. - if (!payload.empty()) { - MutexLock lock(&neteq_mutex_); - if (neteq_->InsertPacket(rtpHeader, payload, - RtpPacketInfo(rtpHeader, receive_time)) != - NetEq::kOK) { - RTC_DLOG(LS_ERROR) << "ChannelReceive::OnReceivedPayloadData() unable to " - "insert packet into NetEq; PT = " - << static_cast(rtpHeader.payloadType); - return; - } + if (payload.empty()) { + return; } - if (nack_tracker_) { - TimeDelta round_trip_time = - rtp_rtcp_->LastRtt().value_or(TimeDelta::Zero()); - nack_tracker_->UpdateLastReceivedPacket(rtpHeader.sequenceNumber, - rtpHeader.timestamp); - std::vector nack_list = - nack_tracker_->GetNackList(round_trip_time.ms()); - if (!nack_list.empty()) { - rtp_rtcp_->SendNACK(nack_list.data(), nack_list.size()); - } + // Push the incoming payload (parsed and ready for decoding) into NetEq. + MutexLock lock(&neteq_mutex_); + if (neteq_->InsertPacket(header, payload, + RtpPacketInfo(header, receive_time)) != NetEq::kOK) { + RTC_DLOG(LS_ERROR) << "ChannelReceive::OnReceivedPayloadData() unable to " + "insert packet into NetEq; PT = " + << static_cast(header.payloadType); } } @@ -392,7 +378,7 @@ void ChannelReceive::InitFrameTransformerDelegate( // Pass a callback to ChannelReceive::OnReceivedPayloadData, to be called by // the delegate to receive transformed audio. ChannelReceiveFrameTransformerDelegate::ReceiveFrameCallback - receive_audio_callback = [this](ArrayView packet, + receive_audio_callback = [this](std::span packet, const RTPHeader& header, Timestamp receive_time) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); @@ -566,7 +552,6 @@ ChannelReceive::ChannelReceive( NetEqFactory* neteq_factory, AudioDeviceModule* audio_device_module, Transport* rtcp_send_transport, - uint32_t local_ssrc, uint32_t remote_ssrc, size_t jitter_buffer_max_packets, bool jitter_buffer_fast_playout, @@ -604,14 +589,20 @@ ChannelReceive::ChannelReceive( configuration.receiver_only = true; configuration.outgoing_transport = rtcp_send_transport; configuration.receive_statistics = rtp_receive_statistics_.get(); - configuration.local_media_ssrc = local_ssrc; configuration.rtcp_packet_type_counter_observer = this; configuration.non_sender_rtt_measurement = enable_non_sender_rtt; if (frame_transformer) InitFrameTransformerDelegate(std::move(frame_transformer)); - rtp_rtcp_ = std::make_unique(env_, configuration); + rtp_rtcp_ = + ModuleRtpRtcpImpl2::CreateReceiveModule(env_, configuration, [this] { + if (packet_router_ == nullptr) { + return kFallbackRtcpSsrcForAudio; + } + return packet_router_->SsrcOfFirstSender().value_or( + kFallbackRtcpSsrcForAudio); + }); rtp_rtcp_->SetRemoteSSRC(remote_ssrc_); // Ensure that RTCP is enabled for the created channel. @@ -676,6 +667,7 @@ void ChannelReceive::SetReceiveCodecs( void ChannelReceive::OnRtpPacket(const RtpPacketReceived& packet) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); + env_.event_log().Log(std::make_unique(packet)); Timestamp now = env_.clock().CurrentTime(); last_received_rtp_timestamp_ = packet.Timestamp(); @@ -693,6 +685,13 @@ void ChannelReceive::OnRtpPacket(const RtpPacketReceived& packet) { packet_copy.set_payload_type_frequency(it->second); if (nack_tracker_) { nack_tracker_->UpdateSampleRate(it->second); + nack_tracker_->UpdateLastReceivedPacket(packet.SequenceNumber(), + packet.Timestamp()); + std::vector nack_list = + nack_tracker_->GetNackList(rtp_rtcp_->LastRtt()); + if (!nack_list.empty()) { + rtp_rtcp_->SendNACK(nack_list.data(), nack_list.size()); + } } rtp_receive_statistics_->OnRtpPacket(packet_copy); @@ -736,8 +735,8 @@ void ChannelReceive::ReceivePacket(const uint8_t* packet, const FrameDecryptorInterface::Result decrypt_result = frame_decryptor_->Decrypt( MediaType::AUDIO, csrcs, - /*additional_data=*/ - nullptr, ArrayView(payload, payload_data_length), + /*additional_data=*/{}, + std::span(payload, payload_data_length), decrypted_audio_payload); if (decrypt_result.IsOk()) { @@ -755,7 +754,7 @@ void ChannelReceive::ReceivePacket(const uint8_t* packet, payload_data_length = 0; } - ArrayView payload_data(payload, payload_data_length); + std::span payload_data(payload, payload_data_length); if (frame_transformer_delegate_) { // Asynchronously transform the received payload. After the payload is // transformed, the delegate will call OnReceivedPayloadData to handle it. @@ -774,12 +773,11 @@ void ChannelReceive::ReceivePacket(const uint8_t* packet, void ChannelReceive::ReceivedRTCPPacket(const uint8_t* data, size_t length) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); - // Store playout timestamp for the received RTCP packet UpdatePlayoutTimestamp(true, env_.clock().CurrentTime()); // Deliver RTCP packet to RTP/RTCP module for parsing - rtp_rtcp_->IncomingRtcpPacket(MakeArrayView(data, length)); + rtp_rtcp_->IncomingRtcpPacket(std::span(data, length)); std::optional rtt = rtp_rtcp_->LastRtt(); if (!rtt.has_value()) { @@ -975,11 +973,6 @@ void ChannelReceive::SetFrameDecryptor( frame_decryptor_ = std::move(frame_decryptor); } -void ChannelReceive::OnLocalSsrcChange(uint32_t local_ssrc) { - RTC_DCHECK_RUN_ON(&worker_thread_checker_); - rtp_rtcp_->SetLocalSsrc(local_ssrc); -} - NetworkStatistics ChannelReceive::GetNetworkStatistics( bool get_and_clear_legacy_stats) const { RTC_DCHECK_RUN_ON(&worker_thread_checker_); @@ -1215,7 +1208,6 @@ std::unique_ptr CreateChannelReceive( NetEqFactory* neteq_factory, AudioDeviceModule* audio_device_module, Transport* rtcp_send_transport, - uint32_t local_ssrc, uint32_t remote_ssrc, size_t jitter_buffer_max_packets, bool jitter_buffer_fast_playout, @@ -1226,8 +1218,8 @@ std::unique_ptr CreateChannelReceive( const CryptoOptions& crypto_options, scoped_refptr frame_transformer) { return std::make_unique( - env, neteq_factory, audio_device_module, rtcp_send_transport, local_ssrc, - remote_ssrc, jitter_buffer_max_packets, jitter_buffer_fast_playout, + env, neteq_factory, audio_device_module, rtcp_send_transport, remote_ssrc, + jitter_buffer_max_packets, jitter_buffer_fast_playout, jitter_buffer_min_delay_ms, enable_non_sender_rtt, decoder_factory, std::move(frame_decryptor), crypto_options, std::move(frame_transformer)); } diff --git a/audio/channel_receive.h b/audio/channel_receive.h index 21918786128..dd4188898a9 100644 --- a/audio/channel_receive.h +++ b/audio/channel_receive.h @@ -166,8 +166,6 @@ class ChannelReceiveInterface : public RtpPacketSinkInterface { virtual void SetFrameDecryptor( scoped_refptr frame_decryptor) = 0; - - virtual void OnLocalSsrcChange(uint32_t local_ssrc) = 0; }; std::unique_ptr CreateChannelReceive( @@ -175,7 +173,6 @@ std::unique_ptr CreateChannelReceive( NetEqFactory* neteq_factory, AudioDeviceModule* audio_device_module, Transport* rtcp_send_transport, - uint32_t local_ssrc, uint32_t remote_ssrc, size_t jitter_buffer_max_packets, bool jitter_buffer_fast_playout, diff --git a/audio/channel_receive_frame_transformer_delegate.cc b/audio/channel_receive_frame_transformer_delegate.cc index 887edaa2ee5..b2422d0aaba 100644 --- a/audio/channel_receive_frame_transformer_delegate.cc +++ b/audio/channel_receive_frame_transformer_delegate.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/rtp_headers.h" #include "api/scoped_refptr.h" @@ -34,7 +34,7 @@ namespace webrtc { class TransformableIncomingAudioFrame : public TransformableAudioFrameInterface { public: - TransformableIncomingAudioFrame(ArrayView payload, + TransformableIncomingAudioFrame(std::span payload, const RTPHeader& header, uint32_t ssrc, const std::string& codec_mime_type, @@ -46,9 +46,9 @@ class TransformableIncomingAudioFrame codec_mime_type_(codec_mime_type), receive_time_(receive_time) {} ~TransformableIncomingAudioFrame() override = default; - ArrayView GetData() const override { return payload_; } + std::span GetData() const override { return payload_; } - void SetData(ArrayView data) override { + void SetData(std::span data) override { payload_.SetData(data.data(), data.size()); } @@ -59,8 +59,8 @@ class TransformableIncomingAudioFrame uint8_t GetPayloadType() const override { return header_.payloadType; } uint32_t GetSsrc() const override { return ssrc_; } uint32_t GetTimestamp() const override { return header_.timestamp; } - ArrayView GetContributingSources() const override { - return ArrayView(header_.arrOfCSRCs, header_.numCSRCs); + std::span GetContributingSources() const override { + return std::span(header_.arrOfCSRCs, header_.numCSRCs); } Direction GetDirection() const override { return Direction::kReceiver; } @@ -159,7 +159,7 @@ void ChannelReceiveFrameTransformerDelegate::Reset() { } void ChannelReceiveFrameTransformerDelegate::Transform( - ArrayView packet, + std::span packet, const RTPHeader& header, uint32_t ssrc, const std::string& codec_mime_type, diff --git a/audio/channel_receive_frame_transformer_delegate.h b/audio/channel_receive_frame_transformer_delegate.h index 1b5f04eeee8..a0ba8fd0f2b 100644 --- a/audio/channel_receive_frame_transformer_delegate.h +++ b/audio/channel_receive_frame_transformer_delegate.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/rtp_headers.h" #include "api/scoped_refptr.h" @@ -34,7 +34,7 @@ namespace webrtc { class ChannelReceiveFrameTransformerDelegate : public TransformedFrameCallback { public: using ReceiveFrameCallback = - std::function packet, + std::function packet, const RTPHeader& header, Timestamp receive_time)>; ChannelReceiveFrameTransformerDelegate( @@ -54,7 +54,7 @@ class ChannelReceiveFrameTransformerDelegate : public TransformedFrameCallback { // Delegates the call to FrameTransformerInterface::Transform, to transform // the frame asynchronously. - void Transform(ArrayView packet, + void Transform(std::span packet, const RTPHeader& header, uint32_t ssrc, const std::string& codec_mime_type, diff --git a/audio/channel_receive_frame_transformer_delegate_unittest.cc b/audio/channel_receive_frame_transformer_delegate_unittest.cc index ae9a01cd693..986cddf5eab 100644 --- a/audio/channel_receive_frame_transformer_delegate_unittest.cc +++ b/audio/channel_receive_frame_transformer_delegate_unittest.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/frame_transformer_factory.h" #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" @@ -28,6 +28,7 @@ #include "system_wrappers/include/ntp_time.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { namespace { @@ -44,12 +45,12 @@ class MockChannelReceive { public: MOCK_METHOD(void, ReceiveFrame, - (ArrayView packet, + (std::span packet, const RTPHeader& header, Timestamp receive_time)); ChannelReceiveFrameTransformerDelegate::ReceiveFrameCallback callback() { - return [this](ArrayView packet, const RTPHeader& header, + return [this](std::span packet, const RTPHeader& header, Timestamp receive_time) { ReceiveFrame(packet, header, receive_time); }; @@ -87,7 +88,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, // transformer, it passes it to the channel using the ReceiveFrameCallback. TEST(ChannelReceiveFrameTransformerDelegateTest, TransformRunsChannelReceiveCallback) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); MockChannelReceive mock_channel; @@ -101,7 +102,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, ASSERT_TRUE(callback); const uint8_t data[] = {1, 2, 3, 4}; - ArrayView packet(data, sizeof(data)); + std::span packet(data, sizeof(data)); RTPHeader header; EXPECT_CALL(mock_channel, ReceiveFrame); ON_CALL(*mock_frame_transformer, Transform) @@ -118,7 +119,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, // transformer, it passes it to the channel using the ReceiveFrameCallback. TEST(ChannelReceiveFrameTransformerDelegateTest, TransformRunsChannelReceiveCallbackForSenderFrame) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); MockChannelReceive mock_channel; @@ -132,7 +133,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, ASSERT_TRUE(callback); const uint8_t data[] = {1, 2, 3, 4}; - ArrayView packet(data, sizeof(data)); + std::span packet(data, sizeof(data)); RTPHeader header; EXPECT_CALL(mock_channel, ReceiveFrame(ElementsAre(1, 2, 3, 4), _, kFakeReceiveTimestamp)); @@ -153,7 +154,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, // after resetting the delegate. TEST(ChannelReceiveFrameTransformerDelegateTest, OnTransformedDoesNotRunChannelReceiveCallbackAfterReset) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); MockChannelReceive mock_channel; @@ -169,7 +170,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, TEST(ChannelReceiveFrameTransformerDelegateTest, ShortCircuitingSkipsTransform) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); MockChannelReceive mock_channel; @@ -177,7 +178,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, make_ref_counted( mock_channel.callback(), mock_frame_transformer, Thread::Current()); const uint8_t data[] = {1, 2, 3, 4}; - ArrayView packet(data, sizeof(data)); + std::span packet(data, sizeof(data)); RTPHeader header; delegate->StartShortCircuiting(); @@ -193,7 +194,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, TEST(ChannelReceiveFrameTransformerDelegateTest, AudioLevelAndCaptureTimeAbsentWithoutExtension) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); scoped_refptr delegate = @@ -207,7 +208,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, ASSERT_TRUE(callback); const uint8_t data[] = {1, 2, 3, 4}; - ArrayView packet(data, sizeof(data)); + std::span packet(data, sizeof(data)); RTPHeader header; std::unique_ptr frame; ON_CALL(*mock_frame_transformer, Transform) @@ -230,7 +231,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, TEST(ChannelReceiveFrameTransformerDelegateTest, AudioLevelPresentWithExtension) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); scoped_refptr delegate = @@ -244,7 +245,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, ASSERT_TRUE(callback); const uint8_t data[] = {1, 2, 3, 4}; - ArrayView packet(data, sizeof(data)); + std::span packet(data, sizeof(data)); RTPHeader header; uint8_t audio_level_dbov = 67; AudioLevel audio_level(/*voice_activity=*/true, audio_level_dbov); @@ -268,7 +269,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, TEST(ChannelReceiveFrameTransformerDelegateTest, CaptureTimePresentWithExtension) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); scoped_refptr delegate = @@ -282,7 +283,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, ASSERT_TRUE(callback); const uint8_t data[] = {1, 2, 3, 4}; - ArrayView packet(data, sizeof(data)); + std::span packet(data, sizeof(data)); Timestamp capture_time = Timestamp::Millis(1234); TimeDelta sender_capture_time_offsets[] = {TimeDelta::Millis(56), TimeDelta::Millis(-79)}; @@ -311,7 +312,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, } TEST(ChannelReceiveFrameTransformerDelegateTest, SetAudioLevel) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); scoped_refptr delegate = @@ -320,7 +321,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, SetAudioLevel) { Thread::Current()); delegate->Init(); const uint8_t data[] = {1, 2, 3, 4}; - ArrayView packet(data, sizeof(data)); + std::span packet(data, sizeof(data)); std::unique_ptr frame; ON_CALL(*mock_frame_transformer, Transform) .WillByDefault( @@ -346,7 +347,7 @@ TEST(ChannelReceiveFrameTransformerDelegateTest, SetAudioLevel) { TEST(ChannelReceiveFrameTransformerDelegateTest, ReceivingSenderFrameWithAudioValueSetsAudioLevelInHeader) { - AutoThread main_thread; + test::RunLoop main_thread; scoped_refptr mock_frame_transformer = make_ref_counted>(); MockChannelReceive mock_channel; diff --git a/audio/channel_receive_unittest.cc b/audio/channel_receive_unittest.cc index 057f7d24e1e..83cc6d56e03 100644 --- a/audio/channel_receive_unittest.cc +++ b/audio/channel_receive_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" @@ -29,7 +29,9 @@ #include "api/test/mock_frame_transformer.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/mock/mock_rtc_event_log.h" #include "modules/audio_device/include/mock_audio_device.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/ntp_time_util.h" #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" @@ -52,7 +54,7 @@ using ::testing::NotNull; using ::testing::Return; using ::testing::Test; -constexpr uint32_t kLocalSsrc = 1111; +constexpr uint32_t kLocalSsrc = kFallbackRtcpSsrcForAudio; constexpr uint32_t kRemoteSsrc = 2222; // We run RTP data with 8 kHz PCMA (fixed payload type 8). constexpr char kPayloadName[] = "PCMA"; @@ -71,9 +73,9 @@ class ChannelReceiveTest : public Test { std::unique_ptr CreateTestChannelReceive() { CryptoOptions crypto_options; auto channel = CreateChannelReceive( - CreateEnvironment(time_controller_.GetClock()), + CreateEnvironment(time_controller_.GetClock(), &log_), /* neteq_factory= */ nullptr, audio_device_module_.get(), &transport_, - kLocalSsrc, kRemoteSsrc, + kRemoteSsrc, /* jitter_buffer_max_packets= */ 0, /* jitter_buffer_fast_playout= */ false, /* jitter_buffer_min_delay_ms= */ 0, @@ -140,7 +142,7 @@ class ChannelReceiveTest : public Test { } void HandleGeneratedRtcp(ChannelReceiveInterface& /* channel */, - ArrayView packet) { + std::span packet) { if (packet[1] == rtcp::ReceiverReport::kPacketType) { // Ignore RR, it requires no response } else { @@ -167,6 +169,7 @@ class ChannelReceiveTest : public Test { protected: GlobalSimulatedTimeController time_controller_; + NiceMock log_; scoped_refptr audio_device_module_; scoped_refptr audio_decoder_factory_; MockTransport transport_; @@ -182,7 +185,7 @@ TEST_F(ChannelReceiveTest, ReceiveReportGeneratedOnTime) { bool receiver_report_sent = false; EXPECT_CALL(transport_, SendRtcp) - .WillRepeatedly([&](ArrayView packet, + .WillRepeatedly([&](std::span packet, const PacketOptions& options) { if (packet.size() >= 2 && packet[1] == rtcp::ReceiverReport::kPacketType) { @@ -202,7 +205,7 @@ TEST_F(ChannelReceiveTest, CaptureStartTimeBecomesValid) { EXPECT_CALL(transport_, SendRtcp) .WillRepeatedly( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { HandleGeneratedRtcp(*channel, packet); return true; }); @@ -273,6 +276,15 @@ TEST_F(ChannelReceiveTest, SettingFrameTransformerMultipleTimes) { channel->SetDepacketizerToDecoderFrameTransformer(mock_frame_transformer); } +TEST_F(ChannelReceiveTest, LogsReceivedPacketToEventLog) { + auto channel = CreateTestChannelReceive(); + + RtpPacketReceived packet = CreateRtpPacket(); + + EXPECT_CALL(log_, LogProxy); + channel->OnRtpPacket(packet); +} + } // namespace } // namespace voe } // namespace webrtc diff --git a/audio/channel_send.cc b/audio/channel_send.cc index 6b7d7754daa..6a0f8c43e08 100644 --- a/audio/channel_send.cc +++ b/audio/channel_send.cc @@ -16,13 +16,13 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/call/bitrate_allocation.h" @@ -166,7 +166,7 @@ class ChannelSend : public ChannelSendInterface, bool InputMute() const override; // CSRCs. - void SetCsrcs(ArrayView csrcs) override; + void SetCsrcs(std::span csrcs) override; // Stats. ANAStats GetANAStatistics() const override; @@ -242,9 +242,9 @@ class ChannelSend : public ChannelSendInterface, int32_t SendRtpAudio(AudioFrameType frameType, uint8_t payloadType, uint32_t rtp_timestamp_without_offset, - ArrayView payload, + std::span payload, int64_t absolute_capture_timestamp_ms, - ArrayView csrcs, + std::span csrcs, std::optional audio_level_dbov) RTC_RUN_ON(worker_thread_); @@ -417,9 +417,9 @@ int32_t ChannelSend::SendData(AudioFrameType frameType, int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType, uint8_t payloadType, uint32_t rtp_timestamp_without_offset, - ArrayView payload, + std::span payload, int64_t absolute_capture_timestamp_ms, - ArrayView csrcs, + std::span csrcs, std::optional audio_level_dbov) { // E2EE Custom Audio Frame Encryption (This is optional). // Keep this buffer around for the lifetime of the send call. @@ -440,7 +440,7 @@ int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType, size_t bytes_written = 0; int encrypt_status = frame_encryptor_->Encrypt(MediaType::AUDIO, rtp_rtcp_->SSRC(), - /*additional_data=*/nullptr, payload, + /*additional_data=*/{}, payload, encrypted_audio_payload, &bytes_written); if (encrypt_status != 0) { RTC_DLOG(LS_ERROR) @@ -516,8 +516,10 @@ ChannelSend::ChannelSend( frame_encryptor_(frame_encryptor), crypto_options_(crypto_options), encoder_queue_(env_.task_queue_factory().CreateTaskQueue( - "AudioEncoder", - TaskQueueFactory::Priority::NORMAL)), + "AudioEncoderQueue", + env_.field_trials().IsEnabled("WebRTC-MediaTaskQueuePriorities") + ? TaskQueueFactory::Priority::kAudio + : TaskQueueFactory::Priority::kNormal)), encoder_queue_checker_(encoder_queue_.get()), encoder_format_("x-unknown", 0, 0) { audio_coding_ = AudioCodingModule::Create(); @@ -540,7 +542,7 @@ ChannelSend::ChannelSend( configuration.rtcp_packet_type_counter_observer = this; configuration.local_media_ssrc = ssrc; - rtp_rtcp_ = std::make_unique(env_, configuration); + rtp_rtcp_ = ModuleRtpRtcpImpl2::CreateSendModule(env_, configuration); rtp_rtcp_->SetSendingMediaStatus(false); rtp_sender_audio_ = @@ -692,7 +694,7 @@ void ChannelSend::ReceivedRTCPPacket(const uint8_t* data, size_t length) { RTC_DCHECK_RUN_ON(worker_thread_); // Deliver RTCP packet to RTP/RTCP module for parsing - rtp_rtcp_->IncomingRtcpPacket(MakeArrayView(data, length)); + rtp_rtcp_->IncomingRtcpPacket(std::span(data, length)); std::optional rtt = rtp_rtcp_->LastRtt(); if (!rtt.has_value()) { @@ -718,7 +720,7 @@ bool ChannelSend::InputMute() const { return input_mute_; } -void ChannelSend::SetCsrcs(ArrayView csrcs) { +void ChannelSend::SetCsrcs(std::span csrcs) { RTC_DCHECK_RUN_ON(worker_thread_); std::vector csrcs_copy( csrcs.begin(), @@ -916,7 +918,7 @@ void ChannelSend::ProcessAndEncodeAudio( rms_level_.AnalyzeMuted(length); } else { rms_level_.Analyze( - ArrayView(audio_frame->data(), length)); + std::span(audio_frame->data(), length)); } } previous_frame_muted_ = is_muted; @@ -981,9 +983,9 @@ void ChannelSend::InitFrameTransformerDelegate( ChannelSendFrameTransformerDelegate::SendFrameCallback send_audio_callback = [this](AudioFrameType frameType, uint8_t payloadType, uint32_t rtp_timestamp_with_offset, - ArrayView payload, + std::span payload, int64_t absolute_capture_timestamp_ms, - ArrayView csrcs, + std::span csrcs, std::optional audio_level_dbov) { RTC_DCHECK_RUN_ON(worker_thread_); return SendRtpAudio( diff --git a/audio/channel_send.h b/audio/channel_send.h index 8a1572a1599..0521f271c33 100644 --- a/audio/channel_send.h +++ b/audio/channel_send.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" @@ -97,7 +97,7 @@ class ChannelSendInterface { // Sets the list of CSRCs to be included in the RTP header. If more than // kRtpCsrcSize CSRCs are provided, only the first kRtpCsrcSize elements are // kept. - virtual void SetCsrcs(ArrayView csrcs) = 0; + virtual void SetCsrcs(std::span csrcs) = 0; virtual void ProcessAndEncodeAudio( std::unique_ptr audio_frame) = 0; diff --git a/audio/channel_send_frame_transformer_delegate.cc b/audio/channel_send_frame_transformer_delegate.cc index 044f1c3d31f..57079c36058 100644 --- a/audio/channel_send_frame_transformer_delegate.cc +++ b/audio/channel_send_frame_transformer_delegate.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -91,8 +91,8 @@ class TransformableOutgoingAudioFrame sequence_number_(sequence_number), audio_level_dbov_(audio_level_dbov) {} ~TransformableOutgoingAudioFrame() override = default; - ArrayView GetData() const override { return payload_; } - void SetData(ArrayView data) override { + std::span GetData() const override { return payload_; } + void SetData(std::span data) override { payload_.SetData(data.data(), data.size()); } uint32_t GetTimestamp() const override { return rtp_timestamp_with_offset_; } @@ -110,7 +110,7 @@ class TransformableOutgoingAudioFrame Direction GetDirection() const override { return Direction::kSender; } std::string GetMimeType() const override { return codec_mime_type_; } - ArrayView GetContributingSources() const override { + std::span GetContributingSources() const override { return csrcs_; } diff --git a/audio/channel_send_frame_transformer_delegate.h b/audio/channel_send_frame_transformer_delegate.h index c495af6a69c..678f51fcb31 100644 --- a/audio/channel_send_frame_transformer_delegate.h +++ b/audio/channel_send_frame_transformer_delegate.h @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/scoped_refptr.h" #include "api/task_queue/task_queue_base.h" @@ -40,9 +40,9 @@ class ChannelSendFrameTransformerDelegate : public TransformedFrameCallback { std::function payload, + std::span payload, int64_t absolute_capture_timestamp_ms, - webrtc::ArrayView csrcs, + std::span csrcs, std::optional audio_level_dbov)>; ChannelSendFrameTransformerDelegate( SendFrameCallback send_frame_callback, diff --git a/audio/channel_send_frame_transformer_delegate_unittest.cc b/audio/channel_send_frame_transformer_delegate_unittest.cc index 20acb49c6d3..d5e19c18271 100644 --- a/audio/channel_send_frame_transformer_delegate_unittest.cc +++ b/audio/channel_send_frame_transformer_delegate_unittest.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/scoped_refptr.h" @@ -51,16 +51,16 @@ class MockChannelSend { (AudioFrameType frameType, uint8_t payloadType, uint32_t rtp_timestamp, - ArrayView payload, + std::span payload, int64_t absolute_capture_timestamp_ms, - ArrayView csrcs, + std::span csrcs, std::optional audio_level_dbov)); ChannelSendFrameTransformerDelegate::SendFrameCallback callback() { return [this](AudioFrameType frameType, uint8_t payloadType, - uint32_t rtp_timestamp, ArrayView payload, + uint32_t rtp_timestamp, std::span payload, int64_t absolute_capture_timestamp_ms, - ArrayView csrcs, + std::span csrcs, std::optional audio_level_dbov) { return SendFrame(frameType, payloadType, rtp_timestamp, payload, absolute_capture_timestamp_ms, csrcs, audio_level_dbov); @@ -73,7 +73,7 @@ std::unique_ptr CreateMockReceiverFrame( std::optional audio_level_dbov) { std::unique_ptr mock_frame = std::make_unique>(); - ArrayView payload(mock_data); + std::span payload(mock_data); ON_CALL(*mock_frame, GetData).WillByDefault(Return(payload)); ON_CALL(*mock_frame, GetPayloadType).WillByDefault(Return(0)); ON_CALL(*mock_frame, GetDirection) diff --git a/audio/channel_send_unittest.cc b/audio/channel_send_unittest.cc index 4835056070f..e777cf6befe 100644 --- a/audio/channel_send_unittest.cc +++ b/audio/channel_send_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_encoder_factory.h" @@ -155,7 +155,7 @@ TEST_F(ChannelSendTest, IncreaseRtpTimestampByPauseDuration) { channel_->StartSend(); uint32_t timestamp; int sent_packets = 0; - auto send_rtp = [&](ArrayView data, + auto send_rtp = [&](std::span data, const PacketOptions& /* options */) { ++sent_packets; RtpPacketReceived packet; @@ -190,7 +190,7 @@ TEST_F(ChannelSendTest, FrameTransformerGetsCorrectTimestamp) { EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback); std::optional sent_timestamp; - auto send_rtp = [&](ArrayView data, + auto send_rtp = [&](std::span data, const PacketOptions& /* options */) { RtpPacketReceived packet; packet.Parse(data); @@ -239,7 +239,7 @@ TEST_F(ChannelSendTest, AudioLevelsAttachedToCorrectTransformedFrame) { EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback); std::vector sent_audio_levels; - auto send_rtp = [&](ArrayView data, + auto send_rtp = [&](std::span data, const PacketOptions& /* options */) { RtpPacketReceived packet(&extension_manager); packet.Parse(data); @@ -303,7 +303,7 @@ TEST_F(ChannelSendTest, AudioLevelsAttachedToInsertedTransformedFrame) { EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback); std::optional sent_audio_level; - auto send_rtp = [&](ArrayView data, + auto send_rtp = [&](std::span data, const PacketOptions& /* options */) { RtpPacketReceived packet(&extension_manager); packet.Parse(data); @@ -323,7 +323,7 @@ TEST_F(ChannelSendTest, AudioLevelsAttachedToInsertedTransformedFrame) { ON_CALL(*mock_frame, AudioLevel()).WillByDefault(Return(audio_level)); uint8_t payload[10]; ON_CALL(*mock_frame, GetData()) - .WillByDefault(Return(ArrayView(&payload[0], 10))); + .WillByDefault(Return(std::span(&payload[0], 10))); EXPECT_THAT(WaitUntil([&] { return callback; }, IsTrue()), IsRtcOk()); callback->OnTransformedFrame(std::move(mock_frame)); @@ -415,7 +415,7 @@ TEST_F(ChannelSendTest, ConfiguredCsrcsAreIncludedInRtpPackets) { channel_->SetCsrcs(expected_csrcs); std::vector csrcs; - auto send_rtp = [&](ArrayView data, + auto send_rtp = [&](std::span data, const PacketOptions& /* options */) { RtpPacketReceived packet; packet.Parse(data); @@ -500,7 +500,7 @@ TEST_F(ChannelSendTest, FrameTransformerTakesPrecedenceOverSetCsrcs) { channel_->StartSend(); std::vector sent_csrcs; - auto send_rtp = [&](ArrayView data, + auto send_rtp = [&](std::span data, const PacketOptions& /* options */) { RtpPacketReceived packet; packet.Parse(data); diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h index 8ac9c33732e..5a2adc50c5e 100644 --- a/audio/mock_voe_channel_proxy.h +++ b/audio/mock_voe_channel_proxy.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_encoder.h" @@ -128,7 +128,6 @@ class MockChannelReceive : public voe::ChannelReceiveInterface { SetFrameDecryptor, (webrtc::scoped_refptr frame_decryptor), (override)); - MOCK_METHOD(void, OnLocalSsrcChange, (uint32_t local_ssrc), (override)); MOCK_METHOD(void, SetMaximumBufferPackets, (size_t max_packets), (override)); MOCK_METHOD(void, SetFastAccelerate, (bool enable), (override)); }; @@ -210,7 +209,7 @@ class MockChannelSend : public voe::ChannelSendInterface { RegisterPacketOverhead, (int packet_byte_overhead), (override)); - MOCK_METHOD(void, SetCsrcs, (ArrayView csrcs), (override)); + MOCK_METHOD(void, SetCsrcs, (std::span csrcs), (override)); }; } // namespace test } // namespace webrtc diff --git a/audio/nack_tracker.cc b/audio/nack_tracker.cc index d0e51c39a5d..9878cd02924 100644 --- a/audio/nack_tracker.cc +++ b/audio/nack_tracker.cc @@ -208,19 +208,19 @@ int64_t NackTracker::TimeToPlay(uint32_t timestamp) const { } // We don't erase elements with time-to-play shorter than round-trip-time. -std::vector NackTracker::GetNackList(int64_t round_trip_time_ms) { - RTC_DCHECK_GE(round_trip_time_ms, 0); +std::vector NackTracker::GetNackList( + std::optional round_trip_time) { std::vector sequence_numbers; - if (round_trip_time_ms == 0) { + if (!round_trip_time.has_value()) { if (config_.require_valid_rtt) { - return sequence_numbers; + return {}; } else { - round_trip_time_ms = config_.default_rtt_ms; + round_trip_time = TimeDelta::Millis(config_.default_rtt_ms); } } for (NackList::const_iterator it = nack_list_.begin(); it != nack_list_.end(); ++it) { - if (Nack(it->second, round_trip_time_ms)) { + if (Nack(it->second, round_trip_time->ms())) { sequence_numbers.push_back(it->first); } } diff --git a/audio/nack_tracker.h b/audio/nack_tracker.h index 390704273c6..81772eaaafd 100644 --- a/audio/nack_tracker.h +++ b/audio/nack_tracker.h @@ -78,11 +78,11 @@ class NackTracker { void UpdateLastReceivedPacket(uint16_t sequence_number, uint32_t timestamp); // Get a list of "missing" packets which have expected time-to-play larger - // than the given round-trip-time (in milliseconds). + // than the given round-trip-time. // Note: Late packets are not included. // Calling this method multiple times may give different results, since the // internal nack list may get flushed if never_nack_multiple_times_ is true. - std::vector GetNackList(int64_t round_trip_time_ms); + std::vector GetNackList(std::optional round_trip_time); // Reset to default values. The NACK list is cleared. // `max_nack_list_size_` preserves its value. @@ -115,7 +115,7 @@ class NackTracker { // If set, the maximum nack delay will be fixed and compared to the latest // received packet instead of using the time to play estimate and the loss // rate. - std::optional fixed_delay; + std::optional fixed_delay = TimeDelta::Seconds(1); }; struct NackElement { diff --git a/audio/nack_tracker_unittest.cc b/audio/nack_tracker_unittest.cc index 299de8472b1..1ca22f70bd7 100644 --- a/audio/nack_tracker_unittest.cc +++ b/audio/nack_tracker_unittest.cc @@ -14,9 +14,11 @@ #include #include #include +#include #include #include "api/field_trials.h" +#include "api/units/time_delta.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" @@ -30,7 +32,7 @@ using ::testing::IsEmpty; constexpr int kSampleRateHz = 16000; constexpr int kPacketSizeMs = 30; constexpr uint32_t kTimestampIncrement = 480; // 30 ms. -constexpr int64_t kShortRoundTripTimeMs = 1; +constexpr TimeDelta kShortRoundTripTime = TimeDelta::Zero(); bool IsNackListCorrect(const std::vector& nack_list, const uint16_t* lost_sequence_numbers, @@ -69,10 +71,10 @@ TEST(NackTrackerTest, EmptyListWhenNoPacketLoss) { std::vector nack_list; for (int n = 0; n < 100; n++) { nack.UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); seq_num++; timestamp += kTimestampIncrement; - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list.empty()); } } @@ -99,7 +101,7 @@ TEST(NackTrackerTest, LatePacketsMovedToNackThenNackListDoesNotChange) { std::vector nack_list; nack.UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list.empty()); seq_num = sequence_num_lost_packets[kNumAllLostPackets - 1] + 1; @@ -107,16 +109,16 @@ TEST(NackTrackerTest, LatePacketsMovedToNackThenNackListDoesNotChange) { int num_lost_packets = std::max(0, kNumAllLostPackets); nack.UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(IsNackListCorrect(nack_list, sequence_num_lost_packets, num_lost_packets)); seq_num++; timestamp += kTimestampIncrement; num_lost_packets++; - for (int n = 0; n < 100; ++n) { + for (int n = 0; n < 20; ++n) { nack.UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(IsNackListCorrect(nack_list, sequence_num_lost_packets, kNumAllLostPackets)); seq_num++; @@ -145,7 +147,7 @@ TEST(NackTrackerTest, ArrivedPacketsAreRemovedFromNackList) { uint32_t timestamp = 0; nack.UpdateLastReceivedPacket(seq_num, timestamp); - std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); + std::vector nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list.empty()); size_t index_retransmitted_rtp = 0; @@ -162,7 +164,7 @@ TEST(NackTrackerTest, ArrivedPacketsAreRemovedFromNackList) { num_lost_packets--; nack.UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(IsNackListCorrect( nack_list, &sequence_num_lost_packets[index_retransmitted_rtp], num_lost_packets)); @@ -176,7 +178,7 @@ TEST(NackTrackerTest, ArrivedPacketsAreRemovedFromNackList) { index_retransmitted_rtp++; timestamp_retransmitted_rtp += kTimestampIncrement; - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(IsNackListCorrect( nack_list, &sequence_num_lost_packets[index_retransmitted_rtp], num_lost_packets - 1)); // One less lost packet in the list. @@ -251,7 +253,8 @@ TEST(NackTrackerTest, EstimateTimestampAndTimeToPlay) { TEST(NackTrackerTest, MissingPacketsPriorToLastDecodedRtpShouldNotBeInNackList) { - FieldTrials field_trials = CreateTestFieldTrials(); + FieldTrials field_trials = CreateTestFieldTrials( + "WebRTC-Audio-NetEqNackTrackerConfig/fixed_delay:/"); for (int m = 0; m < 2; ++m) { uint16_t seq_num_offset = (m == 0) ? 0 : 65531; // Wrap around if `m` is 1. NackTracker nack(field_trials); @@ -272,21 +275,21 @@ TEST(NackTrackerTest, seq_num * kTimestampIncrement); const size_t kExpectedListSize = kNumLostPackets; - std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); + std::vector nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_EQ(kExpectedListSize, nack_list.size()); for (int k = 0; k < 2; ++k) { // Decoding of the first and the second arrived packets. for (int n = 0; n < kPacketSizeMs / 10; ++n) { nack.UpdateLastDecodedPacket(k * kTimestampIncrement); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_EQ(kExpectedListSize, nack_list.size()); } } // Decoding of the last received packet. nack.UpdateLastDecodedPacket(seq_num * kTimestampIncrement); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list.empty()); // Make sure list of late packets is also empty. To check that, push few @@ -296,7 +299,7 @@ TEST(NackTrackerTest, seq_num++; nack.UpdateLastReceivedPacket(seq_num_offset + seq_num, seq_num * kTimestampIncrement); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list.empty()); } } @@ -319,11 +322,11 @@ TEST(NackTrackerTest, Reset) { nack.UpdateLastReceivedPacket(seq_num, seq_num * kTimestampIncrement); const size_t kExpectedListSize = kNumLostPackets; - std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); + std::vector nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_EQ(kExpectedListSize, nack_list.size()); nack.Reset(); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list.empty()); } @@ -347,7 +350,7 @@ TEST(NackTrackerTest, ListSizeAppliedFromBeginning) { timestamp += (num_lost_packets + 1) * kTimestampIncrement; nack.UpdateLastReceivedPacket(seq_num, timestamp); - std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); + std::vector nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_EQ(kNackListSize, nack_list.size()); } } @@ -377,12 +380,12 @@ TEST(NackTrackerTest, ChangeOfListSizeAppliedAndOldElementsRemoved) { nack.UpdateLastReceivedPacket(seq_num, timestamp); size_t expected_size = num_lost_packets; - std::vector nack_list = nack.GetNackList(kShortRoundTripTimeMs); + std::vector nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_EQ(expected_size, nack_list.size()); nack.SetMaxNackListSize(kNackListSize); expected_size = kNackListSize; - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(IsNackListCorrect( nack_list, &seq_num_lost[num_lost_packets - kNackListSize], expected_size)); @@ -393,7 +396,7 @@ TEST(NackTrackerTest, ChangeOfListSizeAppliedAndOldElementsRemoved) { timestamp += kTimestampIncrement; nack.UpdateLastReceivedPacket(seq_num, timestamp); --expected_size; - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(IsNackListCorrect( nack_list, &seq_num_lost[num_lost_packets - kNackListSize + n], expected_size)); @@ -403,13 +406,14 @@ TEST(NackTrackerTest, ChangeOfListSizeAppliedAndOldElementsRemoved) { ++seq_num; timestamp += kTimestampIncrement; nack.UpdateLastReceivedPacket(seq_num, timestamp); - nack_list = nack.GetNackList(kShortRoundTripTimeMs); + nack_list = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list.empty()); } } TEST(NackTrackerTest, RoudTripTimeIsApplied) { - FieldTrials field_trials = CreateTestFieldTrials(); + FieldTrials field_trials = CreateTestFieldTrials( + "WebRTC-Audio-NetEqNackTrackerConfig/fixed_delay:/"); const int kNackListSize = 200; NackTracker nack(field_trials); nack.UpdateSampleRate(kSampleRateHz); @@ -432,7 +436,7 @@ TEST(NackTrackerTest, RoudTripTimeIsApplied) { // sequence number: 1, 2, 3, 4, 5 // time-to-play: 20, 50, 80, 110, 140 // - std::vector nack_list = nack.GetNackList(100); + std::vector nack_list = nack.GetNackList(TimeDelta::Millis(100)); ASSERT_EQ(2u, nack_list.size()); EXPECT_EQ(4, nack_list[0]); EXPECT_EQ(5, nack_list[1]); @@ -460,13 +464,13 @@ TEST(NackTrackerTest, DoNotNackMultipleTimes) { timestamp += (1 + kNumLostPackets) * kTimestampIncrement; nack.UpdateLastReceivedPacket(seq_num, timestamp); - std::vector nack_list = nack.GetNackList(10); + std::vector nack_list = nack.GetNackList(kShortRoundTripTime); ASSERT_EQ(3u, nack_list.size()); EXPECT_EQ(1, nack_list[0]); EXPECT_EQ(2, nack_list[1]); EXPECT_EQ(3, nack_list[2]); // When we get the nack list again, it should be empty. - std::vector nack_list2 = nack.GetNackList(10); + std::vector nack_list2 = nack.GetNackList(kShortRoundTripTime); EXPECT_TRUE(nack_list2.empty()); } @@ -507,16 +511,16 @@ TEST(NackTrackerTest, DoNotNackAfterDtx) { uint16_t seq_num = 0; uint32_t timestamp = 0x87654321; nack.UpdateLastReceivedPacket(seq_num, timestamp); - EXPECT_TRUE(nack.GetNackList(0).empty()); + EXPECT_TRUE(nack.GetNackList(std::nullopt).empty()); constexpr int kDtxPeriod = 400; nack.UpdateLastReceivedPacket(seq_num + 2, timestamp + kDtxPeriod * kSampleRateHz / 1000); - EXPECT_TRUE(nack.GetNackList(0).empty()); + EXPECT_TRUE(nack.GetNackList(std::nullopt).empty()); } TEST(NackTrackerTest, DoNotNackIfLossRateIsTooHigh) { FieldTrials field_trials = CreateTestFieldTrials( - "WebRTC-Audio-NetEqNackTrackerConfig/max_loss_rate:0.4/"); + "WebRTC-Audio-NetEqNackTrackerConfig/max_loss_rate:0.4,fixed_delay:/"); const int kNackListSize = 200; NackTracker nack(field_trials); nack.UpdateSampleRate(kSampleRateHz); @@ -536,7 +540,7 @@ TEST(NackTrackerTest, DoNotNackIfLossRateIsTooHigh) { } // Expect 50% loss rate which is higher that the configured maximum 40%. EXPECT_NEAR(nack.GetPacketLossRateForTest(), 1 << 29, (1 << 30) / 100); - EXPECT_TRUE(nack.GetNackList(0).empty()); + EXPECT_TRUE(nack.GetNackList(std::nullopt).empty()); } TEST(NackTrackerTest, OnlyNackIfRttIsValid) { @@ -558,13 +562,12 @@ TEST(NackTrackerTest, OnlyNackIfRttIsValid) { add_packet(true); add_packet(false); add_packet(true); - EXPECT_TRUE(nack.GetNackList(0).empty()); - EXPECT_FALSE(nack.GetNackList(10).empty()); + EXPECT_TRUE(nack.GetNackList(std::nullopt).empty()); + EXPECT_FALSE(nack.GetNackList(kShortRoundTripTime).empty()); } TEST(NackTrackerTest, FixedDelayMode) { - FieldTrials field_trials = CreateTestFieldTrials( - "WebRTC-Audio-NetEqNackTrackerConfig/fixed_delay:1s/"); + FieldTrials field_trials = CreateTestFieldTrials(); const int kNackListSize = 200; NackTracker nack(field_trials); nack.UpdateSampleRate(kSampleRateHz); @@ -574,18 +577,18 @@ TEST(NackTrackerTest, FixedDelayMode) { nack.UpdateLastReceivedPacket(seq_num, timestamp); nack.UpdateLastReceivedPacket(seq_num + 2, timestamp + 2 * kTimestampIncrement); - EXPECT_THAT(nack.GetNackList(10), ElementsAre(seq_num + 1)); + EXPECT_THAT(nack.GetNackList(kShortRoundTripTime), ElementsAre(seq_num + 1)); // The RTT is larger than the fixed delay, so no packets should be NACKed. - EXPECT_THAT(nack.GetNackList(1000), IsEmpty()); + EXPECT_THAT(nack.GetNackList(TimeDelta::Seconds(1)), IsEmpty()); // Decoding a packet should not affect the NACK list in fixed delay mode. nack.UpdateLastDecodedPacket(timestamp + 2 * kTimestampIncrement); - EXPECT_THAT(nack.GetNackList(10), ElementsAre(seq_num + 1)); + EXPECT_THAT(nack.GetNackList(kShortRoundTripTime), ElementsAre(seq_num + 1)); // Update the latest received packet such that the lost packet is older than // the fixed delay. nack.UpdateLastReceivedPacket( seq_num + 3, timestamp + 2 * kTimestampIncrement + kSampleRateHz); - EXPECT_THAT(nack.GetNackList(10), IsEmpty()); + EXPECT_THAT(nack.GetNackList(kShortRoundTripTime), IsEmpty()); } } // namespace webrtc diff --git a/audio/voip/BUILD.gn b/audio/voip/BUILD.gn index 5780fd2c926..dd5e127e138 100644 --- a/audio/voip/BUILD.gn +++ b/audio/voip/BUILD.gn @@ -16,7 +16,6 @@ rtc_library("voip_core") { deps = [ ":audio_channel", "..:audio", - "../../api:array_view", "../../api:make_ref_counted", "../../api:scoped_refptr", "../../api:transport_api", @@ -43,7 +42,6 @@ rtc_library("audio_channel") { deps = [ ":audio_egress", ":audio_ingress", - "../../api:array_view", "../../api:ref_count", "../../api:rtp_headers", "../../api:scoped_refptr", @@ -67,7 +65,6 @@ rtc_library("audio_ingress") { ] deps = [ "..:audio", - "../../api:array_view", "../../api:scoped_refptr", "../../api/audio:audio_mixer_api", "../../api/audio_codecs:audio_codecs_api", @@ -97,7 +94,6 @@ rtc_library("audio_egress") { ] deps = [ "..:audio", - "../../api:array_view", "../../api:sequence_checker", "../../api/audio:audio_frame_api", "../../api/audio_codecs:audio_codecs_api", diff --git a/audio/voip/audio_channel.cc b/audio/voip/audio_channel.cc index e564f9f59ff..348b76e64dd 100644 --- a/audio/voip/audio_channel.cc +++ b/audio/voip/audio_channel.cc @@ -55,7 +55,7 @@ AudioChannel::AudioChannel(const Environment& env, rtp_config.outgoing_transport = transport; rtp_config.local_media_ssrc = local_ssrc; - rtp_rtcp_ = std::make_unique(env, rtp_config); + rtp_rtcp_ = ModuleRtpRtcpImpl2::CreateSendModule(env, rtp_config); rtp_rtcp_->SetSendingMediaStatus(false); rtp_rtcp_->SetRTCPStatus(RtcpMode::kCompound); diff --git a/audio/voip/audio_channel.h b/audio/voip/audio_channel.h index b670b310f33..268e15eb768 100644 --- a/audio/voip/audio_channel.h +++ b/audio/voip/audio_channel.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_encoder.h" @@ -83,10 +83,10 @@ class AudioChannel : public RefCountInterface { // APIs relayed to AudioIngress. bool IsPlaying() const { return ingress_->IsPlaying(); } - void ReceivedRTPPacket(ArrayView rtp_packet) { + void ReceivedRTPPacket(std::span rtp_packet) { ingress_->ReceivedRTPPacket(rtp_packet); } - void ReceivedRTCPPacket(ArrayView rtcp_packet) { + void ReceivedRTCPPacket(std::span rtcp_packet) { ingress_->ReceivedRTCPPacket(rtcp_packet); } void SetReceiveCodecs(const std::map& codecs) { diff --git a/audio/voip/audio_egress.cc b/audio/voip/audio_egress.cc index 81d5f60f043..36ffe21d442 100644 --- a/audio/voip/audio_egress.cc +++ b/audio/voip/audio_egress.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" @@ -38,7 +38,7 @@ AudioEgress::AudioEgress(const Environment& env, RtpRtcpInterface* rtp_rtcp) audio_coding_(AudioCodingModule::Create()), encoder_queue_(env.task_queue_factory().CreateTaskQueue( "AudioEncoder", - TaskQueueFactory::Priority::NORMAL)), + TaskQueueFactory::Priority::kNormal)), encoder_queue_checker_(encoder_queue_.get()) { audio_coding_->RegisterTransportCallback(this); } @@ -132,7 +132,7 @@ int32_t AudioEgress::SendData(AudioFrameType frame_type, size_t payload_size) { RTC_DCHECK_RUN_ON(&encoder_queue_checker_); - ArrayView payload(payload_data, payload_size); + std::span payload(payload_data, payload_size); // Currently we don't get a capture time from downstream modules (ADM, // AudioTransportImpl). diff --git a/audio/voip/audio_ingress.cc b/audio/voip/audio_ingress.cc index b4525a18cd5..1e2fa34838e 100644 --- a/audio/voip/audio_ingress.cc +++ b/audio/voip/audio_ingress.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_format.h" @@ -160,7 +160,7 @@ void AudioIngress::SetReceiveCodecs( neteq_->SetCodecs(codecs); } -void AudioIngress::ReceivedRTPPacket(ArrayView rtp_packet) { +void AudioIngress::ReceivedRTPPacket(std::span rtp_packet) { RtpPacketReceived rtp_packet_received; rtp_packet_received.Parse(rtp_packet.data(), rtp_packet.size()); @@ -204,7 +204,7 @@ void AudioIngress::ReceivedRTPPacket(ArrayView rtp_packet) { const uint8_t* payload = rtp_packet_received.data() + header.headerLength; size_t payload_length = packet_length - header.headerLength; size_t payload_data_length = payload_length - header.paddingLength; - auto data_view = ArrayView(payload, payload_data_length); + auto data_view = std::span(payload, payload_data_length); // Push the incoming payload (parsed and ready for decoding) into NetEq. if (!data_view.empty()) { @@ -217,7 +217,7 @@ void AudioIngress::ReceivedRTPPacket(ArrayView rtp_packet) { } } -void AudioIngress::ReceivedRTCPPacket(ArrayView rtcp_packet) { +void AudioIngress::ReceivedRTCPPacket(std::span rtcp_packet) { rtcp::CommonHeader rtcp_header; if (rtcp_header.Parse(rtcp_packet.data(), rtcp_packet.size()) && (rtcp_header.type() == rtcp::SenderReport::kPacketType || diff --git a/audio/voip/audio_ingress.h b/audio/voip/audio_ingress.h index e6d9836fd4d..a8ec34f02d2 100644 --- a/audio/voip/audio_ingress.h +++ b/audio/voip/audio_ingress.h @@ -17,8 +17,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_format.h" @@ -72,8 +72,8 @@ class AudioIngress : public AudioMixer::Source { void SetReceiveCodecs(const std::map& codecs); // APIs to handle received RTP/RTCP packets from caller. - void ReceivedRTPPacket(ArrayView rtp_packet); - void ReceivedRTCPPacket(ArrayView rtcp_packet); + void ReceivedRTPPacket(std::span rtp_packet); + void ReceivedRTCPPacket(std::span rtcp_packet); // See comments on LevelFullRange, TotalEnergy, TotalDuration from // audio/audio_level.h. diff --git a/audio/voip/test/BUILD.gn b/audio/voip/test/BUILD.gn index c0efeb6c8ae..0c1acf0d68d 100644 --- a/audio/voip/test/BUILD.gn +++ b/audio/voip/test/BUILD.gn @@ -49,7 +49,6 @@ if (rtc_include_tests) { deps = [ ":mock_task_queue", "..:audio_channel", - "../../../api:array_view", "../../../api:make_ref_counted", "../../../api:scoped_refptr", "../../../api/audio:audio_frame_api", @@ -76,7 +75,6 @@ if (rtc_include_tests) { deps = [ "..:audio_egress", "..:audio_ingress", - "../../../api:array_view", "../../../api:rtp_headers", "../../../api:scoped_refptr", "../../../api/audio:audio_frame_api", @@ -102,7 +100,6 @@ if (rtc_include_tests) { sources = [ "audio_egress_unittest.cc" ] deps = [ "..:audio_egress", - "../../../api:array_view", "../../../api:rtp_headers", "../../../api:scoped_refptr", "../../../api:transport_api", diff --git a/audio/voip/test/audio_channel_unittest.cc b/audio/voip/test/audio_channel_unittest.cc index 8b4c32f273d..a707e2d5ad0 100644 --- a/audio/voip/test/audio_channel_unittest.cc +++ b/audio/voip/test/audio_channel_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/functional/any_invocable.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_decoder_factory.h" @@ -120,7 +120,7 @@ class AudioChannelTest : public ::testing::Test { // Resulted RTP packet is looped back into AudioChannel and gets decoded into // audio frame to see if it has some signal to indicate its validity. TEST_F(AudioChannelTest, PlayRtpByLocalLoop) { - auto loop_rtp = [&](ArrayView packet, Unused) { + auto loop_rtp = [&](std::span packet, Unused) { audio_channel_->ReceivedRTPPacket(packet); return true; }; @@ -145,7 +145,7 @@ TEST_F(AudioChannelTest, PlayRtpByLocalLoop) { // Validate assigned local SSRC is resulted in RTP packet. TEST_F(AudioChannelTest, VerifyLocalSsrcAsAssigned) { RtpPacketReceived rtp; - auto loop_rtp = [&](ArrayView packet, Unused) { + auto loop_rtp = [&](std::span packet, Unused) { rtp.Parse(packet); return true; }; @@ -160,7 +160,7 @@ TEST_F(AudioChannelTest, VerifyLocalSsrcAsAssigned) { // Check metrics after processing an RTP packet. TEST_F(AudioChannelTest, TestIngressStatistics) { - auto loop_rtp = [&](ArrayView packet, Unused) { + auto loop_rtp = [&](std::span packet, Unused) { audio_channel_->ReceivedRTPPacket(packet); return true; }; @@ -237,11 +237,11 @@ TEST_F(AudioChannelTest, TestIngressStatistics) { // Check ChannelStatistics metric after processing RTP and RTCP packets. TEST_F(AudioChannelTest, TestChannelStatistics) { - auto loop_rtp = [&](ArrayView packet, Unused) { + auto loop_rtp = [&](std::span packet, Unused) { audio_channel_->ReceivedRTPPacket(packet); return true; }; - auto loop_rtcp = [&](ArrayView packet, Unused) { + auto loop_rtcp = [&](std::span packet, Unused) { audio_channel_->ReceivedRTCPPacket(packet); return true; }; @@ -306,7 +306,7 @@ TEST_F(AudioChannelTest, RttIsAvailableAfterChangeOfRemoteSsrc) { auto send_recv_rtp = [&](scoped_refptr rtp_sender, scoped_refptr rtp_receiver) { // Setup routing logic via transport_. - auto route_rtp = [&](ArrayView packet, Unused) { + auto route_rtp = [&](std::span packet, Unused) { rtp_receiver->ReceivedRTPPacket(packet); return true; }; @@ -328,7 +328,7 @@ TEST_F(AudioChannelTest, RttIsAvailableAfterChangeOfRemoteSsrc) { auto send_recv_rtcp = [&](scoped_refptr rtcp_sender, scoped_refptr rtcp_receiver) { // Setup routing logic via transport_. - auto route_rtcp = [&](ArrayView packet, Unused) { + auto route_rtcp = [&](std::span packet, Unused) { rtcp_receiver->ReceivedRTCPPacket(packet); return true; }; diff --git a/audio/voip/test/audio_egress_unittest.cc b/audio/voip/test/audio_egress_unittest.cc index 911278223ec..5cf9a2aa437 100644 --- a/audio/voip/test/audio_egress_unittest.cc +++ b/audio/voip/test/audio_egress_unittest.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/audio_format.h" @@ -51,7 +51,7 @@ std::unique_ptr CreateRtpStack(const Environment& env, rtp_config.rtcp_report_interval_ms = 5000; rtp_config.outgoing_transport = transport; rtp_config.local_media_ssrc = remote_ssrc; - auto rtp_rtcp = std::make_unique(env, rtp_config); + auto rtp_rtcp = ModuleRtpRtcpImpl2::CreateSendModule(env, rtp_config); rtp_rtcp->SetSendingMediaStatus(false); rtp_rtcp->SetRTCPStatus(RtcpMode::kCompound); return rtp_rtcp; @@ -131,7 +131,7 @@ TEST_F(AudioEgressTest, ProcessAudioWithMute) { Event event; int rtp_count = 0; RtpPacketReceived rtp; - auto rtp_sent = [&](ArrayView packet, Unused) { + auto rtp_sent = [&](std::span packet, Unused) { rtp.Parse(packet); if (++rtp_count == kExpected) { event.Set(); @@ -169,7 +169,7 @@ TEST_F(AudioEgressTest, ProcessAudioWithSineWave) { Event event; int rtp_count = 0; RtpPacketReceived rtp; - auto rtp_sent = [&](ArrayView packet, Unused) { + auto rtp_sent = [&](std::span packet, Unused) { rtp.Parse(packet); if (++rtp_count == kExpected) { event.Set(); @@ -204,7 +204,7 @@ TEST_F(AudioEgressTest, SkipAudioEncodingAfterStopSend) { constexpr int kExpected = 10; Event event; int rtp_count = 0; - auto rtp_sent = [&](ArrayView /* packet */, Unused) { + auto rtp_sent = [&](std::span /* packet */, Unused) { if (++rtp_count == kExpected) { event.Set(); } @@ -278,7 +278,7 @@ TEST_F(AudioEgressTest, SendDTMF) { // It's possible that we may have actual audio RTP packets along with // DTMF packtets. We are only interested in the exact number of DTMF // packets rtp stack is emitting. - auto rtp_sent = [&](ArrayView packet, Unused) { + auto rtp_sent = [&](std::span packet, Unused) { RtpPacketReceived rtp; rtp.Parse(packet); if (is_dtmf(rtp) && ++dtmf_count == kExpected) { @@ -305,7 +305,7 @@ TEST_F(AudioEgressTest, TestAudioInputLevelAndEnergyDuration) { constexpr int kExpected = 6; Event event; int rtp_count = 0; - auto rtp_sent = [&](ArrayView /* packet */, Unused) { + auto rtp_sent = [&](std::span /* packet */, Unused) { if (++rtp_count == kExpected) { event.Set(); } diff --git a/audio/voip/test/audio_ingress_unittest.cc b/audio/voip/test/audio_ingress_unittest.cc index f867c4cd238..23e39aefe5b 100644 --- a/audio/voip/test/audio_ingress_unittest.cc +++ b/audio/voip/test/audio_ingress_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_decoder_factory.h" @@ -61,7 +61,7 @@ class AudioIngressTest : public ::testing::Test { rtp_config.rtcp_report_interval_ms = 5000; rtp_config.outgoing_transport = &transport_; rtp_config.local_media_ssrc = 0xdeadc0de; - rtp_rtcp_ = std::make_unique(env_, rtp_config); + rtp_rtcp_ = ModuleRtpRtcpImpl2::CreateSendModule(env_, rtp_config); rtp_rtcp_->SetSendingMediaStatus(false); rtp_rtcp_->SetRTCPStatus(RtcpMode::kCompound); @@ -125,7 +125,7 @@ TEST_F(AudioIngressTest, PlayingAfterStartAndStop) { TEST_F(AudioIngressTest, GetAudioFrameAfterRtpReceived) { Event event; - auto handle_rtp = [&](ArrayView packet, Unused) { + auto handle_rtp = [&](std::span packet, Unused) { ingress_->ReceivedRTPPacket(packet); event.Set(); return true; @@ -155,7 +155,7 @@ TEST_F(AudioIngressTest, TestSpeechOutputLevelAndEnergyDuration) { constexpr int kNumRtp = 6; int rtp_count = 0; Event event; - auto handle_rtp = [&](ArrayView packet, Unused) { + auto handle_rtp = [&](std::span packet, Unused) { ingress_->ReceivedRTPPacket(packet); if (++rtp_count == kNumRtp) { event.Set(); @@ -186,7 +186,7 @@ TEST_F(AudioIngressTest, TestSpeechOutputLevelAndEnergyDuration) { TEST_F(AudioIngressTest, PreferredSampleRate) { Event event; - auto handle_rtp = [&](ArrayView packet, Unused) { + auto handle_rtp = [&](std::span packet, Unused) { ingress_->ReceivedRTPPacket(packet); event.Set(); return true; @@ -215,7 +215,7 @@ TEST_F(AudioIngressTest, GetMutedAudioFrameAfterRtpReceivedAndStopPlay) { constexpr int kNumRtp = 6; int rtp_count = 0; Event event; - auto handle_rtp = [&](ArrayView packet, Unused) { + auto handle_rtp = [&](std::span packet, Unused) { ingress_->ReceivedRTPPacket(packet); if (++rtp_count == kNumRtp) { event.Set(); diff --git a/audio/voip/voip_core.cc b/audio/voip/voip_core.cc index cbda72e4e1a..406c4b65e46 100644 --- a/audio/voip/voip_core.cc +++ b/audio/voip/voip_core.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/audio/audio_processing.h" #include "api/audio_codecs/audio_decoder_factory.h" @@ -367,7 +367,7 @@ VoipResult VoipCore::StopPlayout(ChannelId channel_id) { } VoipResult VoipCore::ReceivedRTPPacket(ChannelId channel_id, - ArrayView rtp_packet) { + std::span rtp_packet) { scoped_refptr channel = GetChannel(channel_id); if (!channel) { @@ -380,7 +380,7 @@ VoipResult VoipCore::ReceivedRTPPacket(ChannelId channel_id, } VoipResult VoipCore::ReceivedRTCPPacket(ChannelId channel_id, - ArrayView rtcp_packet) { + std::span rtcp_packet) { scoped_refptr channel = GetChannel(channel_id); if (!channel) { diff --git a/audio/voip/voip_core.h b/audio/voip/voip_core.h index dbd9150be90..45eca080c8c 100644 --- a/audio/voip/voip_core.h +++ b/audio/voip/voip_core.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/audio/audio_mixer.h" #include "api/audio/audio_processing.h" @@ -81,9 +81,9 @@ class VoipCore : public VoipEngine, // Implements VoipNetwork interfaces. VoipResult ReceivedRTPPacket(ChannelId channel_id, - ArrayView rtp_packet) override; + std::span rtp_packet) override; VoipResult ReceivedRTCPPacket(ChannelId channel_id, - ArrayView rtcp_packet) override; + std::span rtcp_packet) override; // Implements VoipCodec interfaces. VoipResult SetSendCodec(ChannelId channel_id, diff --git a/build_overrides/build.gni b/build_overrides/build.gni index ceefc638f84..8cb6b82d89e 100644 --- a/build_overrides/build.gni +++ b/build_overrides/build.gni @@ -22,11 +22,14 @@ asan_suppressions_file = "//build/sanitizers/asan_suppressions.cc" lsan_suppressions_file = "//tools_webrtc/sanitizers/lsan_suppressions_webrtc.cc" tsan_suppressions_file = "//tools_webrtc/sanitizers/tsan_suppressions_webrtc.cc" msan_ignorelist_path = - rebase_path("//tools_webrtc/msan/suppressions.txt", root_build_dir) + rebase_path("//tools_webrtc/sanitizers/msan_suppressions.txt", + root_build_dir) ubsan_ignorelist_path = - rebase_path("//tools_webrtc/ubsan/suppressions.txt", root_build_dir) + rebase_path("//tools_webrtc/sanitizers/ubsan_suppressions.txt", + root_build_dir) ubsan_vptr_ignorelist_path = - rebase_path("//tools_webrtc/ubsan/vptr_suppressions.txt", root_build_dir) + rebase_path("//tools_webrtc/sanitizers/ubsan_vptr_suppressions.txt", + root_build_dir) # For Chromium, Android 32-bit non-component, non-clang builds hit a 4GiB size # limit, making them requiring symbol_level=2. WebRTC doesn't hit that problem diff --git a/call/BUILD.gn b/call/BUILD.gn index 0c9481a459d..85c2fcf9195 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -38,13 +38,11 @@ rtc_library("call_interfaces") { deps = [ ":audio_sender_interface", - ":payload_type", ":receive_stream_interface", ":rtp_interfaces", ":video_receive_stream_api", ":video_send_stream_api", "../api:fec_controller_api", - "../api:field_trials_view", "../api:frame_transformer_interface", "../api:network_state_predictor_api", "../api:ref_count", @@ -70,6 +68,7 @@ rtc_library("call_interfaces") { "../api/transport:network_control", "../api/units:time_delta", "../api/units:timestamp", + "../api/video:video_stream_encoder", "../modules/async_audio_processing", "../modules/congestion_controller/rtp:congestion_controller_feedback_stats", "../modules/rtp_rtcp", @@ -110,7 +109,6 @@ rtc_library("rtp_interfaces") { "rtp_transport_controller_send_interface.h", ] deps = [ - "../api:array_view", "../api:fec_controller_api", "../api:frame_transformer_interface", "../api:network_state_predictor_api", @@ -149,8 +147,9 @@ rtc_library("rtp_receiver") { ] deps = [ ":rtp_interfaces", - "../api:array_view", "../api:sequence_checker", + "../api/environment", + "../logging:rtc_event_rtp_rtcp", "../modules/rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", @@ -177,7 +176,6 @@ rtc_library("rtp_sender") { deps = [ ":bitrate_configurator", ":rtp_interfaces", - "../api:array_view", "../api:bitrate_allocation", "../api:fec_controller_api", "../api:field_trials_view", @@ -220,7 +218,6 @@ rtc_library("rtp_sender") { "../modules/congestion_controller/rtp:congestion_controller_feedback_stats", "../modules/congestion_controller/rtp:control_handler", "../modules/congestion_controller/rtp:transport_feedback", - "../modules/congestion_controller/scream:scream_network_controller", "../modules/pacing", "../modules/rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", @@ -299,8 +296,6 @@ rtc_library("call") { deps = [ ":bitrate_allocator", ":call_interfaces", - ":payload_type", - ":payload_type_picker", ":receive_stream_interface", ":rtp_interfaces", ":rtp_receiver", @@ -308,10 +303,8 @@ rtc_library("call") { ":version", ":video_receive_stream_api", ":video_send_stream_api", - "../api:array_view", "../api:fec_controller_api", "../api:field_trials_view", - "../api:rtc_error", "../api:rtp_headers", "../api:rtp_parameters", "../api:scoped_refptr", @@ -327,13 +320,14 @@ rtc_library("call") { "../api/units:data_size", "../api/units:time_delta", "../api/units:timestamp", + "../api/video:video_stream_encoder", "../audio", "../logging:rtc_event_audio", "../logging:rtc_event_rtp_rtcp", "../logging:rtc_event_video", "../logging:rtc_stream_config", - "../media:codec", "../modules/congestion_controller", + "../modules/pacing", "../modules/rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", "../modules/video_coding", @@ -370,13 +364,17 @@ rtc_library("payload_type_picker") { ] deps = [ ":payload_type", + "../api:payload_type", "../api:rtc_error", + "../api:rtp_parameters", "../api/audio_codecs:audio_codecs_api", "../media:codec", "../media:media_constants", "../rtc_base:checks", "../rtc_base:logging", "../rtc_base:stringutils", + "../rtc_base/containers:flat_map", + "../rtc_base/containers:flat_set", "//third_party/abseil-cpp/absl/strings", ] } @@ -384,9 +382,10 @@ rtc_library("payload_type_picker") { rtc_source_set("payload_type") { sources = [ "payload_type.h" ] deps = [ + "../api:payload_type", "../api:rtc_error", + "../api:rtp_parameters", "../media:codec", - "../rtc_base:strong_alias", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -409,7 +408,6 @@ rtc_library("video_send_stream_api") { ] deps = [ ":rtp_interfaces", - "../api:array_view", "../api:frame_transformer_interface", "../api:rtp_parameters", "../api:rtp_sender_interface", @@ -476,7 +474,6 @@ rtc_library("fake_network") { ] deps = [ ":simulated_packet_receiver", - "../api:array_view", "../api:rtp_parameters", "../api:simulated_network_api", "../api:transport_api", @@ -523,7 +520,6 @@ if (rtc_include_tests) { ":rtp_sender", ":video_receive_stream_api", ":video_send_stream_api", - "../api:array_view", "../api:bitrate_allocation", "../api:create_frame_generator", "../api:field_trials", @@ -531,15 +527,19 @@ if (rtc_include_tests) { "../api:make_ref_counted", "../api:mock_audio_mixer", "../api:mock_frame_transformer", + "../api:mock_video_codec_factory", + "../api:payload_type", "../api:rtp_headers", "../api:rtp_parameters", "../api:scoped_refptr", "../api:simulated_network_api", "../api:transport_api", "../api/adaptation:resource_adaptation_api", + "../api/audio_codecs:audio_codecs_api", "../api/crypto:options", "../api/environment", "../api/environment:environment_factory", + "../api/rtc_event_log", "../api/test/network_emulation", "../api/test/video:function_video_factory", "../api/transport:bitrate_settings", @@ -555,11 +555,14 @@ if (rtc_include_tests) { "../api/video:video_codec_constants", "../api/video:video_frame", "../api/video:video_frame_type", + "../api/video:video_layers_allocation", "../api/video:video_rtp_headers", "../api/video_codecs:video_codecs_api", "../audio", "../common_video:frame_counts", "../common_video/generic_frame_descriptor", + "../logging:mocks", + "../logging:rtc_event_rtp_rtcp", "../media:codec", "../media:media_constants", "../modules/audio_device:mock_audio_device", @@ -595,6 +598,7 @@ if (rtc_include_tests) { "../test:run_loop", "../test:test_common", "../test:test_support", + "../test:video_test_common", "../test:video_test_constants", "../test/scenario", "../test/time_controller", @@ -620,7 +624,6 @@ if (rtc_include_tests) { ":fake_network", ":video_receive_stream_api", ":video_send_stream_api", - "../api:array_view", "../api:field_trials_view", "../api:make_ref_counted", "../api:rtc_event_log_output_file", @@ -656,15 +659,14 @@ if (rtc_include_tests) { "../rtc_base:checks", "../rtc_base:logging", "../rtc_base:rtc_event", - "../rtc_base:stringutils", "../rtc_base:task_queue_for_test", "../rtc_base:threading", "../rtc_base/task_utils:repeating_task", "../system_wrappers:metrics", "../test:encoder_settings", "../test:fake_video_codecs", - "../test:fileutils", "../test:frame_generator_capturer", + "../test:run_loop", "../test:test_common", "../test:test_flags", "../test:test_support", @@ -704,6 +706,7 @@ if (rtc_include_tests) { "../rtc_base:network_route", "../rtc_base/containers:flat_map", "../rtc_base/network:sent_packet", + "../test:run_loop", "../test:test_support", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -714,6 +717,7 @@ if (rtc_include_tests) { sources = [ "test/mock_bitrate_allocator.h" ] deps = [ ":bitrate_allocator", + "../test:run_loop", "../test:test_support", ] } @@ -735,6 +739,7 @@ if (rtc_include_tests) { "../api/audio_codecs:audio_codecs_api", "../api/crypto:frame_decryptor_interface", "../api/transport/rtp:rtp_source", + "../test:run_loop", "../test:test_support", ] } @@ -745,7 +750,9 @@ if (rtc_include_tests) { deps = [ ":payload_type", ":payload_type_picker", + "../api:payload_type", "../api:rtc_error", + "../api:rtp_parameters", "../media:codec", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -765,6 +772,7 @@ if (rtc_include_tests) { "../rtc_base:checks", "../rtc_base:copy_on_write_buffer", "../system_wrappers", + "../test:run_loop", "../test:test_support", "../test/network:simulated_network", ] diff --git a/call/DEPS b/call/DEPS index 98a8a4b68d6..292a8309be7 100644 --- a/call/DEPS +++ b/call/DEPS @@ -17,61 +17,61 @@ include_rules = [ ] specific_include_rules = { - "video_receive_stream\.h": [ + "video_receive_stream\\.h": [ "+common_video/frame_counts.h", ], - "video_send_stream\.h": [ + "video_send_stream\\.h": [ "+common_video", ], - "rtp_transport_controller_send_interface\.h": [ + "rtp_transport_controller_send_interface\\.h": [ "+common_video/frame_counts.h", ], - "call_perf_tests\.cc": [ + "call_perf_tests\\.cc": [ "+media/engine", ], - "simulated_network\.h": [ + "simulated_network\\.h": [ "+test/network/simulated_network.h", ], - "rtp_payload_params\.cc": [ + "rtp_payload_params\\.cc": [ "+common_video/generic_frame_descriptor", ], - "rtp_payload_params\.h": [ + "rtp_payload_params\\.h": [ "+common_video/generic_frame_descriptor", ], - "rtp_payload_params_unittest\.cc": [ + "rtp_payload_params_unittest\\.cc": [ "+common_video/generic_frame_descriptor", ], - "rtp_video_sender\.cc": [ + "rtp_video_sender\\.cc": [ "+common_video/frame_counts.h", "+common_video/generic_frame_descriptor", ], - "rtp_video_sender.h": [ + "rtp_video_sender\\.h": [ "+common_video/frame_counts.h", ], - "rtp_video_sender_unittest.cc": [ + "rtp_video_sender_unittest\\.cc": [ "+common_video/frame_counts.h", "+common_video/generic_frame_descriptor", ], - "payload_type\.h": [ + "payload_type\\.h": [ "+media/base/codec.h", ], - "payload_type_picker\.h": [ + "payload_type_picker\\.h": [ "+media/base/codec.h", "+media/base/media_constants.h", ], - "payload_type_picker\.cc": [ + "payload_type_picker\\.cc": [ "+media/base/codec.h", "+media/base/codec_comparators.h", "+media/base/media_constants.h", ], - "payload_type_picker_unittest\.cc": [ + "payload_type_picker_unittest\\.cc": [ "+media/base/codec.h", "+media/base/media_constants.h", ], - "call\.cc": [ + "call\\.cc": [ "+media/base/codec.h", ], - "fake_payload_type_suggester": [ + "fake_payload_type_suggester\\.h": [ "+media/base/codec.h", ] } diff --git a/call/DIR_METADATA b/call/DIR_METADATA new file mode 100644 index 00000000000..e69de29bb2d diff --git a/call/OWNERS b/call/OWNERS index d37ac06b3c0..0c771a0c1ca 100644 --- a/call/OWNERS +++ b/call/OWNERS @@ -2,8 +2,6 @@ sprang@webrtc.org danilchap@webrtc.org brandtr@webrtc.org tommi@webrtc.org -mflodman@webrtc.org -stefan@webrtc.org perkj@webrtc.org per-file version.cc=webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com diff --git a/call/adaptation/BUILD.gn b/call/adaptation/BUILD.gn index 7425e5b5a3c..0b0da3477d7 100644 --- a/call/adaptation/BUILD.gn +++ b/call/adaptation/BUILD.gn @@ -86,10 +86,10 @@ if (rtc_include_tests) { "../../rtc_base:checks", "../../rtc_base:macromagic", "../../rtc_base:rtc_event", - "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", "../../rtc_base:threading", "../../test:create_test_field_trials", + "../../test:run_loop", "../../test:test_support", "../../test:wait_until", "../../video/config:encoder_config", diff --git a/call/adaptation/resource_adaptation_processor_unittest.cc b/call/adaptation/resource_adaptation_processor_unittest.cc index 4f6396082bd..413f6fe0167 100644 --- a/call/adaptation/resource_adaptation_processor_unittest.cc +++ b/call/adaptation/resource_adaptation_processor_unittest.cc @@ -27,11 +27,11 @@ #include "call/adaptation/video_stream_input_state_provider.h" #include "rtc_base/event.h" #include "rtc_base/task_queue_for_test.h" -#include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -141,12 +141,10 @@ class ResourceAdaptationProcessorTest : public ::testing::Test { processor_.reset(); } - static void WaitUntilTaskQueueIdle() { - ASSERT_TRUE(Thread::Current()->ProcessMessages(0)); - } + void WaitUntilTaskQueueIdle() { main_thread_.Flush(); } protected: - AutoThread main_thread_; + test::RunLoop main_thread_; FakeFrameRateProvider frame_rate_provider_; VideoStreamInputStateProvider input_state_provider_; scoped_refptr resource_; diff --git a/call/bitrate_estimator_tests.cc b/call/bitrate_estimator_tests.cc index 58cbaea0447..7e71efbb769 100644 --- a/call/bitrate_estimator_tests.cc +++ b/call/bitrate_estimator_tests.cc @@ -7,7 +7,6 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#include #include #include #include @@ -152,8 +151,6 @@ class BitrateEstimatorTest : public test::CallTest { VideoReceiveStreamInterface::Config(receive_transport_.get()); // receive_config_.decoders will be set by every stream separately. receive_config_.rtp.remote_ssrc = GetVideoSendConfig()->rtp.ssrcs[0]; - receive_config_.rtp.local_ssrc = - test::VideoTestConstants::kReceiverLocalVideoSsrc; }); } @@ -209,7 +206,6 @@ class BitrateEstimatorTest : public test::CallTest { test_->receive_config_.decoders.push_back(decoder); test_->receive_config_.rtp.remote_ssrc = test_->GetVideoSendConfig()->rtp.ssrcs[0]; - test_->receive_config_.rtp.local_ssrc++; test_->receive_config_.renderer = &test->fake_renderer_; video_receive_stream_ = test_->receiver_call_->CreateVideoReceiveStream( test_->receive_config_.Copy()); diff --git a/call/call.cc b/call/call.cc index ca253775bcb..00ee6d386db 100644 --- a/call/call.cc +++ b/call/call.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" #include "api/adaptation/resource.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/fec_controller.h" #include "api/media_types.h" @@ -43,6 +43,7 @@ #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" +#include "api/video/video_stream_encoder_settings.h" #include "audio/audio_receive_stream.h" #include "audio/audio_send_stream.h" #include "audio/audio_state.h" @@ -112,7 +113,6 @@ std::unique_ptr CreateRtcLogStreamConfig( const VideoReceiveStreamInterface::Config& config) { auto rtclog_config = std::make_unique(); rtclog_config->remote_ssrc = config.rtp.remote_ssrc; - rtclog_config->local_ssrc = config.rtp.local_ssrc; rtclog_config->rtx_ssrc = config.rtp.rtx_ssrc; rtclog_config->rtcp_mode = config.rtp.rtcp_mode; @@ -129,7 +129,6 @@ std::unique_ptr CreateRtcLogStreamConfig( const VideoSendStream::Config& config, size_t ssrc_index) { auto rtclog_config = std::make_unique(); - rtclog_config->local_ssrc = config.rtp.ssrcs[ssrc_index]; if (ssrc_index < config.rtp.rtx.ssrcs.size()) { rtclog_config->rtx_ssrc = config.rtp.rtx.ssrcs[ssrc_index]; } @@ -146,7 +145,6 @@ std::unique_ptr CreateRtcLogStreamConfig( const AudioReceiveStreamInterface::Config& config) { auto rtclog_config = std::make_unique(); rtclog_config->remote_ssrc = config.rtp.remote_ssrc; - rtclog_config->local_ssrc = config.rtp.local_ssrc; return rtclog_config; } @@ -228,10 +226,13 @@ class Call final : public webrtc::Call, webrtc::VideoSendStream* CreateVideoSendStream( webrtc::VideoSendStream::Config config, - VideoEncoderConfig encoder_config) override; + VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback = + nullptr) override; webrtc::VideoSendStream* CreateVideoSendStream( webrtc::VideoSendStream::Config config, VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback, std::unique_ptr fec_controller) override; void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) override; @@ -271,13 +272,6 @@ class Call final : public webrtc::Call, void OnAudioTransportOverheadChanged( int transport_overhead_per_packet) override; - void OnLocalSsrcUpdated(webrtc::AudioReceiveStreamInterface& stream, - uint32_t local_ssrc) override; - void OnLocalSsrcUpdated(VideoReceiveStreamInterface& stream, - uint32_t local_ssrc) override; - void OnLocalSsrcUpdated(FlexfecReceiveStream& stream, - uint32_t local_ssrc) override; - void OnUpdateSyncGroup(webrtc::AudioReceiveStreamInterface& stream, absl::string_view sync_group) override; @@ -353,6 +347,11 @@ class Call final : public webrtc::Call, void DeliverRtcp(MediaType media_type, CopyOnWriteBuffer packet) RTC_RUN_ON(network_thread_); + void DeliverRtpPacket_w(MediaType media_type, + RtpPacketReceived packet, + OnUndemuxablePacketHandler undemuxable_packet_handler) + RTC_RUN_ON(worker_thread_); + AudioReceiveStreamImpl* FindAudioStreamForSyncGroup( absl::string_view sync_group) RTC_RUN_ON(worker_thread_); void ConfigureSync(absl::string_view sync_group) RTC_RUN_ON(worker_thread_); @@ -449,7 +448,8 @@ class Call final : public webrtc::Call, RepeatingTaskHandle receive_side_cc_periodic_task_; RepeatingTaskHandle elastic_bandwidth_allocation_task_; - const std::unique_ptr receive_time_calculator_; + const std::unique_ptr receive_time_calculator_ + RTC_GUARDED_BY(network_thread_); const std::unique_ptr video_send_delay_stats_; const Timestamp start_of_call_; @@ -509,6 +509,7 @@ std::unique_ptr Call::Create(CallConfig config) { VideoSendStream* Call::CreateVideoSendStream( VideoSendStream::Config /* config */, VideoEncoderConfig /* encoder_config */, + EncoderSwitchRequestCallback /* encoder_switch_request_callback */, std::unique_ptr /* fec_controller */) { return nullptr; } @@ -889,6 +890,7 @@ void Call::DestroyAudioReceiveStream( webrtc::VideoSendStream* Call::CreateVideoSendStream( webrtc::VideoSendStream::Config config, VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback, std::unique_ptr fec_controller) { TRACE_EVENT0("webrtc", "Call::CreateVideoSendStream"); RTC_DCHECK_RUN_ON(worker_thread_); @@ -912,7 +914,8 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream( transport_send_.get(), config_.encode_metronome, bitrate_allocator_.get(), video_send_delay_stats_.get(), std::move(config), std::move(encoder_config), suspended_video_send_ssrcs_, - suspended_video_payload_states_, std::move(fec_controller)); + suspended_video_payload_states_, std::move(fec_controller), + std::move(encoder_switch_request_callback)); for (uint32_t ssrc : ssrcs) { RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end()); @@ -933,7 +936,8 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream( webrtc::VideoSendStream* Call::CreateVideoSendStream( webrtc::VideoSendStream::Config config, - VideoEncoderConfig encoder_config) { + VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback) { RTC_DCHECK_RUN_ON(worker_thread_); if (config_.fec_controller_factory) { RTC_LOG(LS_INFO) << "External FEC Controller will be used."; @@ -943,6 +947,7 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream( ? config_.fec_controller_factory->CreateFecController(env_) : std::make_unique(env_); return CreateVideoSendStream(std::move(config), std::move(encoder_config), + std::move(encoder_switch_request_callback), std::move(fec_controller)); } @@ -1053,7 +1058,7 @@ FlexfecReceiveStream* Call::CreateFlexfecReceiveStream( // in a valid state, since OnRtpPacket runs on the same thread. FlexfecReceiveStreamImpl* receive_stream = new FlexfecReceiveStreamImpl( env_, std::move(config), &video_receiver_controller_, - call_stats_->AsRtcpRttStats()); + transport_send_->packet_router(), call_stats_->AsRtcpRttStats()); // TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network // thread. @@ -1220,30 +1225,16 @@ void Call::UpdateAggregateNetworkState() { transport_send_->OnNetworkAvailability(aggregate_network_up); } -void Call::OnLocalSsrcUpdated(webrtc::AudioReceiveStreamInterface& stream, - uint32_t local_ssrc) { - RTC_DCHECK_RUN_ON(worker_thread_); - static_cast(stream).SetLocalSsrc(local_ssrc); -} - -void Call::OnLocalSsrcUpdated(VideoReceiveStreamInterface& stream, - uint32_t local_ssrc) { - RTC_DCHECK_RUN_ON(worker_thread_); - static_cast(stream).SetLocalSsrc(local_ssrc); -} - -void Call::OnLocalSsrcUpdated(FlexfecReceiveStream& stream, - uint32_t local_ssrc) { - RTC_DCHECK_RUN_ON(worker_thread_); - static_cast(stream).SetLocalSsrc(local_ssrc); -} - void Call::OnUpdateSyncGroup(webrtc::AudioReceiveStreamInterface& stream, absl::string_view sync_group) { RTC_DCHECK_RUN_ON(worker_thread_); webrtc::AudioReceiveStreamImpl& receive_stream = static_cast(stream); + std::string old_sync_group(receive_stream.sync_group()); receive_stream.SetSyncGroup(sync_group); + if (old_sync_group != sync_group) { + ConfigureSync(old_sync_group); + } ConfigureSync(sync_group); } @@ -1356,7 +1347,7 @@ void Call::DeliverRtcpPacket(CopyOnWriteBuffer packet) { receive_stats_.AddReceivedRtcpBytes(static_cast(packet.size())); bool rtcp_delivered = false; - ArrayView packet_view(packet.cdata(), packet.size()); + std::span packet_view(packet.cdata(), packet.size()); for (VideoReceiveStream2* stream : video_receive_streams_) { if (stream->DeliverRtcp(packet_view)) rtcp_delivered = true; @@ -1386,8 +1377,7 @@ void Call::DeliverRtpPacket( MediaType media_type, RtpPacketReceived packet, OnUndemuxablePacketHandler undemuxable_packet_handler) { - RTC_DCHECK_RUN_ON(worker_thread_); - RTC_DCHECK(packet.arrival_time().IsFinite()); + RTC_DCHECK_RUN_ON(network_thread_); if (receive_time_calculator_) { int64_t packet_time_us = packet.arrival_time().us(); @@ -1398,10 +1388,34 @@ void Call::DeliverRtpPacket( packet.set_arrival_time(Timestamp::Micros(packet_time_us)); } + if (worker_thread_->IsCurrent()) { + RTC_DCHECK_RUN_ON(worker_thread_); + DeliverRtpPacket_w(media_type, std::move(packet), + std::move(undemuxable_packet_handler)); + } else { + worker_thread_->PostTask(SafeTask( + task_safety_.flag(), + [this, media_type, packet = std::move(packet), + handler = std::move(undemuxable_packet_handler)]() mutable { + RTC_DCHECK_RUN_ON(worker_thread_); + DeliverRtpPacket_w(media_type, std::move(packet), std::move(handler)); + })); + } +} + +void Call::DeliverRtpPacket_w( + MediaType media_type, + RtpPacketReceived packet, + OnUndemuxablePacketHandler undemuxable_packet_handler) { + RTC_DCHECK_RUN_ON(worker_thread_); + RTC_DCHECK(packet.arrival_time().IsFinite()); + NotifyBweOfReceivedPacket(packet, media_type); - env_.event_log().Log(std::make_unique(packet)); + // Packets that are successfully demuxed are logged by their respective + // streams. Packets that fail to demux are logged here. if (media_type != MediaType::AUDIO && media_type != MediaType::VIDEO) { + env_.event_log().Log(std::make_unique(packet)); return; } @@ -1420,9 +1434,11 @@ void Call::DeliverRtpPacket( // Note that we dont want to call NotifyBweOfReceivedPacket twice per // packet. if (!undemuxable_packet_handler(packet)) { + env_.event_log().Log(std::make_unique(packet)); return; } if (!receiver_controller.OnRtpPacket(packet)) { + env_.event_log().Log(std::make_unique(packet)); RTC_LOG(LS_INFO) << "Failed to demux packet " << packet.Ssrc(); return; } diff --git a/call/call.h b/call/call.h index 902b60d3065..636c0f9737f 100644 --- a/call/call.h +++ b/call/call.h @@ -24,6 +24,7 @@ #include "api/scoped_refptr.h" #include "api/task_queue/task_queue_base.h" #include "api/transport/bitrate_settings.h" +#include "api/video/video_stream_encoder_settings.h" #include "call/audio_receive_stream.h" #include "call/audio_send_stream.h" #include "call/call_config.h" @@ -80,11 +81,14 @@ class Call { virtual VideoSendStream* CreateVideoSendStream( VideoSendStream::Config config, - VideoEncoderConfig encoder_config) = 0; + VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback = + nullptr) = 0; virtual VideoSendStream* CreateVideoSendStream( VideoSendStream::Config config, VideoEncoderConfig encoder_config, - std::unique_ptr fec_controller); + EncoderSwitchRequestCallback encoder_switch_request_callback, + std::unique_ptr fec_controller) = 0; virtual void DestroyVideoSendStream(VideoSendStream* send_stream) = 0; virtual VideoReceiveStreamInterface* CreateVideoReceiveStream( @@ -130,15 +134,6 @@ class Call { virtual void OnAudioTransportOverheadChanged( int transport_overhead_per_packet) = 0; - // Called when a receive stream's local ssrc has changed and association with - // send streams needs to be updated. - virtual void OnLocalSsrcUpdated(AudioReceiveStreamInterface& stream, - uint32_t local_ssrc) = 0; - virtual void OnLocalSsrcUpdated(VideoReceiveStreamInterface& stream, - uint32_t local_ssrc) = 0; - virtual void OnLocalSsrcUpdated(FlexfecReceiveStream& stream, - uint32_t local_ssrc) = 0; - virtual void OnUpdateSyncGroup(AudioReceiveStreamInterface& stream, absl::string_view sync_group) = 0; diff --git a/call/call_perf_tests.cc b/call/call_perf_tests.cc index 726cf520804..2eae4f42909 100644 --- a/call/call_perf_tests.cc +++ b/call/call_perf_tests.cc @@ -16,13 +16,13 @@ #include #include #include +#include #include #include #include #include "absl/flags/flag.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/audio/builtin_audio_processing_builder.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" @@ -212,7 +212,6 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec, absl::string_view test_label) { const char* kSyncGroup = "av_sync"; const uint32_t kAudioSendSsrc = 1234; - const uint32_t kAudioRecvSsrc = 5678; BuiltInNetworkBehaviorConfig audio_net_config; audio_net_config.queue_delay_ms = 500; @@ -267,7 +266,7 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec, }); audio_send_transport = std::make_unique( - task_queue(), sender_call_.get(), observer.get(), + send_env_, task_queue(), sender_call_.get(), observer.get(), test::PacketTransport::kSender, audio_pt_map, std::make_unique( Clock::GetRealTimeClock(), @@ -276,7 +275,7 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec, audio_send_transport->SetReceiver(receiver_call_->Receiver()); video_send_transport = std::make_unique( - task_queue(), sender_call_.get(), observer.get(), + send_env_, task_queue(), sender_call_.get(), observer.get(), test::PacketTransport::kSender, video_pt_map, std::make_unique( Clock::GetRealTimeClock(), @@ -285,7 +284,7 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec, video_send_transport->SetReceiver(receiver_call_->Receiver()); receive_transport = std::make_unique( - task_queue(), receiver_call_.get(), observer.get(), + recv_env_, task_queue(), receiver_call_.get(), observer.get(), test::PacketTransport::kReceiver, payload_type_map_, std::make_unique( Clock::GetRealTimeClock(), @@ -324,7 +323,6 @@ void CallPerfTest::TestAudioVideoSync(FecMode fec, AudioReceiveStreamInterface::Config audio_recv_config; audio_recv_config.rtp.remote_ssrc = kAudioSendSsrc; - audio_recv_config.rtp.local_ssrc = kAudioRecvSsrc; audio_recv_config.rtcp_send_transport = receive_transport.get(); audio_recv_config.sync_group = kSyncGroup; audio_recv_config.decoder_factory = audio_decoder_factory_; @@ -568,7 +566,7 @@ void CallPerfTest::TestMinTransmitBitrate(bool pad_to_min_bitrate) { private: // TODO(holmer): Run this with a timer instead of once per packet. - Action OnSendRtp(ArrayView /* packet */) override { + Action OnSendRtp(std::span /* packet */) override { task_queue_->PostTask(SafeTask(task_safety_flag_, [this]() { VideoSendStream::Stats stats = send_stream_->GetStats(); @@ -1023,7 +1021,7 @@ void CallPerfTest::TestEncodeFramerate(VideoEncoderFactory* encoder_factory, } } - Action OnSendRtp(ArrayView /* packet */) override { + Action OnSendRtp(std::span /* packet */) override { const Timestamp now = clock_->CurrentTime(); if (now - last_getstats_time_ > kMinGetStatsInterval) { last_getstats_time_ = now; diff --git a/call/call_unittest.cc b/call/call_unittest.cc index 32f41fece0d..e02489861e4 100644 --- a/call/call_unittest.cc +++ b/call/call_unittest.cc @@ -19,13 +19,16 @@ #include "absl/strings/string_view.h" #include "api/adaptation/resource.h" +#include "api/audio_codecs/audio_decoder_factory.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/make_ref_counted.h" #include "api/media_types.h" +#include "api/rtc_event_log/rtc_event.h" #include "api/scoped_refptr.h" #include "api/test/mock_audio_mixer.h" +#include "api/test/mock_video_decoder_factory.h" #include "api/test/video/function_video_encoder_factory.h" +#include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" #include "api/video_codecs/sdp_video_format.h" @@ -37,17 +40,23 @@ #include "call/audio_state.h" #include "call/call_config.h" #include "call/flexfec_receive_stream.h" +#include "call/video_receive_stream.h" #include "call/video_send_stream.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "logging/rtc_event_log/mock/mock_rtc_event_log.h" #include "modules/audio_device/include/mock_audio_device.h" #include "modules/audio_processing/include/mock_audio_processing.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "test/create_test_environment.h" #include "test/fake_encoder.h" +#include "test/fake_videorenderer.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_audio_decoder_factory.h" #include "test/mock_transport.h" #include "test/run_loop.h" +#include "test/time_controller/simulated_time_controller.h" #include "video/config/video_encoder_config.h" namespace webrtc { @@ -74,15 +83,18 @@ struct CallHelper { : make_ref_counted>(); audio_state_config.audio_device_module = make_ref_counted(); - CallConfig config(CreateEnvironment()); + CallConfig config(CreateTestEnvironment({.event_log = &log_})); config.audio_state = AudioState::Create(audio_state_config); call_ = Call::Create(std::move(config)); } Call* operator->() { return call_.get(); } + MockRtcEventLog& log() { return log_; } + private: RunLoop loop_; + MockRtcEventLog log_; std::unique_ptr call_; }; @@ -190,7 +202,7 @@ TEST(CallTest, CreateDestroy_FlexfecReceiveStream) { MockTransport rtcp_send_transport; FlexfecReceiveStream::Config config(&rtcp_send_transport); config.payload_type = 118; - config.rtp.remote_ssrc = 38837212; + config.remote_ssrc = 38837212; config.protected_media_ssrcs = {27273}; FlexfecReceiveStream* stream = call->CreateFlexfecReceiveStream(config); @@ -209,7 +221,7 @@ TEST(CallTest, CreateDestroy_FlexfecReceiveStreams) { for (int i = 0; i < 2; ++i) { for (uint32_t ssrc = 0; ssrc < 1234567; ssrc += 34567) { - config.rtp.remote_ssrc = ssrc; + config.remote_ssrc = ssrc; config.protected_media_ssrcs = {ssrc + 1}; FlexfecReceiveStream* stream = call->CreateFlexfecReceiveStream(config); EXPECT_NE(stream, nullptr); @@ -237,22 +249,22 @@ TEST(CallTest, MultipleFlexfecReceiveStreamsProtectingSingleVideoStream) { FlexfecReceiveStream* stream; std::list streams; - config.rtp.remote_ssrc = 838383; + config.remote_ssrc = 838383; stream = call->CreateFlexfecReceiveStream(config); EXPECT_NE(stream, nullptr); streams.push_back(stream); - config.rtp.remote_ssrc = 424993; + config.remote_ssrc = 424993; stream = call->CreateFlexfecReceiveStream(config); EXPECT_NE(stream, nullptr); streams.push_back(stream); - config.rtp.remote_ssrc = 99383; + config.remote_ssrc = 99383; stream = call->CreateFlexfecReceiveStream(config); EXPECT_NE(stream, nullptr); streams.push_back(stream); - config.rtp.remote_ssrc = 5548; + config.remote_ssrc = 5548; stream = call->CreateFlexfecReceiveStream(config); EXPECT_NE(stream, nullptr); streams.push_back(stream); @@ -455,4 +467,202 @@ TEST(CallTest, AddAdaptationResourceBeforeCreatingVideoSendStream) { call->DestroyVideoSendStream(stream2); } +MATCHER_P(IsRtcEventRtpPacketIncomingPtrWithSsrc, ssrc, "") { + if (!arg) { + return false; + } + if (arg->GetType() != RtcEvent::Type::RtpPacketIncoming) { + return false; + } + RtcEventRtpPacketIncoming* event = + static_cast(arg); + + return event->Ssrc() == ssrc; +} + +class CallRtcEventLogTest : public ::testing::Test { + protected: + static constexpr uint32_t kUnknownSsrc = 1111; + static constexpr uint32_t kAudioSsrc = 2222; + static constexpr uint32_t kVideoSsrc = 3333; + static constexpr uint32_t kVideoRtxSsrc = 4444; + static constexpr uint32_t kVideoFlexfecSsrc = 5555; + + CallRtcEventLogTest() + : call_(/*use_null_audio_processing=*/false), + base_packet_(/*extensions=*/nullptr, + /*arrival_time=*/Timestamp::Zero()), + audio_decoder_factory_(MockAudioDecoderFactory::CreateEmptyFactory()) { + // Undemuxable base packet. + base_packet_.SetSsrc(kUnknownSsrc); + + // Audio. + AudioReceiveStreamInterface::Config audio_config; + audio_config.rtp.remote_ssrc = kAudioSsrc; + // Needed for DCHECKs. + audio_config.rtcp_send_transport = &transport_; + audio_config.decoder_factory = audio_decoder_factory_; + audio_stream_ = call_->CreateAudioReceiveStream(audio_config); + + // Video. + VideoReceiveStreamInterface::Config video_config(&transport_); + video_config.rtp.remote_ssrc = kVideoSsrc; + video_config.rtp.rtx_ssrc = kVideoRtxSsrc; + // Needed for DCHECKs. + video_config.decoders.emplace_back(SdpVideoFormat("VP8"), + /*payload_type=*/96); + video_config.decoder_factory = &video_decoder_factory_; + video_config.renderer = &renderer_; + video_stream_ = call_->CreateVideoReceiveStream(std::move(video_config)); + + // Flexfec. + FlexfecReceiveStream::Config flexfec_config(&transport_); + flexfec_config.remote_ssrc = kVideoFlexfecSsrc; + flexfec_stream_ = call_->CreateFlexfecReceiveStream(flexfec_config); + } + ~CallRtcEventLogTest() override { + call_->DestroyFlexfecReceiveStream(flexfec_stream_); + call_->DestroyVideoReceiveStream(video_stream_); + call_->DestroyAudioReceiveStream(audio_stream_); + } + + CallHelper call_; + MockFunction + un_demuxable_packet_handler_; + RtpPacketReceived base_packet_; + MockTransport transport_; + scoped_refptr audio_decoder_factory_; + MockVideoDecoderFactory video_decoder_factory_; + test::FakeVideoRenderer renderer_; + AudioReceiveStreamInterface* audio_stream_ = nullptr; + VideoReceiveStreamInterface* video_stream_ = nullptr; + FlexfecReceiveStream* flexfec_stream_ = nullptr; +}; + +TEST_F(CallRtcEventLogTest, LogsRtpPacketIncomingForUndemuxablePacketAnyType) { + RtpPacketReceived unknown_packet = base_packet_; + + EXPECT_CALL(call_.log(), + LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrc(kUnknownSsrc))); + call_->Receiver()->DeliverRtpPacket( + MediaType::ANY, unknown_packet, + un_demuxable_packet_handler_.AsStdFunction()); +} + +TEST_F(CallRtcEventLogTest, + LogsRtpPacketIncomingForUndemuxablePacketAudioType) { + RtpPacketReceived unknown_packet = base_packet_; + + EXPECT_CALL(call_.log(), + LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrc(kUnknownSsrc))); + call_->Receiver()->DeliverRtpPacket( + MediaType::AUDIO, unknown_packet, + un_demuxable_packet_handler_.AsStdFunction()); +} + +TEST_F(CallRtcEventLogTest, + LogsRtpPacketIncomingForUndemuxablePacketVideoType) { + RtpPacketReceived unknown_packet = base_packet_; + + EXPECT_CALL(call_.log(), + LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrc(kUnknownSsrc))); + call_->Receiver()->DeliverRtpPacket( + MediaType::VIDEO, unknown_packet, + un_demuxable_packet_handler_.AsStdFunction()); +} + +TEST_F(CallRtcEventLogTest, LogsRtpPacketIncomingForAudioPacket) { + RtpPacketReceived audio_packet = base_packet_; + audio_packet.SetSsrc(kAudioSsrc); + + EXPECT_CALL(call_.log(), + LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrc(kAudioSsrc))); + call_->Receiver()->DeliverRtpPacket( + MediaType::AUDIO, audio_packet, + un_demuxable_packet_handler_.AsStdFunction()); +} + +TEST_F(CallRtcEventLogTest, LogsRtpPacketIncomingForVideoPacket) { + RtpPacketReceived video_packet = base_packet_; + video_packet.SetSsrc(kVideoSsrc); + + EXPECT_CALL(call_.log(), + LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrc(kVideoSsrc))); + call_->Receiver()->DeliverRtpPacket( + MediaType::VIDEO, video_packet, + un_demuxable_packet_handler_.AsStdFunction()); +} + +TEST_F(CallRtcEventLogTest, LogsRtpPacketIncomingForVideoRtxPacket) { + RtpPacketReceived rtx_packet = base_packet_; + rtx_packet.SetSsrc(kVideoRtxSsrc); + + EXPECT_CALL(call_.log(), + LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrc(kVideoRtxSsrc))); + call_->Receiver()->DeliverRtpPacket( + MediaType::VIDEO, rtx_packet, + un_demuxable_packet_handler_.AsStdFunction()); +} + +TEST_F(CallRtcEventLogTest, LogsRtpPacketIncomingForVideoFlexfecPacket) { + RtpPacketReceived flexfec_packet = base_packet_; + flexfec_packet.SetSsrc(kVideoFlexfecSsrc); + + EXPECT_CALL( + call_.log(), + LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrc(kVideoFlexfecSsrc))); + call_->Receiver()->DeliverRtpPacket( + MediaType::VIDEO, flexfec_packet, + un_demuxable_packet_handler_.AsStdFunction()); +} + +TEST(CallTest, HandlesAudioSyncGroupUpdate) { + // Set up a call with an audio stream and a video stream in the same sync + // group. + GlobalSimulatedTimeController time_controller(Timestamp::Seconds(1)); + Environment env = CreateTestEnvironment({.time = &time_controller}); + + AudioState::Config audio_state_config; + audio_state_config.audio_mixer = make_ref_counted(); + audio_state_config.audio_device_module = + make_ref_counted(); + CallConfig config(env); + config.audio_state = AudioState::Create(audio_state_config); + std::unique_ptr call(Call::Create(std::move(config))); + + AudioReceiveStreamInterface::Config audio_config; + MockTransport rtcp_send_transport; + audio_config.rtp.remote_ssrc = 42; + audio_config.rtcp_send_transport = &rtcp_send_transport; + audio_config.decoder_factory = make_ref_counted(); + audio_config.sync_group = "group1"; + AudioReceiveStreamInterface* audio_stream = + call->CreateAudioReceiveStream(audio_config); + ASSERT_NE(audio_stream, nullptr); + + MockTransport video_rtcp_transport; + test::FakeVideoRenderer video_renderer; + VideoReceiveStreamInterface::Config video_config(&video_rtcp_transport); + video_config.rtp.remote_ssrc = 43; + video_config.sync_group = "group1"; + video_config.renderer = &video_renderer; + MockVideoDecoderFactory video_decoder_factory; + video_config.decoder_factory = &video_decoder_factory; + VideoReceiveStreamInterface::Decoder decoder; + decoder.payload_type = 96; + decoder.video_format = SdpVideoFormat("VP8"); + video_config.decoders.push_back(decoder); + VideoReceiveStreamInterface* video_stream = + call->CreateVideoReceiveStream(std::move(video_config)); + ASSERT_NE(video_stream, nullptr); + + // Move the audio stream to a new sync group and then remove it. + call->OnUpdateSyncGroup(*audio_stream, "group2"); + call->DestroyAudioReceiveStream(audio_stream); + + // The video stream should not be affected. + time_controller.AdvanceTime(TimeDelta::Seconds(2)); + call->DestroyVideoReceiveStream(video_stream); +} + } // namespace webrtc diff --git a/call/fake_network_pipe.cc b/call/fake_network_pipe.cc index 4504b6c7047..a8165937286 100644 --- a/call/fake_network_pipe.cc +++ b/call/fake_network_pipe.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/media_types.h" #include "api/test/simulated_network.h" @@ -142,7 +142,7 @@ void FakeNetworkPipe::RemoveActiveTransport(Transport* transport) { } } -bool FakeNetworkPipe::SendRtp(ArrayView packet, +bool FakeNetworkPipe::SendRtp(std::span packet, const PacketOptions& options, Transport* transport) { RTC_DCHECK(transport); @@ -150,7 +150,7 @@ bool FakeNetworkPipe::SendRtp(ArrayView packet, return true; } -bool FakeNetworkPipe::SendRtcp(ArrayView packet, +bool FakeNetworkPipe::SendRtcp(std::span packet, Transport* transport) { RTC_DCHECK(transport); EnqueuePacket(CopyOnWriteBuffer(packet), std::nullopt, true, transport); @@ -322,10 +322,10 @@ void FakeNetworkPipe::DeliverNetworkPacket(NetworkPacket* packet) { return; } if (packet->is_rtcp()) { - transport->SendRtcp(MakeArrayView(packet->data(), packet->data_length()), + transport->SendRtcp(std::span(packet->data(), packet->data_length()), packet->packet_options()); } else { - transport->SendRtp(MakeArrayView(packet->data(), packet->data_length()), + transport->SendRtp(std::span(packet->data(), packet->data_length()), packet->packet_options()); } } else if (receiver_) { diff --git a/call/fake_network_pipe.h b/call/fake_network_pipe.h index a30c50eecb0..168f8afffa6 100644 --- a/call/fake_network_pipe.h +++ b/call/fake_network_pipe.h @@ -17,8 +17,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/test/simulated_network.h" #include "call/simulated_packet_receiver.h" @@ -133,10 +133,10 @@ class FakeNetworkPipe : public SimulatedPacketReceiverInterface { // Methods for use with Transport interface. When/if packets are delivered, // they will be passed to the instance specified by the `transport` parameter. // Note that that instance must be in the map of active transports. - bool SendRtp(ArrayView packet, + bool SendRtp(std::span packet, const PacketOptions& options, Transport* transport); - bool SendRtcp(ArrayView packet, Transport* transport); + bool SendRtcp(std::span packet, Transport* transport); // Implements the PacketReceiver interface. When/if packets are delivered, // they will be passed directly to the receiver instance given in diff --git a/call/fake_payload_type_suggester.h b/call/fake_payload_type_suggester.h index 086e85ca6b4..2b46fabddc2 100644 --- a/call/fake_payload_type_suggester.h +++ b/call/fake_payload_type_suggester.h @@ -12,7 +12,9 @@ #define CALL_FAKE_PAYLOAD_TYPE_SUGGESTER_H_ #include "absl/strings/string_view.h" +#include "api/payload_type.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" #include "call/payload_type.h" #include "call/payload_type_picker.h" #include "media/base/codec.h" @@ -21,22 +23,35 @@ namespace webrtc { // Fake payload type suggester, for use in tests. // It uses a real PayloadTypePicker in order to do consistent PT // assignment. -class FakePayloadTypeSuggester : public webrtc::PayloadTypeSuggester { +class FakePayloadTypeSuggester : public PayloadTypeSuggester { public: - webrtc::RTCErrorOr SuggestPayloadType( - absl::string_view mid, - const Codec& codec) override { + RTCErrorOr SuggestPayloadType(absl::string_view mid, + const Codec& codec) override { // Ignores mid argument. return pt_picker_.SuggestMapping(codec, nullptr); } - webrtc::RTCError AddLocalMapping(absl::string_view, - webrtc::PayloadType payload_type, - const Codec& codec) override { - return webrtc::RTCError::OK(); + RTCError AddLocalMapping(absl::string_view, + PayloadType payload_type, + const Codec& codec) override { + return RTCError::OK(); + } + RTCErrorOr SuggestRtpHeaderExtensionId( + absl::string_view mid, + const RtpExtension& extension, + RtpTransceiverIdDomain id_domain) override { + return rtp_extension_picker_.SuggestMapping( + extension.uri, extension.encrypt, extension.id, id_domain, nullptr); + } + RTCError AddRtpHeaderExtensionMapping(absl::string_view mid, + const RtpExtension& extension, + bool local) override { + return rtp_extension_picker_.AddMapping(extension.id, extension.uri, + extension.encrypt); } private: - webrtc::PayloadTypePicker pt_picker_; + PayloadTypePicker pt_picker_; + RtpHeaderExtensionPicker rtp_extension_picker_; }; } // namespace webrtc diff --git a/call/flexfec_receive_stream.h b/call/flexfec_receive_stream.h index eb70e206ecb..95cf9eb2aee 100644 --- a/call/flexfec_receive_stream.h +++ b/call/flexfec_receive_stream.h @@ -43,7 +43,10 @@ class FlexfecReceiveStream : public RtpPacketSinkInterface, // Payload type for FlexFEC. int payload_type = -1; - ReceiveStreamRtpConfig rtp; + // Synchronization source (stream identifier) to be received. + // This member will not change mid-stream and can be assumed to be const + // post initialization. + uint32_t remote_ssrc = 0; // Vector containing a single element, corresponding to the SSRC of the // media stream being protected by this FlexFEC stream. The vector MUST have diff --git a/call/flexfec_receive_stream_impl.cc b/call/flexfec_receive_stream_impl.cc index ad7b3bbc8d8..d694d06f148 100644 --- a/call/flexfec_receive_stream_impl.cc +++ b/call/flexfec_receive_stream_impl.cc @@ -11,17 +11,19 @@ #include "call/flexfec_receive_stream_impl.h" #include -#include #include #include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/sequence_checker.h" #include "call/flexfec_receive_stream.h" #include "call/rtp_stream_receiver_controller_interface.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "modules/pacing/packet_router.h" #include "modules/rtp_rtcp/include/flexfec_receiver.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" @@ -32,8 +34,7 @@ std::string FlexfecReceiveStream::Config::ToString() const { char buf[1024]; SimpleStringBuilder ss(buf); ss << "{payload_type: " << payload_type; - ss << ", remote_ssrc: " << rtp.remote_ssrc; - ss << ", local_ssrc: " << rtp.local_ssrc; + ss << ", remote_ssrc: " << remote_ssrc; ss << ", protected_media_ssrcs: ["; size_t i = 0; for (; i + 1 < protected_media_ssrcs.size(); ++i) @@ -49,7 +50,7 @@ bool FlexfecReceiveStream::Config::IsCompleteAndEnabled() const { if (payload_type < 0) return false; // Do we have the necessary SSRC information? - if (rtp.remote_ssrc == 0) + if (remote_ssrc == 0) return false; // TODO(brandtr): Update this check when we support multistream protection. if (protected_media_ssrcs.size() != 1u) @@ -72,7 +73,7 @@ std::unique_ptr MaybeCreateFlexfecReceiver( } RTC_DCHECK_GE(config.payload_type, 0); RTC_DCHECK_LE(config.payload_type, 127); - if (config.rtp.remote_ssrc == 0) { + if (config.remote_ssrc == 0) { RTC_LOG(LS_WARNING) << "Invalid FlexFEC SSRC given. " "This FlexfecReceiveStream will therefore be useless."; @@ -95,7 +96,7 @@ std::unique_ptr MaybeCreateFlexfecReceiver( } RTC_DCHECK_EQ(1U, config.protected_media_ssrcs.size()); return std::unique_ptr(new FlexfecReceiver( - clock, config.rtp.remote_ssrc, config.protected_media_ssrcs[0], + clock, config.remote_ssrc, config.protected_media_ssrcs[0], recovered_packet_receiver)); } @@ -105,27 +106,38 @@ FlexfecReceiveStreamImpl::FlexfecReceiveStreamImpl( const Environment& env, Config config, RecoveredPacketReceiver* recovered_packet_receiver, + PacketRouter* packet_router, RtcpRttStats* rtt_stats) - : remote_ssrc_(config.rtp.remote_ssrc), + : env_(env), + remote_ssrc_(config.remote_ssrc), payload_type_(config.payload_type), receiver_(MaybeCreateFlexfecReceiver(&env.clock(), config, recovered_packet_receiver)), rtp_receive_statistics_(ReceiveStatistics::Create(&env.clock())), - rtp_rtcp_(env, - {.audio = false, - .receiver_only = true, - .receive_statistics = rtp_receive_statistics_.get(), - .outgoing_transport = config.rtcp_send_transport, - .rtt_stats = rtt_stats, - .local_media_ssrc = config.rtp.local_ssrc}) { + rtp_rtcp_(ModuleRtpRtcpImpl2::CreateReceiveModule( + env, + {.audio = false, + .receiver_only = true, + .receive_statistics = rtp_receive_statistics_.get(), + .outgoing_transport = config.rtcp_send_transport, + .rtt_stats = rtt_stats}, + [packet_router] { + // Use the same logic as for the video receiver. + if (packet_router != nullptr) { + return packet_router->SsrcOfFirstSender().value_or( + kFallbackRtcpSsrcForVideo); + } else { + return kFallbackRtcpSsrcForVideo; + } + })) { RTC_LOG(LS_INFO) << "FlexfecReceiveStreamImpl: " << config.ToString(); RTC_DCHECK_GE(payload_type_, -1); packet_sequence_checker_.Detach(); // RTCP reporting. - rtp_rtcp_.SetRTCPStatus(config.rtcp_mode); + rtp_rtcp_->SetRTCPStatus(config.rtcp_mode); } FlexfecReceiveStreamImpl::~FlexfecReceiveStreamImpl() { @@ -155,6 +167,7 @@ void FlexfecReceiveStreamImpl::UnregisterFromTransport() { void FlexfecReceiveStreamImpl::OnRtpPacket(const RtpPacketReceived& packet) { RTC_DCHECK_RUN_ON(&packet_sequence_checker_); + env_.event_log().Log(std::make_unique(packet)); if (!receiver_) return; @@ -177,12 +190,4 @@ int FlexfecReceiveStreamImpl::payload_type() const { return payload_type_; } -void FlexfecReceiveStreamImpl::SetLocalSsrc(uint32_t local_ssrc) { - RTC_DCHECK_RUN_ON(&packet_sequence_checker_); - if (local_ssrc == rtp_rtcp_.local_media_ssrc()) - return; - - rtp_rtcp_.SetLocalSsrc(local_ssrc); -} - } // namespace webrtc diff --git a/call/flexfec_receive_stream_impl.h b/call/flexfec_receive_stream_impl.h index 2e1b9c452c1..211cd662385 100644 --- a/call/flexfec_receive_stream_impl.h +++ b/call/flexfec_receive_stream_impl.h @@ -19,6 +19,7 @@ #include "api/sequence_checker.h" #include "call/flexfec_receive_stream.h" #include "call/rtp_packet_sink_interface.h" +#include "modules/pacing/packet_router.h" #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread_annotations.h" @@ -38,6 +39,7 @@ class FlexfecReceiveStreamImpl : public FlexfecReceiveStream { FlexfecReceiveStreamImpl(const Environment& env, Config config, RecoveredPacketReceiver* recovered_packet_receiver, + PacketRouter* packet_router, RtcpRttStats* rtt_stats); // Destruction happens on the worker thread. Prior to destruction the caller // must ensure that a registration with the transport has been cleared. See @@ -61,15 +63,11 @@ class FlexfecReceiveStreamImpl : public FlexfecReceiveStream { void SetPayloadType(int payload_type) override; int payload_type() const override; - // Updates the `rtp_video_stream_receiver_`'s `local_ssrc` when the default - // sender has been created, changed or removed. - void SetLocalSsrc(uint32_t local_ssrc); - uint32_t remote_ssrc() const { return remote_ssrc_; } void SetRtcpMode(RtcpMode mode) override { RTC_DCHECK_RUN_ON(&packet_sequence_checker_); - rtp_rtcp_.SetRTCPStatus(mode); + rtp_rtcp_->SetRTCPStatus(mode); } const ReceiveStatistics* GetStats() const override { @@ -77,6 +75,7 @@ class FlexfecReceiveStreamImpl : public FlexfecReceiveStream { } private: + const Environment env_; RTC_NO_UNIQUE_ADDRESS SequenceChecker packet_sequence_checker_; const uint32_t remote_ssrc_; @@ -90,7 +89,7 @@ class FlexfecReceiveStreamImpl : public FlexfecReceiveStream { // RTCP reporting. const std::unique_ptr rtp_receive_statistics_; - ModuleRtpRtcpImpl2 rtp_rtcp_; + const std::unique_ptr rtp_rtcp_; std::unique_ptr rtp_stream_receiver_ RTC_GUARDED_BY(packet_sequence_checker_); diff --git a/call/flexfec_receive_stream_unittest.cc b/call/flexfec_receive_stream_unittest.cc index 0621014bbb7..c4df175cdfd 100644 --- a/call/flexfec_receive_stream_unittest.cc +++ b/call/flexfec_receive_stream_unittest.cc @@ -12,22 +12,23 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment_factory.h" #include "api/rtp_headers.h" #include "call/flexfec_receive_stream_impl.h" #include "call/rtp_stream_receiver_controller.h" +#include "logging/rtc_event_log/mock/mock_rtc_event_log.h" #include "modules/rtp_rtcp/mocks/mock_recovered_packet_receiver.h" #include "modules/rtp_rtcp/mocks/mock_rtcp_rtt_stats.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" -#include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_transport.h" +#include "test/run_loop.h" namespace webrtc { @@ -44,14 +45,14 @@ FlexfecReceiveStream::Config CreateDefaultConfig( Transport* rtcp_send_transport) { FlexfecReceiveStream::Config config(rtcp_send_transport); config.payload_type = kFlexfecPlType; - config.rtp.remote_ssrc = ByteReader::ReadBigEndian(kFlexfecSsrc); + config.remote_ssrc = ByteReader::ReadBigEndian(kFlexfecSsrc); config.protected_media_ssrcs = { ByteReader::ReadBigEndian(kMediaSsrc)}; EXPECT_TRUE(config.IsCompleteAndEnabled()); return config; } -RtpPacketReceived ParsePacket(ArrayView packet) { +RtpPacketReceived ParsePacket(std::span packet) { RtpPacketReceived parsed_packet(nullptr); EXPECT_TRUE(parsed_packet.Parse(packet)); return parsed_packet; @@ -63,14 +64,13 @@ TEST(FlexfecReceiveStreamConfigTest, IsCompleteAndEnabled) { MockTransport rtcp_send_transport; FlexfecReceiveStream::Config config(&rtcp_send_transport); - config.rtp.local_ssrc = 18374743; config.rtcp_mode = RtcpMode::kCompound; EXPECT_FALSE(config.IsCompleteAndEnabled()); config.payload_type = 123; EXPECT_FALSE(config.IsCompleteAndEnabled()); - config.rtp.remote_ssrc = 238423838; + config.remote_ssrc = 238423838; EXPECT_FALSE(config.IsCompleteAndEnabled()); config.protected_media_ssrcs.push_back(138989393); @@ -85,7 +85,8 @@ class FlexfecReceiveStreamTest : public ::testing::Test { FlexfecReceiveStreamTest() : config_(CreateDefaultConfig(&rtcp_send_transport_)) { receive_stream_ = std::make_unique( - CreateEnvironment(), config_, &recovered_packet_receiver_, &rtt_stats_); + CreateEnvironment(&log_), config_, &recovered_packet_receiver_, + /* packet_router= */ nullptr, &rtt_stats_); receive_stream_->RegisterWithTransport(&rtp_stream_receiver_controller_); } @@ -93,8 +94,9 @@ class FlexfecReceiveStreamTest : public ::testing::Test { receive_stream_->UnregisterFromTransport(); } - AutoThread main_thread_; + test::RunLoop main_thread_; MockTransport rtcp_send_transport_; + MockRtcEventLog log_; FlexfecReceiveStream::Config config_; MockRecoveredPacketReceiver recovered_packet_receiver_; MockRtcpRttStats rtt_stats_; @@ -149,4 +151,9 @@ TEST_F(FlexfecReceiveStreamTest, RecoversPacket) { receive_stream_->UnregisterFromTransport(); } +TEST_F(FlexfecReceiveStreamTest, LogsReceivedPacketToEventLog) { + EXPECT_CALL(log_, LogProxy); + receive_stream_->OnRtpPacket(RtpPacketReceived()); +} + } // namespace webrtc diff --git a/call/packet_receiver.h b/call/packet_receiver.h index c149f8e3ccb..a1b5f66c13f 100644 --- a/call/packet_receiver.h +++ b/call/packet_receiver.h @@ -27,7 +27,7 @@ class PacketReceiver { using OnUndemuxablePacketHandler = absl::AnyInvocable; - // Must be called on the worker thread. + // Can be called on the network thread or the worker thread. // If `media_type` is not Audio or Video, packets may be used for BWE // calculations but are not demuxed. virtual void DeliverRtpPacket( diff --git a/call/payload_type.h b/call/payload_type.h index f67bb6b0042..5e22a1d2568 100644 --- a/call/payload_type.h +++ b/call/payload_type.h @@ -11,38 +11,17 @@ #ifndef CALL_PAYLOAD_TYPE_H_ #define CALL_PAYLOAD_TYPE_H_ -#include #include "absl/strings/string_view.h" +#include "api/payload_type.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" #include "media/base/codec.h" -#include "rtc_base/strong_alias.h" namespace webrtc { class PayloadTypePicker; -class PayloadType : public StrongAlias { - public: - // Non-explicit conversions from and to ints are to be deprecated and - // removed once calling code is upgraded. - PayloadType(uint8_t pt) { value_ = pt; } // NOLINT: explicit - constexpr operator uint8_t() const& { return value_; } // NOLINT: Explicit - static bool IsValid(PayloadType id, bool rtcp_mux) { - // A payload type is a 7-bit value in the RTP header, so max = 127. - // If RTCP multiplexing is used, the numbers from 64 to 95 are reserved - // for RTCP packets. - if (rtcp_mux && (id > 63 && id < 96)) { - return false; - } - return id >= 0 && id <= 127; - } - template - friend void AbslStringify(Sink& sink, const PayloadType pt) { - absl::Format(&sink, "%d", pt.value_); - } -}; - class PayloadTypeSuggester { public: virtual ~PayloadTypeSuggester() = default; @@ -58,6 +37,20 @@ class PayloadTypeSuggester { virtual RTCError AddLocalMapping(absl::string_view mid, PayloadType payload_type, const Codec& codec) = 0; + + // Suggest an ID for a given RTP header extension on a given media section. + // The function will either return an ID already in use on the connection + // or a newly suggested one. + virtual RTCErrorOr SuggestRtpHeaderExtensionId( + absl::string_view mid, + const RtpExtension& extension, + RtpTransceiverIdDomain id_domain) = 0; + // Register an RTP header extension ID as mapped to a specific extension + // for this MID at this time. + [[nodiscard]] virtual RTCError AddRtpHeaderExtensionMapping( + absl::string_view mid, + const RtpExtension& extension, + bool local) = 0; }; } // namespace webrtc diff --git a/call/payload_type_picker.cc b/call/payload_type_picker.cc index c25c0d6f968..3fcbc6a5aec 100644 --- a/call/payload_type_picker.cc +++ b/call/payload_type_picker.cc @@ -11,19 +11,22 @@ #include "call/payload_type_picker.h" #include -#include #include #include #include #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "api/audio_codecs/audio_format.h" +#include "api/payload_type.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" #include "call/payload_type.h" #include "media/base/codec.h" #include "media/base/codec_comparators.h" #include "media/base/media_constants.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/logging.h" #include "rtc_base/string_encode.h" @@ -95,7 +98,7 @@ bool CodecPrefersLowerRange(const Codec& codec) { } RTCErrorOr FindFreePayloadType(const Codec& codec, - std::set seen_pt) { + flat_set seen_pt) { // Prefer to use lower range for codecs that can handle it. bool prefer_lower_range = CodecPrefersLowerRange(codec); if (prefer_lower_range) { @@ -332,4 +335,119 @@ void PayloadTypeRecorder::Rollback() { payload_type_to_codec_ = checkpoint_payload_type_to_codec_; } +RTCError RtpHeaderExtensionRecorder::AddMapping(int id, + absl::string_view uri, + bool encrypt) { + auto it = uri_to_id_.find(std::pair{uri, encrypt}); + if (it != uri_to_id_.end()) { + if (it->second != id) { + // TODO: https://issues.webrtc.org/41480892 - This will return an error in + // the future. + RTC_LOG(LS_ERROR) << "RtpHeaderExtensionRecorder: Redefining mapping for " + << uri << " (encrypt=" << encrypt << ") from " + << it->second << " to " << id; + } + } + uri_to_id_[{std::string(uri), encrypt}] = id; + return RTCError::OK(); +} + +RTCErrorOr RtpHeaderExtensionRecorder::LookupId(absl::string_view uri, + bool encrypt) const { + auto it = uri_to_id_.find(std::pair{uri, encrypt}); + if (it == uri_to_id_.end()) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "No ID found for extension"); + } + return it->second; +} + +void RtpHeaderExtensionRecorder::Commit() { + checkpoint_uri_to_id_ = uri_to_id_; +} + +void RtpHeaderExtensionRecorder::Rollback() { + uri_to_id_ = checkpoint_uri_to_id_; +} + +RTCErrorOr RtpHeaderExtensionPicker::SuggestMapping( + absl::string_view uri, + bool encrypt, + int preferred_id, + RtpTransceiverIdDomain id_domain, + const RtpHeaderExtensionRecorder* excluder) { + // If we already have a mapping for this (uri, encrypt), use it. + for (const auto& entry : entries_) { + if (entry.uri == uri && entry.encrypt == encrypt) { + if (excluder) { + auto result = excluder->LookupId(entry.uri, entry.encrypt); + if (result.ok() && result.value() != entry.id) { + continue; + } + } + return entry.id; + } + } + + // Test compatibility: If preferred_id is provided and free, use it. + if (preferred_id >= 1 && preferred_id <= 255 && + seen_ids_.count(preferred_id) == 0) { + if (preferred_id <= 14) { + AddMapping(preferred_id, uri, encrypt); + return preferred_id; + } + // We allow preferred_id >= 15 even if id_domain is kOneByteOnly because + // it might be a re-negotiation or a test where the ID was explicitly + // assigned. Automatic allocation below will still respect id_domain. + if (preferred_id >= 15) { + AddMapping(preferred_id, uri, encrypt); + return preferred_id; + } + } + + // Find a free ID. + // One-byte range: 1-14. + // We prefer to allocate from the top of the range (14 down to 1). + for (int id = 14; id >= 1; --id) { + if (seen_ids_.count(id) == 0) { + AddMapping(id, uri, encrypt); + return id; + } + } + + if (id_domain == RtpTransceiverIdDomain::kTwoByteAllowed) { + // TODO: issues.webrtc.org/334925828 - add unit tests for this case. + // Two-byte range: 16-255. (Avoid 15, which is special in RFC 8285) + for (int id = 16; id <= 255; ++id) { + if (seen_ids_.count(id) == 0) { + AddMapping(id, uri, encrypt); + return id; + } + } + } + + return RTCError(RTCErrorType::RESOURCE_EXHAUSTED, + "No free RTP extension IDs"); +} + +RTCError RtpHeaderExtensionPicker::AddMapping(int id, + absl::string_view uri, + bool encrypt) { + RTC_DCHECK_GT(id, 0); + RTC_DCHECK_LE(id, 255); + // 15 is special and should be avoided, but allowed in the two-byte form + // according to RFC 8285. But still, it's unexpected to see it used. + if (id == 15) { + RTC_LOG(LS_WARNING) << "Use of special URI extension id 15 encountered."; + } + for (const auto& entry : entries_) { + if (entry.id == id && entry.uri == uri && entry.encrypt == encrypt) { + return RTCError::OK(); + } + } + entries_.push_back({std::string(uri), encrypt, id}); + seen_ids_.insert(id); + return RTCError::OK(); +} + } // namespace webrtc diff --git a/call/payload_type_picker.h b/call/payload_type_picker.h index affe6dd9d78..721ec237c8a 100644 --- a/call/payload_type_picker.h +++ b/call/payload_type_picker.h @@ -11,21 +11,25 @@ #ifndef CALL_PAYLOAD_TYPE_PICKER_H_ #define CALL_PAYLOAD_TYPE_PICKER_H_ -#include -#include +#include #include #include +#include "absl/strings/string_view.h" +#include "api/payload_type.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" #include "call/payload_type.h" #include "media/base/codec.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_map.h" +#include "rtc_base/containers/flat_set.h" namespace webrtc { class PayloadTypeRecorder; -class PayloadTypePicker { +class PayloadTypePicker final { public: PayloadTypePicker(); PayloadTypePicker(const PayloadTypePicker&) = delete; @@ -39,7 +43,7 @@ class PayloadTypePicker { RTCError AddMapping(PayloadType payload_type, Codec codec); private: - class MapEntry { + class MapEntry final { public: MapEntry(PayloadType payload_type, Codec codec) : payload_type_(payload_type), codec_(codec) {} @@ -55,7 +59,7 @@ class PayloadTypePicker { } }; std::vector entries_; - std::set seen_payload_types_; + flat_set seen_payload_types_; template friend void AbslStringify(Sink& sink, const PayloadTypePicker& picker) { sink.Append("Reserved:"); @@ -69,7 +73,7 @@ class PayloadTypePicker { } }; -class PayloadTypeRecorder { +class PayloadTypeRecorder final { public: explicit PayloadTypeRecorder(PayloadTypePicker& suggester) : suggester_(suggester) {} @@ -98,10 +102,49 @@ class PayloadTypeRecorder { private: PayloadTypePicker& suggester_; - std::map payload_type_to_codec_; - std::map checkpoint_payload_type_to_codec_; + flat_map payload_type_to_codec_; + flat_map checkpoint_payload_type_to_codec_; int disallow_redefinition_level_ = 0; - std::set accepted_definitions_; + flat_set accepted_definitions_; +}; + +class RtpHeaderExtensionRecorder final { + public: + RtpHeaderExtensionRecorder() {} + ~RtpHeaderExtensionRecorder() {} + + RTCError AddMapping(int id, absl::string_view uri, bool encrypt); + RTCErrorOr LookupId(absl::string_view uri, bool encrypt) const; + + void Commit(); + void Rollback(); + + private: + // (uri, encrypt) -> id + flat_map, int> uri_to_id_; + flat_map, int> checkpoint_uri_to_id_; +}; + +class RtpHeaderExtensionPicker final { + public: + RtpHeaderExtensionPicker() {} + ~RtpHeaderExtensionPicker() {} + + RTCErrorOr SuggestMapping(absl::string_view uri, + bool encrypt, + int preferred_id, + RtpTransceiverIdDomain id_domain, + const RtpHeaderExtensionRecorder* excluder); + RTCError AddMapping(int id, absl::string_view uri, bool encrypt); + + private: + struct MapEntry { + std::string uri; + bool encrypt; + int id; + }; + std::vector entries_; + flat_set seen_ids_; }; } // namespace webrtc diff --git a/call/payload_type_picker_unittest.cc b/call/payload_type_picker_unittest.cc index 46e0de7ef18..562bb2cb5ea 100644 --- a/call/payload_type_picker_unittest.cc +++ b/call/payload_type_picker_unittest.cc @@ -13,6 +13,7 @@ #include #include "absl/strings/str_cat.h" +#include "api/payload_type.h" #include "api/video_codecs/sdp_video_format.h" #include "call/payload_type.h" #include "media/base/codec.h" @@ -96,7 +97,6 @@ TEST(PayloadTypePicker, RollbackAndCommit) { PayloadTypeRecorder recorder(picker); const PayloadType a_payload_type(123); const PayloadType b_payload_type(124); - const PayloadType not_a_payload_type(44); Codec a_codec = CreateVideoCodec(0, "vp8"); diff --git a/call/rampup_tests.cc b/call/rampup_tests.cc index e7bd6d4ba15..621597afb45 100644 --- a/call/rampup_tests.cc +++ b/call/rampup_tests.cc @@ -278,10 +278,9 @@ void RampUpTester::ModifyFlexfecConfigs( RTC_DCHECK_EQ(1, num_flexfec_streams_); (*receive_configs)[0].payload_type = test::VideoTestConstants::kFlexfecPayloadType; - (*receive_configs)[0].rtp.remote_ssrc = + (*receive_configs)[0].remote_ssrc = test::VideoTestConstants::kFlexfecSendSsrc; (*receive_configs)[0].protected_media_ssrcs = {video_ssrcs_[0]}; - (*receive_configs)[0].rtp.local_ssrc = video_ssrcs_[0]; } void RampUpTester::OnCallsCreated(Call* sender_call, diff --git a/call/receive_stream.h b/call/receive_stream.h index 32678cbd8dc..231f4480561 100644 --- a/call/receive_stream.h +++ b/call/receive_stream.h @@ -34,11 +34,10 @@ class ReceiveStreamInterface { // This member will not change mid-stream and can be assumed to be const // post initialization. uint32_t remote_ssrc = 0; - - // Sender SSRC used for sending RTCP (such as receiver reports). - // This value may change mid-stream and must be done on the same thread - // that the value is read on (i.e. packet delivery). - uint32_t local_ssrc = 0; + // This member is no longer used by WebRTC, but retained in order to + // allow downstream code to compile. + // TODO: issues.webrtc.org/41480926 - Delete when downstream changed. + [[deprecated("No longer used")]] uint32_t local_ssrc = 0; }; protected: diff --git a/call/rtp_bitrate_configurator.cc b/call/rtp_bitrate_configurator.cc index 3e7912b6bce..17f0ca6c83d 100644 --- a/call/rtp_bitrate_configurator.cc +++ b/call/rtp_bitrate_configurator.cc @@ -17,6 +17,8 @@ #include "api/units/data_rate.h" #include "rtc_base/checks.h" +namespace webrtc { + namespace { // Returns its smallest positive argument. If neither argument is positive, @@ -33,7 +35,6 @@ int MinPositive(int a, int b) { } // namespace -namespace webrtc { RtpBitrateConfigurator::RtpBitrateConfigurator( const BitrateConstraints& bitrate_config) : bitrate_config_(bitrate_config), base_bitrate_config_(bitrate_config) { diff --git a/call/rtp_config.cc b/call/rtp_config.cc index 115912cd46d..f48ae304621 100644 --- a/call/rtp_config.cc +++ b/call/rtp_config.cc @@ -19,7 +19,6 @@ #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "rtc_base/checks.h" #include "rtc_base/strings/string_builder.h" diff --git a/call/rtp_demuxer.cc b/call/rtp_demuxer.cc index c49d4fc4f8b..e0783475442 100644 --- a/call/rtp_demuxer.cc +++ b/call/rtp_demuxer.cc @@ -161,7 +161,13 @@ bool RtpDemuxer::AddSink(const RtpDemuxerCriteria& criteria, } for (uint32_t ssrc : criteria.ssrcs()) { - sink_by_ssrc_.emplace(ssrc, sink); + auto result = sink_by_ssrc_.emplace(ssrc, sink); + if (!result.second) { + if (signaled_ssrcs_.find(ssrc) == signaled_ssrcs_.end()) { + result.first->second = sink; + } + } + signaled_ssrcs_.insert(ssrc); } for (uint8_t payload_type : criteria.payload_types()) { @@ -231,10 +237,12 @@ bool RtpDemuxer::CriteriaWouldConflict( for (uint32_t ssrc : criteria.ssrcs()) { const auto sink_by_ssrc = sink_by_ssrc_.find(ssrc); if (sink_by_ssrc != sink_by_ssrc_.end()) { - RTC_LOG(LS_INFO) << criteria.ToString() - << " would conflict with existing sink = " - << sink_by_ssrc->second << " binding by SSRC=" << ssrc; - return true; + if (signaled_ssrcs_.find(ssrc) != signaled_ssrcs_.end()) { + RTC_LOG(LS_INFO) << criteria.ToString() + << " would conflict with existing sink = " + << sink_by_ssrc->second << " binding by SSRC=" << ssrc; + return true; + } } } @@ -270,6 +278,10 @@ void RtpDemuxer::AddSink(absl::string_view rsid, RtpPacketSinkInterface* sink) { bool RtpDemuxer::RemoveSink(const RtpPacketSinkInterface* sink) { RTC_DCHECK(sink); + flat_set ssrcs = GetSsrcsForSink(sink); + for (uint32_t ssrc : ssrcs) { + signaled_ssrcs_.erase(ssrc); + } size_t num_removed = RemoveFromMapByValue(&sink_by_mid_, sink) + RemoveFromMapByValue(&sink_by_ssrc_, sink) + RemoveFromMultimapByValue(&sinks_by_pt_, sink) + @@ -400,8 +412,13 @@ RtpPacketSinkInterface* RtpDemuxer::ResolveSink( return ssrc_sink_it->second; } - // Legacy senders will only signal payload type, support that as last resort. - return ResolveSinkByPayloadType(packet.PayloadType(), ssrc); + if (use_payload_type_demuxing_) { + // Legacy senders will only signal payload type. + // Support that as a last resort. + return ResolveSinkByPayloadType(packet.PayloadType(), ssrc); + } + + return nullptr; } RtpPacketSinkInterface* RtpDemuxer::ResolveSinkByMid(absl::string_view mid, diff --git a/call/rtp_demuxer.h b/call/rtp_demuxer.h index acc985f486d..f2f76cb5625 100644 --- a/call/rtp_demuxer.h +++ b/call/rtp_demuxer.h @@ -136,6 +136,10 @@ class RtpDemuxer { RtpDemuxer(const RtpDemuxer&) = delete; void operator=(const RtpDemuxer&) = delete; + void set_use_payload_type_demuxing(bool enable) { + use_payload_type_demuxing_ = enable; + } + // Registers a sink that will be notified when RTP packets match its given // criteria according to the algorithm described in the class description. // Returns true if the sink was successfully added. @@ -213,6 +217,7 @@ class RtpDemuxer { flat_map, RtpPacketSinkInterface*> sink_by_mid_and_rsid_; flat_map sink_by_rsid_; + flat_set signaled_ssrcs_; // Tracks all the MIDs that have been identified in added criteria. Used to // determine if a packet should be dropped right away because the MID is @@ -230,6 +235,7 @@ class RtpDemuxer { void AddSsrcSinkBinding(uint32_t ssrc, RtpPacketSinkInterface* sink); const bool use_mid_; + bool use_payload_type_demuxing_ = true; }; } // namespace webrtc diff --git a/call/rtp_demuxer_unittest.cc b/call/rtp_demuxer_unittest.cc index 363d58aa132..b878d2bbaef 100644 --- a/call/rtp_demuxer_unittest.cc +++ b/call/rtp_demuxer_unittest.cc @@ -369,6 +369,89 @@ TEST_F(RtpDemuxerTest, OnRtpPacketCalledOnCorrectSinkByPayloadType) { EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); } +TEST_F(RtpDemuxerTest, DontSignalRtpPayloadTypeWhenPtDemuxingDisabled) { + constexpr uint32_t ssrc1 = 10; + constexpr uint32_t ssrc2 = 11; + constexpr uint8_t pt1 = 30; + constexpr uint8_t pt2 = 31; + + // Sink 1 registered when PT demuxing is enabled (default). + MockRtpPacketSink sink1; + RtpDemuxerCriteria criteria1; + criteria1.payload_types() = {pt1}; + EXPECT_TRUE(AddSink(criteria1, &sink1)); + + // Disable PT demuxing. + demuxer_.set_use_payload_type_demuxing(false); + + // Sink 2 registered when PT demuxing is disabled. + MockRtpPacketSink sink2; + RtpDemuxerCriteria criteria2; + criteria2.payload_types() = {pt2}; + EXPECT_TRUE(AddSink(criteria2, &sink2)); + + // Packet with pt1 should not go to sink1 because fallback is disabled. + auto packet1 = CreatePacketWithSsrc(ssrc1); + packet1->SetPayloadType(pt1); + EXPECT_CALL(sink1, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet1)); + + // Packet with pt2 should not go to sink2 because fallback is disabled and it + // was not registered by PT. + auto packet2 = CreatePacketWithSsrc(ssrc2); + packet2->SetPayloadType(pt2); + EXPECT_CALL(sink2, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet2)); +} + +TEST_F(RtpDemuxerTest, DynamicPayloadTypeDemuxing) { + constexpr uint32_t ssrc = 10; + constexpr uint8_t payload_type = 30; + + MockRtpPacketSink sink; + RtpDemuxerCriteria criteria; + criteria.payload_types() = {payload_type}; + + demuxer_.set_use_payload_type_demuxing(false); + EXPECT_TRUE(AddSink(criteria, &sink)); + + auto packet = CreatePacketWithSsrc(ssrc); + packet->SetPayloadType(payload_type); + + EXPECT_CALL(sink, OnRtpPacket(_)).Times(0); + EXPECT_FALSE(demuxer_.OnRtpPacket(*packet)); + + demuxer_.set_use_payload_type_demuxing(true); + + EXPECT_CALL(sink, OnRtpPacket(SamePacketAs(*packet))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet)); +} + +TEST_F(RtpDemuxerTest, SignaledSsrcOverridesLearnedBinding) { + constexpr uint32_t ssrc = 10; + constexpr uint8_t payload_type = 30; + + MockRtpPacketSink sink1; + RtpDemuxerCriteria criteria1; + criteria1.payload_types() = {payload_type}; + EXPECT_TRUE(AddSink(criteria1, &sink1)); + + auto packet1 = CreatePacketWithSsrc(ssrc); + packet1->SetPayloadType(payload_type); + EXPECT_CALL(sink1, OnRtpPacket(SamePacketAs(*packet1))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet1)); + + MockRtpPacketSink sink2; + RtpDemuxerCriteria criteria2; + criteria2.ssrcs().insert(ssrc); + EXPECT_TRUE(AddSink(criteria2, &sink2)); + + auto packet2 = CreatePacketWithSsrc(ssrc); + EXPECT_CALL(sink1, OnRtpPacket(_)).Times(0); + EXPECT_CALL(sink2, OnRtpPacket(SamePacketAs(*packet2))).Times(1); + EXPECT_TRUE(demuxer_.OnRtpPacket(*packet2)); +} + TEST_F(RtpDemuxerTest, PacketsDeliveredInRightOrder) { constexpr uint32_t ssrc = 101; MockRtpPacketSink sink; diff --git a/call/rtp_payload_params.cc b/call/rtp_payload_params.cc index ee3c6e298e3..d5be15c6d2f 100644 --- a/call/rtp_payload_params.cc +++ b/call/rtp_payload_params.cc @@ -24,7 +24,6 @@ #include "api/video/render_resolution.h" #include "api/video/video_codec_constants.h" #include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" #include "api/video/video_timing.h" #include "call/rtp_config.h" #include "common_video/generic_frame_descriptor/generic_frame_info.h" @@ -221,7 +220,7 @@ RTPVideoHeader RtpPayloadParams::GetRtpVideoHeader( &rtp_video_header); } rtp_video_header.simulcastIdx = image.SimulcastIndex().value_or(0); - rtp_video_header.frame_type = image._frameType; + rtp_video_header.frame_type = image.frame_type(); rtp_video_header.rotation = image.rotation_; rtp_video_header.content_type = image.content_type_; rtp_video_header.playout_delay = image.PlayoutDelay(); @@ -233,7 +232,7 @@ RTPVideoHeader RtpPayloadParams::GetRtpVideoHeader( rtp_video_header.video_frame_tracking_id = image.VideoFrameTrackingId(); SetVideoTiming(image, &rtp_video_header.video_timing); - const bool is_keyframe = image._frameType == VideoFrameType::kVideoFrameKey; + const bool is_keyframe = image.IsKey(); const bool first_frame_in_picture = (codec_specific_info && codec_specific_info->codecType == kVideoCodecVP9) ? codec_specific_info->codecSpecific.VP9.first_frame_in_picture diff --git a/call/rtp_payload_params_unittest.cc b/call/rtp_payload_params_unittest.cc index a72826cf1e2..18a3cdfa86e 100644 --- a/call/rtp_payload_params_unittest.cc +++ b/call/rtp_payload_params_unittest.cc @@ -10,6 +10,7 @@ #include "call/rtp_payload_params.h" +#include #include #include #include @@ -213,7 +214,7 @@ TEST(RtpPayloadParamsTest, CreatesGenericDescriptorForVp8) { RtpPayloadParams params(CreateTestEnvironment(), kSsrc1, &state); EncodedImage key_frame_image; - key_frame_image._frameType = VideoFrameType::kVideoFrameKey; + key_frame_image.set_frame_type(VideoFrameType::kVideoFrameKey); CodecSpecificInfo key_frame_info; key_frame_info.codecType = kVideoCodecVP8; key_frame_info.codecSpecific.VP8.temporalIdx = 0; @@ -221,7 +222,7 @@ TEST(RtpPayloadParamsTest, CreatesGenericDescriptorForVp8) { key_frame_image, &key_frame_info, /*shared_frame_id=*/123); EncodedImage delta_t1_image; - delta_t1_image._frameType = VideoFrameType::kVideoFrameDelta; + delta_t1_image.set_frame_type(VideoFrameType::kVideoFrameDelta); CodecSpecificInfo delta_t1_info; delta_t1_info.codecType = kVideoCodecVP8; delta_t1_info.codecSpecific.VP8.temporalIdx = 1; @@ -229,7 +230,7 @@ TEST(RtpPayloadParamsTest, CreatesGenericDescriptorForVp8) { delta_t1_image, &delta_t1_info, /*shared_frame_id=*/124); EncodedImage delta_t0_image; - delta_t0_image._frameType = VideoFrameType::kVideoFrameDelta; + delta_t0_image.set_frame_type(VideoFrameType::kVideoFrameDelta); CodecSpecificInfo delta_t0_info; delta_t0_info.codecType = kVideoCodecVP8; delta_t0_info.codecSpecific.VP8.temporalIdx = 0; @@ -372,7 +373,7 @@ TEST(RtpPayloadParamsTest, GenerateFrameIdWhenExternalFrameIdsAreNotProvided) { state.frame_id = 123; EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); CodecSpecificInfo codec_info; codec_info.codecType = kVideoCodecGeneric; @@ -385,7 +386,7 @@ TEST(RtpPayloadParamsTest, GenerateFrameIdWhenExternalFrameIdsAreNotProvided) { ASSERT_TRUE(header.generic); EXPECT_THAT(header.generic->frame_id, Eq(123)); - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); header = params.GetRtpVideoHeader(encoded_image, &codec_info, std::nullopt); ASSERT_TRUE(header.generic); EXPECT_THAT(header.generic->frame_id, Eq(124)); @@ -397,7 +398,7 @@ TEST(RtpPayloadParamsTest, PictureIdForOldGenericFormat) { EncodedImage encoded_image; CodecSpecificInfo codec_info; codec_info.codecType = kVideoCodecGeneric; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); RtpPayloadParams params( CreateTestEnvironment( @@ -412,7 +413,7 @@ TEST(RtpPayloadParamsTest, PictureIdForOldGenericFormat) { ASSERT_TRUE(generic); EXPECT_EQ(0, generic->picture_id); - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); header = params.GetRtpVideoHeader(encoded_image, &codec_info, 20); generic = std::get_if(&header.video_type_header); ASSERT_TRUE(generic); @@ -423,7 +424,7 @@ TEST(RtpPayloadParamsTest, GenericDescriptorForGenericCodec) { RtpPayloadState state; EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); CodecSpecificInfo codec_info; codec_info.codecType = kVideoCodecGeneric; @@ -442,7 +443,7 @@ TEST(RtpPayloadParamsTest, GenericDescriptorForGenericCodec) { EXPECT_THAT(header.generic->dependencies, IsEmpty()); EXPECT_THAT(header.generic->chain_diffs, ElementsAre(0)); - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); header = params.GetRtpVideoHeader(encoded_image, &codec_info, 3); ASSERT_TRUE(header.generic); EXPECT_THAT(header.generic->frame_id, Eq(3)); @@ -461,7 +462,7 @@ TEST(RtpPayloadParamsTest, SetsGenericFromGenericFrameInfo) { RtpPayloadParams params(CreateTestEnvironment(), kSsrc1, &state); - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_info.generic_frame_info = GenericFrameInfo::Builder().S(1).T(0).Dtis("S").Build(); codec_info.generic_frame_info->encoder_buffers = { @@ -479,7 +480,7 @@ TEST(RtpPayloadParamsTest, SetsGenericFromGenericFrameInfo) { ElementsAre(DecodeTargetIndication::kSwitch)); EXPECT_THAT(key_header.generic->chain_diffs, SizeIs(2)); - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); codec_info.generic_frame_info = GenericFrameInfo::Builder().S(2).T(3).Dtis("D").Build(); codec_info.generic_frame_info->encoder_buffers = { @@ -513,7 +514,7 @@ class RtpPayloadParamsVp8ToGenericTest : public ::testing::Test { uint16_t width = 0, uint16_t height = 0) { EncodedImage encoded_image; - encoded_image._frameType = frame_type; + encoded_image.set_frame_type(frame_type); encoded_image._encodedWidth = width; encoded_image._encodedHeight = height; @@ -553,7 +554,7 @@ TEST_F(RtpPayloadParamsVp8ToGenericTest, TooHighTemporalIndex) { ConvertAndCheck(0, 0, VideoFrameType::kVideoFrameKey, kNoSync, {}, 480, 360); EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); CodecSpecificInfo codec_info; codec_info.codecType = kVideoCodecVP8; codec_info.codecSpecific.VP8.temporalIdx = @@ -605,7 +606,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, NoScalability) { codec_info.end_of_picture = true; // Key frame. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_info.codecSpecific.VP9.inter_pic_predicted = false; codec_info.codecSpecific.VP9.num_ref_pics = 0; RTPVideoHeader header = params.GetRtpVideoHeader(encoded_image, &codec_info, @@ -623,7 +624,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, NoScalability) { EXPECT_EQ(header.generic->chain_diffs[0], 0); // Delta frame. - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); codec_info.codecSpecific.VP9.inter_pic_predicted = true; codec_info.codecSpecific.VP9.num_ref_pics = 1; codec_info.codecSpecific.VP9.p_diff[0] = 1; @@ -657,7 +658,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, NoScalabilityNonFlexibleMode) { codec_info.end_of_picture = true; // Key frame. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_info.codecSpecific.VP9.inter_pic_predicted = false; RTPVideoHeader key_header = params.GetRtpVideoHeader(encoded_image, &codec_info, @@ -674,7 +675,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, NoScalabilityNonFlexibleMode) { ASSERT_THAT(key_header.generic->chain_diffs, Not(IsEmpty())); EXPECT_EQ(key_header.generic->chain_diffs[0], 0); - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); codec_info.codecSpecific.VP9.inter_pic_predicted = true; RTPVideoHeader delta_header = params.GetRtpVideoHeader(encoded_image, &codec_info, @@ -710,7 +711,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith2Layers) { RTPVideoHeader headers[6]; // Key frame. - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.num_ref_pics = 0; info.codecSpecific.VP9.temporal_up_switch = true; @@ -719,7 +720,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith2Layers) { // Delta frames. info.codecSpecific.VP9.inter_pic_predicted = true; - image._frameType = VideoFrameType::kVideoFrameDelta; + image.set_frame_type(VideoFrameType::kVideoFrameDelta); info.codecSpecific.VP9.temporal_up_switch = true; info.codecSpecific.VP9.temporal_idx = 1; @@ -821,7 +822,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith3Layers) { RTPVideoHeader headers[9]; // Key frame. - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.num_ref_pics = 0; info.codecSpecific.VP9.temporal_up_switch = true; @@ -830,7 +831,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, TemporalScalabilityWith3Layers) { // Delta frames. info.codecSpecific.VP9.inter_pic_predicted = true; - image._frameType = VideoFrameType::kVideoFrameDelta; + image.set_frame_type(VideoFrameType::kVideoFrameDelta); info.codecSpecific.VP9.temporal_up_switch = true; info.codecSpecific.VP9.temporal_idx = 2; @@ -974,7 +975,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, SpatialScalabilityKSvc) { RTPVideoHeader headers[4]; // Key frame. - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); image.SetSpatialIndex(0); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.inter_layer_predicted = false; @@ -993,7 +994,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, SpatialScalabilityKSvc) { // Delta frames. info.codecSpecific.VP9.inter_pic_predicted = true; - image._frameType = VideoFrameType::kVideoFrameDelta; + image.set_frame_type(VideoFrameType::kVideoFrameDelta); info.codecSpecific.VP9.num_ref_pics = 1; info.codecSpecific.VP9.p_diff[0] = 1; @@ -1012,7 +1013,8 @@ TEST(RtpPayloadParamsVp9ToGenericTest, SpatialScalabilityKSvc) { headers[3] = params.GetRtpVideoHeader(image, &info, /*shared_frame_id=*/7); ASSERT_TRUE(headers[0].generic); - int num_decode_targets = headers[0].generic->decode_target_indications.size(); + size_t num_decode_targets = + headers[0].generic->decode_target_indications.size(); // Rely on implementation detail there are always kMaxTemporalStreams temporal // layers assumed, in particular assume Decode Target#0 matches layer S0T0, // and Decode Target#kMaxTemporalStreams matches layer S1T0. @@ -1083,7 +1085,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, RTPVideoHeader headers[3]; // Key frame. - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); image.SetSpatialIndex(0); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.inter_layer_predicted = false; @@ -1094,7 +1096,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, headers[0] = params.GetRtpVideoHeader(image, &info, /*shared_frame_id=*/1); // S0 delta frame. - image._frameType = VideoFrameType::kVideoFrameDelta; + image.set_frame_type(VideoFrameType::kVideoFrameDelta); info.codecSpecific.VP9.num_spatial_layers = 2; info.codecSpecific.VP9.non_ref_for_inter_layer_pred = false; info.codecSpecific.VP9.first_frame_in_picture = true; @@ -1190,21 +1192,21 @@ TEST(RtpPayloadParamsVp9ToGenericTest, ChangeFirstActiveLayer) { // S0 key frame. info.codecSpecific.VP9.num_spatial_layers = 2; info.codecSpecific.VP9.first_active_layer = 0; - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); image.SetSpatialIndex(0); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.num_ref_pics = 0; headers[0] = params.GetRtpVideoHeader(image, &info, /*shared_frame_id=*/0); // S1 key frame. - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); image.SetSpatialIndex(1); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.num_ref_pics = 0; headers[1] = params.GetRtpVideoHeader(image, &info, /*shared_frame_id=*/1); // S0 delta frame. - image._frameType = VideoFrameType::kVideoFrameDelta; + image.set_frame_type(VideoFrameType::kVideoFrameDelta); image.SetSpatialIndex(0); info.codecSpecific.VP9.inter_pic_predicted = true; info.codecSpecific.VP9.num_ref_pics = 1; @@ -1212,7 +1214,7 @@ TEST(RtpPayloadParamsVp9ToGenericTest, ChangeFirstActiveLayer) { headers[2] = params.GetRtpVideoHeader(image, &info, /*shared_frame_id=*/2); // S1 delta frame. - image._frameType = VideoFrameType::kVideoFrameDelta; + image.set_frame_type(VideoFrameType::kVideoFrameDelta); info.codecSpecific.VP9.inter_pic_predicted = true; info.codecSpecific.VP9.num_ref_pics = 1; info.codecSpecific.VP9.p_diff[0] = 1; @@ -1221,14 +1223,14 @@ TEST(RtpPayloadParamsVp9ToGenericTest, ChangeFirstActiveLayer) { // S2 key frame info.codecSpecific.VP9.num_spatial_layers = 3; info.codecSpecific.VP9.first_active_layer = 2; - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); image.SetSpatialIndex(2); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.num_ref_pics = 0; headers[4] = params.GetRtpVideoHeader(image, &info, /*shared_frame_id=*/4); // S2 delta frame. - image._frameType = VideoFrameType::kVideoFrameDelta; + image.set_frame_type(VideoFrameType::kVideoFrameDelta); info.codecSpecific.VP9.inter_pic_predicted = true; info.codecSpecific.VP9.num_ref_pics = 1; info.codecSpecific.VP9.p_diff[0] = 1; @@ -1237,14 +1239,14 @@ TEST(RtpPayloadParamsVp9ToGenericTest, ChangeFirstActiveLayer) { // S0 key frame after pause. info.codecSpecific.VP9.num_spatial_layers = 2; info.codecSpecific.VP9.first_active_layer = 0; - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); image.SetSpatialIndex(0); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.num_ref_pics = 0; headers[6] = params.GetRtpVideoHeader(image, &info, /*shared_frame_id=*/6); // S1 key frame. - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); image.SetSpatialIndex(1); info.codecSpecific.VP9.inter_pic_predicted = false; info.codecSpecific.VP9.num_ref_pics = 0; @@ -1352,7 +1354,7 @@ class RtpPayloadParamsH264ToGenericTest : public ::testing::Test { DecodeTargetIndication::kSwitch, DecodeTargetIndication::kSwitch}) { EncodedImage encoded_image; - encoded_image._frameType = frame_type; + encoded_image.set_frame_type(frame_type); encoded_image._encodedWidth = width; encoded_image._encodedHeight = height; @@ -1395,7 +1397,7 @@ TEST_F(RtpPayloadParamsH264ToGenericTest, TooHighTemporalIndex) { ConvertAndCheck(0, 0, VideoFrameType::kVideoFrameKey, kNoSync, {}, 480, 360); EncodedImage encoded_image; - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); CodecSpecificInfo codec_info; codec_info.codecType = kVideoCodecH264; codec_info.codecSpecific.H264.temporal_idx = diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc index 846ee3ff4f5..c9b250258b9 100644 --- a/call/rtp_transport_controller_send.cc +++ b/call/rtp_transport_controller_send.cc @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/call/transport.h" #include "api/fec_controller.h" #include "api/frame_transformer_interface.h" @@ -901,7 +901,7 @@ void RtpTransportControllerSend::PostUpdates(NetworkControlUpdate update) { void RtpTransportControllerSend::OnReport( Timestamp receive_time, - ArrayView report_blocks) { + std::span report_blocks) { RTC_DCHECK_RUN_ON(&sequence_checker_); if (report_blocks.empty()) return; diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h index 26becb0f915..f5d0cdef5fe 100644 --- a/call/rtp_transport_controller_send.h +++ b/call/rtp_transport_controller_send.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/fec_controller.h" #include "api/frame_transformer_interface.h" @@ -129,7 +129,7 @@ class RtpTransportControllerSend final void OnReceiverEstimatedMaxBitrate(Timestamp receive_time, DataRate bitrate) override; void OnReport(Timestamp receive_time, - ArrayView report_blocks) override; + std::span report_blocks) override; void OnRttUpdate(Timestamp receive_time, TimeDelta rtt) override; void OnTransportFeedback(Timestamp receive_time, const rtcp::TransportFeedback& feedback) override; diff --git a/call/rtp_transport_controller_send_unittest.cc b/call/rtp_transport_controller_send_unittest.cc index 3e07614c36f..5bf29ea7d60 100644 --- a/call/rtp_transport_controller_send_unittest.cc +++ b/call/rtp_transport_controller_send_unittest.cc @@ -23,10 +23,10 @@ #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h" #include "rtc_base/containers/flat_map.h" -#include "rtc_base/thread.h" #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { namespace { @@ -102,7 +102,7 @@ rtcp::CongestionControlFeedback GenerateFeedback( TEST(RtpTransportControllerSendTest, IgnoresFeedbackForReportedReceivedPacketThatWereNotSent) { - AutoThread main_thread; + test::RunLoop main_thread; RtpTransportControllerSend transport({.env = CreateTestEnvironment()}); transport.SetPreferredRtcpCcAckType(RtcpFeedbackType::CCFB); PacketSender sender(transport); @@ -131,7 +131,7 @@ TEST(RtpTransportControllerSendTest, AccumulatesNumberOfReportedReceivedPacketsPerSsrcPerEcnMarkingType) { constexpr uint32_t kSsrc1 = 1'000; constexpr uint32_t kSsrc2 = 2'000; - AutoThread main_thread; + test::RunLoop main_thread; RtpTransportControllerSend transport({.env = CreateTestEnvironment()}); transport.SetPreferredRtcpCcAckType(RtcpFeedbackType::CCFB); @@ -176,7 +176,7 @@ TEST(RtpTransportControllerSendTest, } TEST(RtpTransportControllerSendTest, CalculatesNumberOfBleachedPackets) { - AutoThread main_thread; + test::RunLoop main_thread; RtpTransportControllerSend transport({.env = CreateTestEnvironment()}); transport.SetPreferredRtcpCcAckType(RtcpFeedbackType::CCFB); PacketSender sender(transport); @@ -211,7 +211,7 @@ TEST(RtpTransportControllerSendTest, CalculatesNumberOfBleachedPackets) { TEST(RtpTransportControllerSendTest, AccumulatesNumberOfReportedLostAndRecoveredPackets) { - AutoThread main_thread; + test::RunLoop main_thread; RtpTransportControllerSend transport({.env = CreateTestEnvironment()}); transport.SetPreferredRtcpCcAckType(RtcpFeedbackType::CCFB); @@ -258,7 +258,7 @@ TEST(RtpTransportControllerSendTest, TEST(RtpTransportControllerSendTest, DoesNotCountGapsInSequenceNumberBetweenReportsAsLoss) { - AutoThread main_thread; + test::RunLoop main_thread; RtpTransportControllerSend transport({.env = CreateTestEnvironment()}); transport.SetPreferredRtcpCcAckType(RtcpFeedbackType::CCFB); diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index 8a09f25ce03..7267add42b7 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,6 @@ #include "absl/algorithm/container.h" #include "absl/base/nullability.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/call/bitrate_allocation.h" #include "api/crypto/crypto_options.h" #include "api/environment/environment.h" @@ -292,7 +292,7 @@ std::vector CreateRtpStreamSenders( configuration.need_rtp_packet_infos = rtp_config.lntf.enabled; - auto rtp_rtcp = std::make_unique(env, configuration); + auto rtp_rtcp = ModuleRtpRtcpImpl2::CreateSendModule(env, configuration); rtp_rtcp->SetSendingStatus(false); rtp_rtcp->SetSendingMediaStatus(false); rtp_rtcp->SetRTCPStatus(RtcpMode::kCompound); @@ -347,7 +347,7 @@ bool TransportSeqNumExtensionConfigured(const RtpConfig& config) { bool IsFirstFrameOfACodedVideoSequence( const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info) { - if (encoded_image._frameType != VideoFrameType::kVideoFrameKey) { + if (!encoded_image.IsKey()) { return false; } @@ -551,7 +551,7 @@ EncodedImageCallback::Result RtpVideoSender::OnEncodedImage( const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info) { fec_controller_->UpdateWithEncodedData(encoded_image.size(), - encoded_image._frameType); + encoded_image.frame_type()); MutexLock lock(&mutex_); RTC_DCHECK(!rtp_streams_.empty()); if (!active_) @@ -572,7 +572,7 @@ EncodedImageCallback::Result RtpVideoSender::OnEncodedImage( if (!rtp_streams_[simulcast_index].rtp_rtcp->OnSendingRtpFrame( encoded_image.RtpTimestamp(), encoded_image.capture_time_ms_, rtp_config_.GetStreamConfig(simulcast_index).payload_type, - encoded_image._frameType == VideoFrameType::kVideoFrameKey)) { + encoded_image.IsKey())) { // The payload router could be active but this module isn't sending. return Result(Result::ERROR_SEND_FAILED); } @@ -620,12 +620,12 @@ EncodedImageCallback::Result RtpVideoSender::OnEncodedImage( expected_retransmission_time, csrcs_); if (frame_count_observer_) { FrameCounts& counts = frame_counts_[simulcast_index]; - if (encoded_image._frameType == VideoFrameType::kVideoFrameKey) { + if (encoded_image.IsKey()) { ++counts.key_frames; - } else if (encoded_image._frameType == VideoFrameType::kVideoFrameDelta) { + } else if (encoded_image.IsDelta()) { ++counts.delta_frames; } else { - RTC_DCHECK(encoded_image._frameType == VideoFrameType::kEmptyFrame); + RTC_DCHECK(encoded_image.frame_type() == VideoFrameType::kEmptyFrame); } frame_count_observer_->FrameCountUpdated( counts, rtp_config_.ssrcs[simulcast_index]); @@ -636,6 +636,10 @@ EncodedImageCallback::Result RtpVideoSender::OnEncodedImage( return Result(Result::OK, rtp_timestamp); } +void RtpVideoSender::OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) {} + void RtpVideoSender::OnBitrateAllocationUpdated( const VideoBitrateAllocation& bitrate) { RTC_DCHECK_RUN_ON(&transport_checker_); @@ -691,6 +695,11 @@ void RtpVideoSender::OnVideoLayersAllocationUpdated( transport_queue_.PostTask( SafeTask(safety_.flag(), [this, sending = std::move(sending)] { RTC_DCHECK_RUN_ON(&transport_checker_); + // It's possible for another task to be scheduled on the transport + // checker ahead of this call that makes the sender not active. + if (!IsActive()) { + return; + } RTC_CHECK_EQ(sending.size(), rtp_streams_.size()); for (size_t i = 0; i < sending.size(); ++i) { SetModuleIsActive(sending[i], *rtp_streams_[i].rtp_rtcp); @@ -715,7 +724,7 @@ DataRate RtpVideoSender::GetPostEncodeOverhead() const { return post_encode_overhead; } -void RtpVideoSender::DeliverRtcp(ArrayView packet) { +void RtpVideoSender::DeliverRtcp(std::span packet) { // Runs on a network thread. for (const RtpStreamSender& stream : rtp_streams_) stream.rtp_rtcp->IncomingRtcpPacket(packet); @@ -916,7 +925,7 @@ uint32_t RtpVideoSender::GetProtectionBitrateBps() const { std::vector RtpVideoSender::GetSentRtpPacketInfos( uint32_t ssrc, - ArrayView sequence_numbers) const { + std::span sequence_numbers) const { for (const auto& rtp_stream : rtp_streams_) { if (ssrc == rtp_stream.rtp_rtcp->SSRC()) { return rtp_stream.rtp_rtcp->GetSentRtpPacketInfos(sequence_numbers); @@ -1011,7 +1020,7 @@ void RtpVideoSender::OnPacketFeedbackVector( // clean up anyway. continue; } - ArrayView rtp_sequence_numbers(kv.second); + std::span rtp_sequence_numbers(kv.second); it->second->OnPacketsAcknowledged(rtp_sequence_numbers); } } @@ -1023,7 +1032,7 @@ void RtpVideoSender::SetEncodingData(size_t width, rtp_config_.max_packet_size); } -void RtpVideoSender::SetCsrcs(ArrayView csrcs) { +void RtpVideoSender::SetCsrcs(std::span csrcs) { MutexLock lock(&mutex_); csrcs_.assign(csrcs.begin(), csrcs.begin() + std::min(csrcs.size(), kRtpCsrcSize)); diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index 77f60ef7a6b..5d35e5d4929 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include "absl/base/nullability.h" -#include "api/array_view.h" #include "api/call/bitrate_allocation.h" #include "api/call/transport.h" #include "api/crypto/crypto_options.h" @@ -113,7 +113,7 @@ class RtpVideoSender : public RtpVideoSenderInterface, std::map GetRtpPayloadStates() const RTC_LOCKS_EXCLUDED(mutex_) override; - void DeliverRtcp(ArrayView packet) + void DeliverRtcp(std::span packet) RTC_LOCKS_EXCLUDED(mutex_) override; // Implements webrtc::VCMProtectionCallback. int ProtectionRequest(const FecProtectionParams* delta_params, @@ -138,6 +138,10 @@ class RtpVideoSender : public RtpVideoSenderInterface, const CodecSpecificInfo* codec_specific_info) RTC_LOCKS_EXCLUDED(mutex_) override; + void OnFrameDropped(uint32_t rtp_timestamp, + int spatial_id, + bool is_end_of_temporal_unit) override; + void OnBitrateAllocationUpdated(const VideoBitrateAllocation& bitrate) RTC_LOCKS_EXCLUDED(mutex_) override; void OnVideoLayersAllocationUpdated( @@ -154,12 +158,12 @@ class RtpVideoSender : public RtpVideoSenderInterface, // Sets the list of CSRCs to be included in every packet. If more than // kRtpCsrcSize CSRCs are provided, only the first kRtpCsrcSize elements are // kept. - void SetCsrcs(ArrayView csrcs) + void SetCsrcs(std::span csrcs) RTC_LOCKS_EXCLUDED(mutex_) override; std::vector GetSentRtpPacketInfos( uint32_t ssrc, - ArrayView sequence_numbers) const + std::span sequence_numbers) const RTC_LOCKS_EXCLUDED(mutex_) override; // From StreamFeedbackObserver. diff --git a/call/rtp_video_sender_interface.h b/call/rtp_video_sender_interface.h index 754fe03cd48..035044cd82c 100644 --- a/call/rtp_video_sender_interface.h +++ b/call/rtp_video_sender_interface.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/call/bitrate_allocation.h" #include "api/fec_controller_override.h" #include "api/video/video_layers_allocation.h" @@ -40,7 +40,7 @@ class RtpVideoSenderInterface : public EncodedImageCallback, virtual std::map GetRtpStates() const = 0; virtual std::map GetRtpPayloadStates() const = 0; - virtual void DeliverRtcp(ArrayView packet) = 0; + virtual void DeliverRtcp(std::span packet) = 0; virtual void OnBitrateAllocationUpdated( const VideoBitrateAllocation& bitrate) = 0; @@ -55,10 +55,10 @@ class RtpVideoSenderInterface : public EncodedImageCallback, virtual void SetEncodingData(size_t width, size_t height, size_t num_temporal_layers) = 0; - virtual void SetCsrcs(ArrayView csrcs) = 0; + virtual void SetCsrcs(std::span csrcs) = 0; virtual std::vector GetSentRtpPacketInfos( uint32_t ssrc, - ArrayView sequence_numbers) const = 0; + std::span sequence_numbers) const = 0; // Implements FecControllerOverride. void SetFecAllowed(bool fec_allowed) override = 0; diff --git a/call/rtp_video_sender_unittest.cc b/call/rtp_video_sender_unittest.cc index b0799b098d4..d7d30dfb290 100644 --- a/call/rtp_video_sender_unittest.cc +++ b/call/rtp_video_sender_unittest.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/call/bitrate_allocation.h" #include "api/call/transport.h" #include "api/crypto/crypto_options.h" @@ -40,6 +40,7 @@ #include "api/video/encoded_image.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" +#include "api/video/video_layers_allocation.h" #include "api/video_codecs/video_encoder.h" #include "call/rtp_config.h" #include "call/rtp_transport_config.h" @@ -137,7 +138,7 @@ VideoSendStream::Config CreateVideoSendStreamConfig( const std::vector& ssrcs, const std::vector& rtx_ssrcs, int payload_type, - ArrayView payload_types) { + std::span payload_types) { VideoSendStream::Config config(transport); config.rtp.ssrcs = ssrcs; config.rtp.rtx.ssrcs = rtx_ssrcs; @@ -292,7 +293,7 @@ TEST(RtpVideoSenderTest, SendOnOneModule) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {}); @@ -317,7 +318,7 @@ TEST(RtpVideoSenderTest, OnEncodedImageReturnOkWhenSendingTrue) { EncodedImage encoded_image_1; encoded_image_1.SetRtpTimestamp(1); encoded_image_1.capture_time_ms_ = 2; - encoded_image_1._frameType = VideoFrameType::kVideoFrameKey; + encoded_image_1.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image_1.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, @@ -341,7 +342,7 @@ TEST(RtpVideoSenderTest, OnEncodedImageReturnErrorCodeWhenSendingFalse) { EncodedImage encoded_image_1; encoded_image_1.SetRtpTimestamp(1); encoded_image_1.capture_time_ms_ = 2; - encoded_image_1._frameType = VideoFrameType::kVideoFrameKey; + encoded_image_1.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image_1.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); EncodedImage encoded_image_2(encoded_image_1); @@ -369,7 +370,7 @@ TEST(RtpVideoSenderTest, EncodedImage encoded_image_1; encoded_image_1.SetRtpTimestamp(1); encoded_image_1.capture_time_ms_ = 2; - encoded_image_1._frameType = VideoFrameType::kVideoFrameKey; + encoded_image_1.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image_1.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); EncodedImage encoded_image_2(encoded_image_1); encoded_image_2.SetSimulcastIndex(1); @@ -455,10 +456,10 @@ TEST(RtpVideoSenderTest, FrameCountCallbacks) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); // No callbacks when not active. EXPECT_CALL(callback, FrameCountUpdated).Times(0); @@ -479,7 +480,7 @@ TEST(RtpVideoSenderTest, FrameCountCallbacks) { ::testing::Mock::VerifyAndClearExpectations(&callback); - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); EXPECT_CALL(callback, FrameCountUpdated(_, kSsrc1)) .WillOnce(SaveArg<0>(&frame_counts)); EXPECT_EQ(EncodedImageCallback::Result::OK, @@ -501,7 +502,7 @@ TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); // Send two tiny images, mapping to two RTP packets. Capture sequence numbers. @@ -511,7 +512,7 @@ TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { .Times(2) .WillRepeatedly( [&rtp_sequence_numbers, &transport_sequence_numbers]( - ArrayView packet, const PacketOptions& options) { + std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); rtp_sequence_numbers.push_back(rtp_packet.SequenceNumber()); @@ -537,13 +538,13 @@ TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { EXPECT_CALL(test.transport(), SendRtp) .Times(2) .WillRepeatedly([&retransmitted_rtp_sequence_numbers]( - ArrayView packet, + std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); // Capture the retransmitted sequence number from the RTX header. - ArrayView payload = rtp_packet.payload(); + std::span payload = rtp_packet.payload(); retransmitted_rtp_sequence_numbers.push_back( ByteReader::ReadBigEndian(payload.data())); return true; @@ -578,13 +579,13 @@ TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { // still be retransmitted. test.AdvanceTime(TimeDelta::Millis(33)); EXPECT_CALL(test.transport(), SendRtp) - .WillOnce([&lost_packet_feedback](ArrayView packet, + .WillOnce([&lost_packet_feedback](std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); // Capture the retransmitted sequence number from the RTX header. - ArrayView payload = rtp_packet.payload(); + std::span payload = rtp_packet.payload(); EXPECT_EQ(lost_packet_feedback.rtp_sequence_number, ByteReader::ReadBigEndian(payload.data())); return true; @@ -666,7 +667,7 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData( EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); encoded_image.SetSimulcastIndex(0); @@ -681,7 +682,7 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { EXPECT_CALL(test.transport(), SendRtp) .WillOnce( [&frame1_rtp_sequence_number, &frame1_transport_sequence_number]( - ArrayView packet, const PacketOptions& options) { + std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); frame1_rtp_sequence_number = rtp_packet.SequenceNumber(); @@ -700,7 +701,7 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { EXPECT_CALL(test.transport(), SendRtp) .WillOnce( [&frame2_rtp_sequence_number, &frame2_transport_sequence_number]( - ArrayView packet, const PacketOptions& options) { + std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); frame2_rtp_sequence_number = rtp_packet.SequenceNumber(); @@ -717,7 +718,7 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { // Inject a transport feedback where the packet for the first frame is lost, // expect a retransmission for it. EXPECT_CALL(test.transport(), SendRtp) - .WillOnce([&frame1_rtp_sequence_number](ArrayView packet, + .WillOnce([&frame1_rtp_sequence_number](std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); @@ -725,7 +726,7 @@ TEST(RtpVideoSenderTest, EarlyRetransmits) { // Retransmitted sequence number from the RTX header should match // the lost packet. - ArrayView payload = rtp_packet.payload(); + std::span payload = rtp_packet.payload(); EXPECT_EQ(ByteReader::ReadBigEndian(payload.data()), frame1_rtp_sequence_number); return true; @@ -760,7 +761,7 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) { std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -785,7 +786,7 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) { // Send two tiny images, mapping to single RTP packets. // Send in key frame. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_specific.generic_frame_info = GenericFrameInfo::Builder().T(0).Dtis("S").Build(); codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; @@ -797,7 +798,7 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) { sent_packets.back().HasExtension()); // Send in delta frame. - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); codec_specific.template_structure = std::nullopt; codec_specific.generic_frame_info = GenericFrameInfo::Builder().T(1).Dtis("D").Build(); @@ -824,7 +825,7 @@ TEST(RtpVideoSenderTest, SimulcastIndependentFrameIds) { std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -845,7 +846,7 @@ TEST(RtpVideoSenderTest, SimulcastIndependentFrameIds) { }; codec_specific.generic_frame_info = GenericFrameInfo::Builder().T(0).Dtis("S").Build(); - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; encoded_image.SetSimulcastIndex(0); @@ -883,7 +884,7 @@ TEST(RtpVideoSenderTest, std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -904,7 +905,7 @@ TEST(RtpVideoSenderTest, }; codec_specific.generic_frame_info = GenericFrameInfo::Builder().T(0).Dtis("S").Build(); - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; encoded_image.SetSimulcastIndex(0); @@ -938,7 +939,7 @@ TEST(RtpVideoSenderTest, MixedCodecSimulcastPayloadType) { std::vector sent_packets; EXPECT_CALL(test.transport(), SendRtp) .Times(3) - .WillRepeatedly([&](ArrayView packet, + .WillRepeatedly([&](std::span packet, const PacketOptions& options) -> bool { RtpPacket& rtp_packet = sent_packets.emplace_back(); EXPECT_TRUE(rtp_packet.Parse(packet)); @@ -983,7 +984,7 @@ TEST(RtpVideoSenderTest, MixedCodecSimulcastPayloadType) { EXPECT_CALL(test.transport(), SendRtp) .Times(3) .WillRepeatedly( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { RtpPacket& rtp_packet = sent_rtx_packets.emplace_back(); EXPECT_TRUE(rtp_packet.Parse(packet)); return true; @@ -1009,14 +1010,14 @@ TEST(RtpVideoSenderTest, std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions&) { + [&](std::span packet, const PacketOptions&) { EXPECT_TRUE(sent_packets.emplace_back(&extensions).Parse(packet)); return true; }); test.SetSending(true); EncodedImage key_frame_image; - key_frame_image._frameType = VideoFrameType::kVideoFrameKey; + key_frame_image.set_frame_type(VideoFrameType::kVideoFrameKey); key_frame_image.SetEncodedData( EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); CodecSpecificInfo key_frame_info; @@ -1026,7 +1027,7 @@ TEST(RtpVideoSenderTest, EncodedImageCallback::Result::OK); EncodedImage delta_image; - delta_image._frameType = VideoFrameType::kVideoFrameDelta; + delta_image.set_frame_type(VideoFrameType::kVideoFrameDelta); delta_image.SetEncodedData( EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); CodecSpecificInfo delta_info; @@ -1055,7 +1056,7 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptorForVp9) { std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -1065,7 +1066,7 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptorForVp9) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData( EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); @@ -1111,7 +1112,7 @@ TEST(RtpVideoSenderTest, std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -1121,7 +1122,7 @@ TEST(RtpVideoSenderTest, EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image._encodedWidth = 320; encoded_image._encodedHeight = 180; encoded_image.SetEncodedData( @@ -1140,7 +1141,7 @@ TEST(RtpVideoSenderTest, EncodedImageCallback::Result::OK); // Send in 2nd picture. - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); encoded_image.SetRtpTimestamp(3000); codec_specific.codecSpecific.VP9.inter_pic_predicted = true; codec_specific.codecSpecific.VP9.num_ref_pics = 1; @@ -1165,7 +1166,7 @@ TEST(RtpVideoSenderTest, std::vector sent_packets; EXPECT_CALL(test.transport(), SendRtp(_, _)) .Times(2) - .WillRepeatedly([&](ArrayView packet, + .WillRepeatedly([&](std::span packet, const PacketOptions& options) -> bool { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); @@ -1176,7 +1177,7 @@ TEST(RtpVideoSenderTest, EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image._encodedWidth = 320; encoded_image._encodedHeight = 180; encoded_image.SetEncodedData( @@ -1191,7 +1192,7 @@ TEST(RtpVideoSenderTest, EncodedImageCallback::Result::OK); // Send in 2nd picture. - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); encoded_image.SetRtpTimestamp(3000); EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, EncodedImageCallback::Result::OK); @@ -1223,7 +1224,7 @@ TEST(RtpVideoSenderTest, GenerateDependecyDescriptorForGenericCodecs) { std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -1233,7 +1234,7 @@ TEST(RtpVideoSenderTest, GenerateDependecyDescriptorForGenericCodecs) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image._encodedWidth = 320; encoded_image._encodedHeight = 180; encoded_image.SetEncodedData( @@ -1248,7 +1249,7 @@ TEST(RtpVideoSenderTest, GenerateDependecyDescriptorForGenericCodecs) { EncodedImageCallback::Result::OK); // Send in 2nd picture. - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); encoded_image.SetRtpTimestamp(3000); EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, EncodedImageCallback::Result::OK); @@ -1269,7 +1270,7 @@ TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) { std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -1294,7 +1295,7 @@ TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) { // Send two tiny images, mapping to single RTP packets. // Send in a key frame. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_specific.generic_frame_info = GenericFrameInfo::Builder().T(0).Dtis("S").Build(); codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; @@ -1306,7 +1307,7 @@ TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) { sent_packets.back().HasExtension()); // Send in a new key frame without the support for the dependency descriptor. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); codec_specific.template_structure = std::nullopt; EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, EncodedImageCallback::Result::OK); @@ -1381,7 +1382,7 @@ TEST(RtpVideoSenderTest, ClearsPendingPacketsOnInactivation) { std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -1398,7 +1399,7 @@ TEST(RtpVideoSenderTest, ClearsPendingPacketsOnInactivation) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData( EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); EXPECT_EQ(test.router() @@ -1459,7 +1460,7 @@ TEST(RtpVideoSenderTest, std::vector sent_packets; ON_CALL(test.transport(), SendRtp) .WillByDefault( - [&](ArrayView packet, const PacketOptions& options) { + [&](std::span packet, const PacketOptions& options) { sent_packets.emplace_back(&extensions); EXPECT_TRUE(sent_packets.back().Parse(packet)); return true; @@ -1476,7 +1477,7 @@ TEST(RtpVideoSenderTest, encoded_image.SetSimulcastIndex(0); encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData( EncodedImageBuffer::Create(kImage, std::size(kImage))); EXPECT_EQ(test.router() @@ -1541,7 +1542,7 @@ TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(1); encoded_image.capture_time_ms_ = 2; - encoded_image._frameType = VideoFrameType::kVideoFrameKey; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); // Send two tiny images, mapping to two RTP packets. Capture sequence numbers. @@ -1552,7 +1553,7 @@ TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) { .Times(2) .WillRepeatedly( [&rtp_sequence_numbers, &transport_sequence_numbers]( - ArrayView packet, const PacketOptions& options) { + std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); rtp_sequence_numbers.push_back(rtp_packet.SequenceNumber()); @@ -1567,7 +1568,7 @@ TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) { test.router()->OnEncodedImage(encoded_image, &key_codec_info).error); encoded_image.SetRtpTimestamp(2); encoded_image.capture_time_ms_ = 3; - encoded_image._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(VideoFrameType::kVideoFrameDelta); CodecSpecificInfo delta_codec_info; delta_codec_info.codecType = kVideoCodecVP8; delta_codec_info.codecSpecific.VP8.temporalIdx = 1; @@ -1587,13 +1588,13 @@ TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) { EXPECT_CALL(test.transport(), SendRtp) .Times(1) .WillRepeatedly([&retransmitted_rtp_sequence_numbers]( - ArrayView packet, + std::span packet, const PacketOptions& options) { RtpPacket rtp_packet; EXPECT_TRUE(rtp_packet.Parse(packet)); EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); // Capture the retransmitted sequence number from the RTX header. - ArrayView payload = rtp_packet.payload(); + std::span payload = rtp_packet.payload(); retransmitted_rtp_sequence_numbers.push_back( ByteReader::ReadBigEndian(payload.data())); return true; @@ -1607,4 +1608,73 @@ TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) { EXPECT_EQ(retransmitted_rtp_sequence_numbers, base_rtp_sequence_numbers); } +TEST(RtpVideoSenderTest, PostTaskRaceDoesNotLeadToDanglingPointer) { + NiceMock transport; + NiceMock encoder_feedback; + + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1000000)); + Environment env = CreateEnvironment(time_controller.GetClock(), + time_controller.CreateTaskQueueFactory()); + + VideoSendStream::Config config(&transport); + config.rtp.ssrcs = {kSsrc1}; + + SendStatisticsProxy stats_proxy( + time_controller.GetClock(), config, + VideoEncoderConfig::ContentType::kRealtimeVideo, env.field_trials()); + + BitrateConstraints bitrate_config = GetBitrateConfig(); + RtpTransportConfig transport_config{.env = env, + .bitrate_config = bitrate_config}; + RtpTransportControllerSend transport_controller(transport_config); + transport_controller.EnsureStarted(); + + RateLimiter retransmission_rate_limiter(time_controller.GetClock(), + kRetransmitWindowSizeMs); + + std::map suspended_ssrcs; + std::map suspended_payload_states; + + auto router = std::make_unique( + env, time_controller.GetMainThread(), suspended_ssrcs, + suspended_payload_states, config.rtp, config.rtcp_report_interval_ms, + &transport, + CreateObservers(&encoder_feedback, &stats_proxy, &stats_proxy, + &stats_proxy, nullptr, &stats_proxy), + &transport_controller, &retransmission_rate_limiter, + std::make_unique(env), nullptr, CryptoOptions{}, + nullptr); + + router->SetSending(true); + // Verify it's registered + EXPECT_TRUE( + transport_controller.packet_router()->SsrcOfFirstSender().has_value()); + + // Trigger race: + // 1. OnVideoLayersAllocationUpdated posts a task to re-register modules + // because it sees active_ is true. + VideoLayersAllocation allocation; + allocation.active_spatial_layers.push_back({.rtp_stream_index = 0}); + router->OnVideoLayersAllocationUpdated(allocation); + + // 2. Immediately set active_ to false. This happens BEFORE the posted task + // runs. + router->SetSending(false); + + // 3. Let the posted task run. With the fix, it should return early and not + // re-register the module because active_ is now false. + time_controller.AdvanceTime(TimeDelta::Zero()); + + // 4. Verify that PacketRouter does NOT have a sender. + EXPECT_FALSE( + transport_controller.packet_router()->SsrcOfFirstSender().has_value()); + + // 5. Destroy the router. + router.reset(); + + // 6. Verify that PacketRouter still does not have a sender. + EXPECT_FALSE( + transport_controller.packet_router()->SsrcOfFirstSender().has_value()); +} + } // namespace webrtc diff --git a/call/rtx_receive_stream.cc b/call/rtx_receive_stream.cc index eae0e7b0645..2f773802de0 100644 --- a/call/rtx_receive_stream.cc +++ b/call/rtx_receive_stream.cc @@ -10,27 +10,51 @@ #include "call/rtx_receive_stream.h" - #include #include +#include +#include +#include #include -#include "api/array_view.h" +#include "api/environment/environment.h" #include "api/sequence_checker.h" #include "call/rtp_packet_sink_interface.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" #include "modules/rtp_rtcp/include/receive_statistics.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "rtc_base/checks.h" #include "rtc_base/logging.h" namespace webrtc { +// Deprecated, don't use. +RtxReceiveStream::RtxReceiveStream( + RtpPacketSinkInterface* media_sink, + std::map associated_payload_types, + uint32_t media_ssrc, + ReceiveStatistics* rtp_receive_statistics /* = nullptr */) + : env_(std::nullopt), + media_sink_(media_sink), + associated_payload_types_(std::move(associated_payload_types)), + media_ssrc_(media_ssrc), + rtp_receive_statistics_(rtp_receive_statistics) { + packet_checker_.Detach(); + if (associated_payload_types_.empty()) { + RTC_LOG(LS_WARNING) + << "RtxReceiveStream created with empty payload type mapping."; + } +} + RtxReceiveStream::RtxReceiveStream( + const Environment& env, RtpPacketSinkInterface* media_sink, std::map associated_payload_types, uint32_t media_ssrc, ReceiveStatistics* rtp_receive_statistics /* = nullptr */) - : media_sink_(media_sink), + : env_(env), + media_sink_(media_sink), associated_payload_types_(std::move(associated_payload_types)), media_ssrc_(media_ssrc), rtp_receive_statistics_(rtp_receive_statistics) { @@ -54,9 +78,10 @@ void RtxReceiveStream::OnRtpPacket(const RtpPacketReceived& rtx_packet) { if (rtp_receive_statistics_) { rtp_receive_statistics_->OnRtpPacket(rtx_packet); } - ArrayView payload = rtx_packet.payload(); + std::span payload = rtx_packet.payload(); if (payload.size() < kRtxHeaderSize) { + LogRtpPacketToEventLog(rtx_packet, /*osn=*/std::nullopt); return; } @@ -65,21 +90,39 @@ void RtxReceiveStream::OnRtpPacket(const RtpPacketReceived& rtx_packet) { RTC_DLOG(LS_VERBOSE) << "Unknown payload type " << static_cast(rtx_packet.PayloadType()) << " on rtx ssrc " << rtx_packet.Ssrc(); + LogRtpPacketToEventLog(rtx_packet, /*osn=*/std::nullopt); return; } + + RTC_DCHECK_GE(payload.size(), 2); + // The RTX payload header is two bytes that represents the original sequence + // number ('osn') of the associated original packet. + // https://datatracker.ietf.org/doc/html/rfc4588#section-4 + uint16_t osn = (payload[0] << 8) + payload[1]; + LogRtpPacketToEventLog(rtx_packet, osn); + RtpPacketReceived media_packet; media_packet.CopyHeaderFrom(rtx_packet); media_packet.SetSsrc(media_ssrc_); - media_packet.SetSequenceNumber((payload[0] << 8) + payload[1]); + media_packet.SetSequenceNumber(osn); media_packet.SetPayloadType(it->second); media_packet.set_recovered(true); media_packet.set_arrival_time(rtx_packet.arrival_time()); - // Skip the RTX header. - media_packet.SetPayload(payload.subview(kRtxHeaderSize)); + // Skip the RTX payload header. + media_packet.SetPayload(payload.subspan(kRtxHeaderSize)); media_sink_->OnRtpPacket(media_packet); } +void RtxReceiveStream::LogRtpPacketToEventLog(const RtpPacketReceived& packet, + std::optional osn) { + if (!env_) { + return; + } + env_->event_log().Log( + std::make_unique(packet, osn)); +} + } // namespace webrtc diff --git a/call/rtx_receive_stream.h b/call/rtx_receive_stream.h index e98055ae3bc..357ed3e36fa 100644 --- a/call/rtx_receive_stream.h +++ b/call/rtx_receive_stream.h @@ -13,7 +13,9 @@ #include #include +#include +#include "api/environment/environment.h" #include "api/sequence_checker.h" #include "call/rtp_packet_sink_interface.h" #include "rtc_base/system/no_unique_address.h" @@ -27,7 +29,14 @@ class ReceiveStatistics; // are passed on to a sink representing the associated media stream. class RtxReceiveStream : public RtpPacketSinkInterface { public: - RtxReceiveStream(RtpPacketSinkInterface* media_sink, + // TODO: b/305890678 - Remove when downstream users have been updated. + [[deprecated("Use other ctor")]] RtxReceiveStream( + RtpPacketSinkInterface* media_sink, + std::map associated_payload_types, + uint32_t media_ssrc, + ReceiveStatistics* rtp_receive_statistics = nullptr); + RtxReceiveStream(const Environment& env, + RtpPacketSinkInterface* media_sink, std::map associated_payload_types, uint32_t media_ssrc, // TODO(nisse): Delete this argument, and @@ -45,6 +54,10 @@ class RtxReceiveStream : public RtpPacketSinkInterface { void OnRtpPacket(const RtpPacketReceived& packet) override; private: + void LogRtpPacketToEventLog(const RtpPacketReceived& packet, + std::optional osn); + + const std::optional env_; RTC_NO_UNIQUE_ADDRESS SequenceChecker packet_checker_; RtpPacketSinkInterface* const media_sink_; // Map from rtx payload type -> media payload type. diff --git a/call/rtx_receive_stream_unittest.cc b/call/rtx_receive_stream_unittest.cc index 342e80f1625..9a30cc437e2 100644 --- a/call/rtx_receive_stream_unittest.cc +++ b/call/rtx_receive_stream_unittest.cc @@ -14,15 +14,20 @@ #include #include #include +#include -#include "api/array_view.h" +#include "api/environment/environment.h" +#include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "api/video/video_rotation.h" #include "call/test/mock_rtp_packet_sink_interface.h" +#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h" +#include "logging/rtc_event_log/mock/mock_rtc_event_log.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" @@ -30,12 +35,17 @@ namespace webrtc { namespace { +using ::testing::_; +using ::testing::Eq; +using ::testing::NotNull; using ::testing::Property; using ::testing::StrictMock; +using ::testing::Truly; constexpr int kMediaPayloadType = 100; constexpr int kRtxPayloadType = 98; constexpr int kUnknownPayloadType = 90; +constexpr uint32_t kRtxSSRC = 0x22222222; constexpr uint32_t kMediaSSRC = 0x3333333; constexpr uint16_t kMediaSeqno = 0x5657; @@ -113,17 +123,18 @@ std::map PayloadTypeMapping() { } template -ArrayView Truncate(ArrayView a, size_t drop) { - return a.subview(0, a.size() - drop); +std::span Truncate(std::span a, size_t drop) { + return a.subspan(0, a.size() - drop); } } // namespace TEST(RtxReceiveStreamTest, RestoresPacketPayload) { StrictMock media_sink; - RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + Environment env = CreateTestEnvironment(); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); RtpPacketReceived rtx_packet; - EXPECT_TRUE(rtx_packet.Parse(ArrayView(kRtxPacket))); + EXPECT_TRUE(rtx_packet.Parse(std::span(kRtxPacket))); EXPECT_CALL(media_sink, OnRtpPacket) .WillOnce([](const RtpPacketReceived& packet) { @@ -138,9 +149,10 @@ TEST(RtxReceiveStreamTest, RestoresPacketPayload) { TEST(RtxReceiveStreamTest, SetsRecoveredFlag) { StrictMock media_sink; - RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + Environment env = CreateTestEnvironment(); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); RtpPacketReceived rtx_packet; - EXPECT_TRUE(rtx_packet.Parse(ArrayView(kRtxPacket))); + EXPECT_TRUE(rtx_packet.Parse(std::span(kRtxPacket))); EXPECT_FALSE(rtx_packet.recovered()); EXPECT_CALL(media_sink, OnRtpPacket) .WillOnce([](const RtpPacketReceived& packet) { @@ -155,28 +167,35 @@ TEST(RtxReceiveStreamTest, IgnoresUnknownPayloadType) { const std::map payload_type_mapping = { {kUnknownPayloadType, kMediaPayloadType}}; - RtxReceiveStream rtx_sink(&media_sink, payload_type_mapping, kMediaSSRC); + MockRtcEventLog log; + Environment env = CreateTestEnvironment({.event_log = &log}); + RtxReceiveStream rtx_sink(env, &media_sink, payload_type_mapping, kMediaSSRC); RtpPacketReceived rtx_packet; - EXPECT_TRUE(rtx_packet.Parse(ArrayView(kRtxPacket))); + EXPECT_TRUE(rtx_packet.Parse(std::span(kRtxPacket))); + EXPECT_CALL(log, LogProxy(_)); rtx_sink.OnRtpPacket(rtx_packet); } TEST(RtxReceiveStreamTest, IgnoresTruncatedPacket) { StrictMock media_sink; - RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + MockRtcEventLog log; + Environment env = CreateTestEnvironment({.event_log = &log}); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); RtpPacketReceived rtx_packet; EXPECT_TRUE( - rtx_packet.Parse(Truncate(ArrayView(kRtxPacket), 2))); + rtx_packet.Parse(Truncate(std::span(kRtxPacket), 2))); + EXPECT_CALL(log, LogProxy(_)); rtx_sink.OnRtpPacket(rtx_packet); } TEST(RtxReceiveStreamTest, CopiesRtpHeaderExtensions) { StrictMock media_sink; - RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + Environment env = CreateTestEnvironment(); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); RtpHeaderExtensionMap extension_map; extension_map.RegisterByType(3, kRtpExtensionVideoRotation); RtpPacketReceived rtx_packet(&extension_map); - EXPECT_TRUE(rtx_packet.Parse(ArrayView(kRtxPacketWithCVO))); + EXPECT_TRUE(rtx_packet.Parse(std::span(kRtxPacketWithCVO))); VideoRotation rotation = kVideoRotation_0; EXPECT_TRUE(rtx_packet.GetExtension(&rotation)); @@ -198,9 +217,10 @@ TEST(RtxReceiveStreamTest, CopiesRtpHeaderExtensions) { TEST(RtxReceiveStreamTest, PropagatesArrivalTime) { StrictMock media_sink; - RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + Environment env = CreateTestEnvironment(); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); RtpPacketReceived rtx_packet(nullptr); - EXPECT_TRUE(rtx_packet.Parse(ArrayView(kRtxPacket))); + EXPECT_TRUE(rtx_packet.Parse(std::span(kRtxPacket))); rtx_packet.set_arrival_time(Timestamp::Millis(123)); EXPECT_CALL(media_sink, OnRtpPacket(Property(&RtpPacketReceived::arrival_time, Timestamp::Millis(123)))); @@ -209,20 +229,21 @@ TEST(RtxReceiveStreamTest, PropagatesArrivalTime) { TEST(RtxReceiveStreamTest, SupportsLargePacket) { StrictMock media_sink; - RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + Environment env = CreateTestEnvironment(); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); RtpPacketReceived rtx_packet; constexpr int kRtxPacketSize = 2000; constexpr int kRtxPayloadOffset = 14; uint8_t large_rtx_packet[kRtxPacketSize]; memcpy(large_rtx_packet, kRtxPacket, sizeof(kRtxPacket)); - ArrayView payload(large_rtx_packet + kRtxPayloadOffset, + std::span payload(large_rtx_packet + kRtxPayloadOffset, kRtxPacketSize - kRtxPayloadOffset); // Fill payload. for (size_t i = 0; i < payload.size(); i++) { payload[i] = i; } - EXPECT_TRUE(rtx_packet.Parse(ArrayView(large_rtx_packet))); + EXPECT_TRUE(rtx_packet.Parse(std::span(large_rtx_packet))); EXPECT_CALL(media_sink, OnRtpPacket) .WillOnce([&](const RtpPacketReceived& packet) { @@ -237,7 +258,8 @@ TEST(RtxReceiveStreamTest, SupportsLargePacket) { TEST(RtxReceiveStreamTest, SupportsLargePacketWithPadding) { StrictMock media_sink; - RtxReceiveStream rtx_sink(&media_sink, PayloadTypeMapping(), kMediaSSRC); + Environment env = CreateTestEnvironment(); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); RtpPacketReceived rtx_packet; constexpr int kRtxPacketSize = 2000; constexpr int kRtxPayloadOffset = 14; @@ -245,10 +267,10 @@ TEST(RtxReceiveStreamTest, SupportsLargePacketWithPadding) { uint8_t large_rtx_packet[kRtxPacketSize]; memcpy(large_rtx_packet, kRtxPacketWithPadding, sizeof(kRtxPacketWithPadding)); - ArrayView payload( + std::span payload( large_rtx_packet + kRtxPayloadOffset, kRtxPacketSize - kRtxPayloadOffset - kRtxPaddingSize); - ArrayView padding( + std::span padding( large_rtx_packet + kRtxPacketSize - kRtxPaddingSize, kRtxPaddingSize); // Fill payload. @@ -260,7 +282,7 @@ TEST(RtxReceiveStreamTest, SupportsLargePacketWithPadding) { padding[i] = kRtxPaddingSize; } - EXPECT_TRUE(rtx_packet.Parse(ArrayView(large_rtx_packet))); + EXPECT_TRUE(rtx_packet.Parse(std::span(large_rtx_packet))); EXPECT_CALL(media_sink, OnRtpPacket) .WillOnce([&](const RtpPacketReceived& packet) { @@ -273,4 +295,30 @@ TEST(RtxReceiveStreamTest, SupportsLargePacketWithPadding) { rtx_sink.OnRtpPacket(rtx_packet); } +MATCHER_P2(IsRtcEventRtpPacketIncomingPtrWithSsrcAndOsn, ssrc, osn, "") { + if (!arg) { + return false; + } + if (arg->GetType() != RtcEvent::Type::RtpPacketIncoming) { + return false; + } + RtcEventRtpPacketIncoming* event = + static_cast(arg); + + return event->Ssrc() == ssrc && event->rtx_original_sequence_number() == osn; +} + +TEST(RtxReceiveStreamTest, LogsRtpPacketIncoming) { + MockRtpPacketSink media_sink; + MockRtcEventLog log; + Environment env = CreateTestEnvironment({.event_log = &log}); + RtxReceiveStream rtx_sink(env, &media_sink, PayloadTypeMapping(), kMediaSSRC); + RtpPacketReceived rtx_packet; + EXPECT_TRUE(rtx_packet.Parse(std::span(kRtxPacket))); + + EXPECT_CALL(log, LogProxy(IsRtcEventRtpPacketIncomingPtrWithSsrcAndOsn( + kRtxSSRC, kMediaSeqno))); + rtx_sink.OnRtpPacket(rtx_packet); +} + } // namespace webrtc diff --git a/call/version.cc b/call/version.cc index de24512d3d1..1d4ee7360fb 100644 --- a/call/version.cc +++ b/call/version.cc @@ -13,7 +13,7 @@ namespace webrtc { // The timestamp is always in UTC. -const char* const kSourceTimestamp = "WebRTC source stamp 2026-01-11T04:04:53"; +const char* const kSourceTimestamp = "WebRTC source stamp 2026-04-02T04:08:10"; void LoadWebRTCVersionInRegister() { // Using volatile to instruct the compiler to not optimize `p` away even diff --git a/call/video_receive_stream.cc b/call/video_receive_stream.cc index c16cd59b4dd..5a1c168f29e 100644 --- a/call/video_receive_stream.cc +++ b/call/video_receive_stream.cc @@ -142,7 +142,6 @@ VideoReceiveStreamInterface::Config::Rtp::~Rtp() = default; std::string VideoReceiveStreamInterface::Config::Rtp::ToString() const { StringBuilder ss; ss << "{remote_ssrc: " << remote_ssrc; - ss << ", local_ssrc: " << local_ssrc; ss << ", rtcp_mode: " << (rtcp_mode == RtcpMode::kCompound ? "RtcpMode::kCompound" : "RtcpMode::kReducedSize"); diff --git a/call/video_send_stream.h b/call/video_send_stream.h index c5a24d2c489..683be5f4697 100644 --- a/call/video_send_stream.h +++ b/call/video_send_stream.h @@ -15,11 +15,11 @@ #include #include +#include #include #include #include "api/adaptation/resource.h" -#include "api/array_view.h" #include "api/call/transport.h" #include "api/crypto/crypto_options.h" #include "api/frame_transformer_interface.h" @@ -264,7 +264,7 @@ class VideoSendStream { virtual void SetStats(const Stats& stats) { RTC_CHECK_NOTREACHED(); } // Sets the list of CSRCs to be included in every packet. - virtual void SetCsrcs(ArrayView csrcs) = 0; + virtual void SetCsrcs(std::span csrcs) = 0; virtual void GenerateKeyFrame(const std::vector& rids) = 0; diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn index 83e9ef98a0d..c54c797a84f 100644 --- a/common_audio/BUILD.gn +++ b/common_audio/BUILD.gn @@ -45,7 +45,6 @@ rtc_library("common_audio") { deps = [ ":common_audio_c", ":sinc_resampler", - "../api:array_view", "../api/audio:audio_frame_api", "../api/units:timestamp", "../rtc_base:checks", diff --git a/common_audio/channel_buffer.h b/common_audio/channel_buffer.h index 7d040e5d5f1..5a79f004503 100644 --- a/common_audio/channel_buffer.h +++ b/common_audio/channel_buffer.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "rtc_base/checks.h" @@ -89,19 +89,19 @@ class ChannelBuffer { num_channels_(num_channels), num_bands_(num_bands), bands_view_(num_allocated_channels_, - std::vector>(num_bands_)), + std::vector>(num_bands_)), channels_view_(num_bands_, - std::vector>(num_allocated_channels_)) { + std::vector>(num_allocated_channels_)) { // Temporarily cast away const_ness to allow populating the array views. auto* bands_view = - const_cast>>*>(&bands_view_); + const_cast>>*>(&bands_view_); auto* channels_view = - const_cast>>*>(&channels_view_); + const_cast>>*>(&channels_view_); for (size_t ch = 0; ch < num_allocated_channels_; ++ch) { for (size_t band = 0; band < num_bands_; ++band) { (*channels_view)[band][ch] = - ArrayView(&data_[ch * num_frames_ + band * num_frames_per_band_], + std::span(&data_[ch * num_frames_ + band * num_frames_per_band_], num_frames_per_band_); (*bands_view)[ch][band] = channels_view_[band][ch]; channels_[band * num_allocated_channels_ + ch] = @@ -133,10 +133,10 @@ class ChannelBuffer { const ChannelBuffer* t = this; return const_cast(t->channels(band)); } - ArrayView> channels_view(size_t band = 0) { + std::span> channels_view(size_t band = 0) { return channels_view_[band]; } - ArrayView> channels_view(size_t band = 0) const { + std::span> channels_view(size_t band = 0) const { return channels_view_[band]; } @@ -157,10 +157,10 @@ class ChannelBuffer { return const_cast(t->bands(channel)); } - ArrayView> bands_view(size_t channel) { + std::span> bands_view(size_t channel) { return bands_view_[channel]; } - ArrayView> bands_view(size_t channel) const { + std::span> bands_view(size_t channel) const { return bands_view_[channel]; } @@ -204,8 +204,8 @@ class ChannelBuffer { // Number of channels the user sees. size_t num_channels_; const size_t num_bands_; - const std::vector>> bands_view_; - const std::vector>> channels_view_; + const std::vector>> bands_view_; + const std::vector>> channels_view_; }; // One int16_t and one float ChannelBuffer that are kept in sync. The sync is diff --git a/common_audio/include/audio_util.h b/common_audio/include/audio_util.h index 672ebd1aa00..0b04f812bb8 100644 --- a/common_audio/include/audio_util.h +++ b/common_audio/include/audio_util.h @@ -106,7 +106,7 @@ inline float FloatS16ToDbfs(float v) { // Copy audio from `src` channels to `dest` channels unless `src` and `dest` // point to the same address. `src` and `dest` must have the same number of // channels, and there must be sufficient space allocated in `dest`. -// TODO: b/335805780 - Accept ArrayView. +// TODO: b/335805780 - Accept std::span. template void CopyAudioIfNeeded(const T* const* src, int num_frames, diff --git a/common_audio/ring_buffer.c b/common_audio/ring_buffer.c index a3fabd058f7..c7ff7825072 100644 --- a/common_audio/ring_buffer.c +++ b/common_audio/ring_buffer.c @@ -14,6 +14,7 @@ #include "common_audio/ring_buffer.h" #include // size_t +#include #include #include @@ -56,6 +57,10 @@ RingBuffer* WebRtc_CreateBuffer(size_t element_count, size_t element_size) { return NULL; } + if (element_count > SIZE_MAX / element_size) { + return NULL; + } + self = malloc(sizeof(RingBuffer)); if (!self) { return NULL; diff --git a/common_audio/ring_buffer_unittest.cc b/common_audio/ring_buffer_unittest.cc index f93f042d9cd..a87de9d0d0f 100644 --- a/common_audio/ring_buffer_unittest.cc +++ b/common_audio/ring_buffer_unittest.cc @@ -11,6 +11,7 @@ #include "common_audio/ring_buffer.h" #include +#include #include #include #include @@ -142,6 +143,8 @@ TEST(RingBufferTest, PassingNulltoReadBufferForcesMemcpy) { TEST(RingBufferTest, CreateHandlesErrors) { EXPECT_TRUE(WebRtc_CreateBuffer(0, 1) == nullptr); EXPECT_TRUE(WebRtc_CreateBuffer(1, 0) == nullptr); + EXPECT_TRUE(WebRtc_CreateBuffer(SIZE_MAX, 2) == nullptr); + EXPECT_TRUE(WebRtc_CreateBuffer(2, SIZE_MAX) == nullptr); RingBuffer* buffer = WebRtc_CreateBuffer(1, 1); EXPECT_TRUE(buffer != nullptr); WebRtc_FreeBuffer(buffer); diff --git a/common_audio/vad/vad_filterbank.c b/common_audio/vad/vad_filterbank.c index 32830fa06f6..04cfba908bb 100644 --- a/common_audio/vad/vad_filterbank.c +++ b/common_audio/vad/vad_filterbank.c @@ -12,6 +12,7 @@ #include "common_audio/signal_processing/include/signal_processing_library.h" #include "rtc_base/checks.h" +#include "rtc_base/sanitizer.h" // Constants used in LogOfEnergy(). static const int16_t kLogConst = 24660; // 160*log10(2) in Q9. @@ -79,7 +80,8 @@ static void HighPassFilter(const int16_t* data_in, // - filter_coefficient [i] : Given in Q15. // - filter_state [i/o] : State of the filter given in Q(-1). // - data_out [o] : Output audio signal given in Q(-1). -static void AllPassFilter(const int16_t* data_in, +static void RTC_NO_SANITIZE("signed-integer-overflow") // https://bugs.webrtc.org/490331112 + AllPassFilter(const int16_t* data_in, size_t data_length, int16_t filter_coefficient, int16_t* filter_state, diff --git a/common_audio/window_generator.cc b/common_audio/window_generator.cc index 5a6de1aec1b..30465879c13 100644 --- a/common_audio/window_generator.cc +++ b/common_audio/window_generator.cc @@ -17,13 +17,13 @@ #include "rtc_base/checks.h" -using std::complex; +namespace webrtc { namespace { // Modified Bessel function of order 0 for complex inputs. -complex I0(complex x) { - complex y = x / 3.75f; +std::complex I0(std::complex x) { + std::complex y = x / 3.75f; y *= y; return 1.0f + y * (3.5156229f + y * (3.0899424f + @@ -34,8 +34,6 @@ complex I0(complex x) { } // namespace -namespace webrtc { - void WindowGenerator::Hanning(int length, float* window) { RTC_CHECK_GT(length, 1); RTC_CHECK(window != nullptr); @@ -55,7 +53,7 @@ void WindowGenerator::KaiserBesselDerived(float alpha, float sum = 0.0f; for (size_t i = 0; i <= half; ++i) { - complex r = (4.0f * i) / length - 1.0f; + std::complex r = (4.0f * i) / length - 1.0f; sum += I0(std::numbers::pi_v * alpha * sqrt(1.0f - r * r)).real(); window[i] = sum; } diff --git a/common_video/BUILD.gn b/common_video/BUILD.gn index e00ee943b78..52753212af6 100644 --- a/common_video/BUILD.gn +++ b/common_video/BUILD.gn @@ -64,36 +64,30 @@ rtc_library("common_video") { } deps = [ - "../api:array_view", "../api:make_ref_counted", "../api:scoped_refptr", - "../api:sequence_checker", - "../api/task_queue", "../api/units:time_delta", "../api/units:timestamp", - "../api/video:encoded_image", - "../api/video:video_bitrate_allocation", - "../api/video:video_bitrate_allocator", "../api/video:video_frame", "../api/video:video_frame_i010", "../api/video:video_rtp_headers", "../api/video_codecs:bitstream_parser_api", - "../api/video_codecs:video_codecs_api", "../rtc_base:bit_buffer", "../rtc_base:bitstream_reader", "../rtc_base:buffer", "../rtc_base:checks", - "../rtc_base:event_tracer", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:race_checker", "../rtc_base:rate_statistics", "../rtc_base:refcount", - "../rtc_base:safe_minmax", "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/system:rtc_export", + "../system_wrappers", "../system_wrappers:metrics", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/numeric:bits", "//third_party/libyuv", ] @@ -149,19 +143,16 @@ if (rtc_include_tests && !build_with_chromium) { deps = [ ":common_video", - "../api:array_view", "../api:scoped_refptr", "../api/units:time_delta", "../api/units:timestamp", "../api/video:video_frame", "../api/video:video_frame_i010", "../api/video:video_rtp_headers", - "../api/video_codecs:video_codecs_api", "../rtc_base:bit_buffer", "../rtc_base:buffer", "../rtc_base:checks", "../rtc_base:logging", - "../rtc_base:macromagic", "../rtc_base:rtc_base_tests_utils", "../rtc_base:timeutils", "../system_wrappers", diff --git a/common_video/OWNERS b/common_video/OWNERS index 1c080874f09..4b06c4e32bf 100644 --- a/common_video/OWNERS +++ b/common_video/OWNERS @@ -1,5 +1,2 @@ -magjed@webrtc.org -marpan@webrtc.org sprang@webrtc.org ssilkin@webrtc.org -stefan@webrtc.org diff --git a/common_video/bitrate_adjuster.cc b/common_video/bitrate_adjuster.cc index 66f31d9e0b5..db96484bde2 100644 --- a/common_video/bitrate_adjuster.cc +++ b/common_video/bitrate_adjuster.cc @@ -16,9 +16,10 @@ #include #include +#include "absl/base/nullability.h" #include "rtc_base/logging.h" #include "rtc_base/synchronization/mutex.h" -#include "rtc_base/time_utils.h" +#include "system_wrappers/include/clock.h" namespace webrtc { @@ -33,9 +34,11 @@ const float BitrateAdjuster::kBitrateTolerancePct = .1f; const float BitrateAdjuster::kBytesPerMsToBitsPerSecond = 8 * 1000; -BitrateAdjuster::BitrateAdjuster(float min_adjusted_bitrate_pct, +BitrateAdjuster::BitrateAdjuster(Clock* absl_nonnull clock, + float min_adjusted_bitrate_pct, float max_adjusted_bitrate_pct) - : min_adjusted_bitrate_pct_(min_adjusted_bitrate_pct), + : clock_(*clock), + min_adjusted_bitrate_pct_(min_adjusted_bitrate_pct), max_adjusted_bitrate_pct_(max_adjusted_bitrate_pct), bitrate_tracker_(1.5 * kBitrateUpdateIntervalMs, kBytesPerMsToBitsPerSecond) { @@ -73,12 +76,12 @@ uint32_t BitrateAdjuster::GetAdjustedBitrateBps() const { std::optional BitrateAdjuster::GetEstimatedBitrateBps() { MutexLock lock(&mutex_); - return bitrate_tracker_.Rate(TimeMillis()); + return bitrate_tracker_.Rate(clock_.TimeInMilliseconds()); } void BitrateAdjuster::Update(size_t frame_size) { MutexLock lock(&mutex_); - uint32_t current_time_ms = TimeMillis(); + uint32_t current_time_ms = clock_.TimeInMilliseconds(); bitrate_tracker_.Update(frame_size, current_time_ms); UpdateBitrate(current_time_ms); } diff --git a/common_video/bitrate_adjuster_unittest.cc b/common_video/bitrate_adjuster_unittest.cc index 088e71fd89a..da3f162a286 100644 --- a/common_video/bitrate_adjuster_unittest.cc +++ b/common_video/bitrate_adjuster_unittest.cc @@ -14,7 +14,8 @@ #include #include "api/units/time_delta.h" -#include "rtc_base/fake_clock.h" +#include "api/units/timestamp.h" +#include "system_wrappers/include/clock.h" #include "test/gtest.h" namespace webrtc { @@ -22,7 +23,8 @@ namespace webrtc { class BitrateAdjusterTest : public ::testing::Test { public: BitrateAdjusterTest() - : adjuster_(kMinAdjustedBitratePct, kMaxAdjustedBitratePct) {} + : clock_(Timestamp::Seconds(123)), + adjuster_(&clock_, kMinAdjustedBitratePct, kMaxAdjustedBitratePct) {} // Simulate an output bitrate for one update cycle of BitrateAdjuster. void SimulateBitrateBps(uint32_t bitrate_bps) { @@ -67,7 +69,7 @@ class BitrateAdjusterTest : public ::testing::Test { protected: static const float kMinAdjustedBitratePct; static const float kMaxAdjustedBitratePct; - ScopedFakeClock clock_; + SimulatedClock clock_; BitrateAdjuster adjuster_; }; diff --git a/common_video/framerate_controller_unittest.cc b/common_video/framerate_controller_unittest.cc index 4b0abdef4e0..d9b11cbe623 100644 --- a/common_video/framerate_controller_unittest.cc +++ b/common_video/framerate_controller_unittest.cc @@ -29,7 +29,7 @@ class FramerateControllerTest : public ::testing::Test { return next_timestamp_us_ * kNumNanosecsPerMicrosec; } - int64_t next_timestamp_us_ = TimeMicros(); + int64_t next_timestamp_us_ = 6543210; FramerateController controller_; }; diff --git a/common_video/generic_frame_descriptor/BUILD.gn b/common_video/generic_frame_descriptor/BUILD.gn index 79314f8ef97..ebf4c4a341b 100644 --- a/common_video/generic_frame_descriptor/BUILD.gn +++ b/common_video/generic_frame_descriptor/BUILD.gn @@ -15,10 +15,8 @@ rtc_library("generic_frame_descriptor") { ] deps = [ - "../../api:array_view", "../../api/transport/rtp:dependency_descriptor", "../../api/video:video_codec_constants", - "../../rtc_base:checks", "../../rtc_base/system:rtc_export", "//third_party/abseil-cpp/absl/container:inlined_vector", "//third_party/abseil-cpp/absl/strings:string_view", diff --git a/common_video/h264/h264_bitstream_parser.cc b/common_video/h264/h264_bitstream_parser.cc index 7bc6ab40bf2..145e4d2d8de 100644 --- a/common_video/h264/h264_bitstream_parser.cc +++ b/common_video/h264/h264_bitstream_parser.cc @@ -12,9 +12,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "common_video/h264/pps_parser.h" #include "common_video/h264/sps_parser.h" @@ -34,7 +34,7 @@ H264BitstreamParser::H264BitstreamParser() = default; H264BitstreamParser::~H264BitstreamParser() = default; H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu( - ArrayView source, + std::span source, uint8_t nalu_type) { if (!sps_ || !pps_) return kInvalidStream; @@ -311,20 +311,20 @@ H264BitstreamParser::Result H264BitstreamParser::ParseNonParameterSetNalu( return kOk; } -void H264BitstreamParser::ParseSlice(ArrayView slice) { +void H264BitstreamParser::ParseSlice(std::span slice) { if (slice.empty()) { return; } H264::NaluType nalu_type = H264::ParseNaluType(slice[0]); switch (nalu_type) { case H264::NaluType::kSps: { - sps_ = SpsParser::ParseSps(slice.subview(H264::kNaluTypeSize)); + sps_ = SpsParser::ParseSps(slice.subspan(H264::kNaluTypeSize)); if (!sps_) RTC_DLOG(LS_WARNING) << "Unable to parse SPS from H264 bitstream."; break; } case H264::NaluType::kPps: { - pps_ = PpsParser::ParsePps(slice.subview(H264::kNaluTypeSize)); + pps_ = PpsParser::ParsePps(slice.subspan(H264::kNaluTypeSize)); if (!pps_) RTC_DLOG(LS_WARNING) << "Unable to parse PPS from H264 bitstream."; break; @@ -343,11 +343,11 @@ void H264BitstreamParser::ParseSlice(ArrayView slice) { } } -void H264BitstreamParser::ParseBitstream(ArrayView bitstream) { +void H264BitstreamParser::ParseBitstream(std::span bitstream) { std::vector nalu_indices = H264::FindNaluIndices(bitstream); for (const H264::NaluIndex& index : nalu_indices) ParseSlice( - bitstream.subview(index.payload_start_offset, index.payload_size)); + bitstream.subspan(index.payload_start_offset, index.payload_size)); } std::optional H264BitstreamParser::GetLastSliceQp() const { diff --git a/common_video/h264/h264_bitstream_parser.h b/common_video/h264/h264_bitstream_parser.h index 2dafa861850..810b91cbddf 100644 --- a/common_video/h264/h264_bitstream_parser.h +++ b/common_video/h264/h264_bitstream_parser.h @@ -14,8 +14,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/video_codecs/bitstream_parser.h" #include "common_video/h264/pps_parser.h" #include "common_video/h264/sps_parser.h" @@ -33,7 +33,7 @@ class H264BitstreamParser : public BitstreamParser { H264BitstreamParser(); ~H264BitstreamParser() override; - void ParseBitstream(ArrayView bitstream) override; + void ParseBitstream(std::span bitstream) override; std::optional GetLastSliceQp() const override; protected: @@ -42,8 +42,8 @@ class H264BitstreamParser : public BitstreamParser { kInvalidStream, kUnsupportedStream, }; - void ParseSlice(ArrayView slice); - Result ParseNonParameterSetNalu(ArrayView source, + void ParseSlice(std::span slice); + Result ParseNonParameterSetNalu(std::span source, uint8_t nalu_type); // SPS/PPS state, updated when parsing new SPS/PPS, used to parse slices. diff --git a/common_video/h264/h264_common.cc b/common_video/h264/h264_common.cc index 4ab704c4eb5..387d360d4a1 100644 --- a/common_video/h264/h264_common.cc +++ b/common_video/h264/h264_common.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/buffer.h" namespace webrtc { @@ -22,7 +22,7 @@ namespace H264 { const uint8_t kNaluTypeMask = 0x1F; -std::vector FindNaluIndices(ArrayView buffer) { +std::vector FindNaluIndices(std::span buffer) { // This is sorta like Boyer-Moore, but with only the first optimization step: // given a 3-byte sequence we're looking at, if the 3rd byte isn't 1 or 0, // skip ahead to the next 3-byte sequence. 0s and 1s are relatively rare, so @@ -72,7 +72,7 @@ NaluType ParseNaluType(uint8_t data) { return static_cast(data & kNaluTypeMask); } -std::vector ParseRbsp(ArrayView data) { +std::vector ParseRbsp(std::span data) { std::vector out; out.reserve(data.size()); @@ -95,7 +95,7 @@ std::vector ParseRbsp(ArrayView data) { return out; } -void WriteRbsp(ArrayView bytes, Buffer* destination) { +void WriteRbsp(std::span bytes, Buffer* destination) { static const uint8_t kZerosInStartSequence = 2; static const uint8_t kEmulationByte = 0x03u; size_t num_consecutive_zeros = 0; diff --git a/common_video/h264/h264_common.h b/common_video/h264/h264_common.h index 68c95041076..932fb546049 100644 --- a/common_video/h264/h264_common.h +++ b/common_video/h264/h264_common.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/system/rtc_export.h" @@ -65,7 +65,7 @@ struct NaluIndex { // Returns a vector of the NALU indices in the given buffer. RTC_EXPORT std::vector FindNaluIndices( - ArrayView buffer); + std::span buffer); // Get the NAL type from the header byte immediately following start sequence. RTC_EXPORT NaluType ParseNaluType(uint8_t data); @@ -84,23 +84,23 @@ RTC_EXPORT NaluType ParseNaluType(uint8_t data); // the 03 emulation byte. // Parse the given data and remove any emulation byte escaping. -std::vector ParseRbsp(ArrayView data); +std::vector ParseRbsp(std::span data); // TODO: bugs.webrtc.org/42225170 - Deprecate. inline std::vector ParseRbsp(const uint8_t* data, size_t length) { - return ParseRbsp(MakeArrayView(data, length)); + return ParseRbsp(std::span(data, length)); } // Write the given data to the destination buffer, inserting and emulation // bytes in order to escape any data the could be interpreted as a start // sequence. -void WriteRbsp(ArrayView bytes, Buffer* destination); +void WriteRbsp(std::span bytes, Buffer* destination); // TODO: bugs.webrtc.org/42225170 - Deprecate. inline void WriteRbsp(const uint8_t* bytes, size_t length, Buffer* destination) { - WriteRbsp(MakeArrayView(bytes, length), destination); + WriteRbsp(std::span(bytes, length), destination); } } // namespace H264 } // namespace webrtc diff --git a/common_video/h264/pps_parser.cc b/common_video/h264/pps_parser.cc index dc36ba1d104..2ecfaa55a1f 100644 --- a/common_video/h264/pps_parser.cc +++ b/common_video/h264/pps_parser.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include "absl/numeric/bits.h" -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/checks.h" @@ -32,14 +32,14 @@ constexpr int kMinPicInitQpDeltaValue = -26; // http://www.itu.int/rec/T-REC-H.264 std::optional PpsParser::ParsePps( - ArrayView data) { + std::span data) { // First, parse out rbsp, which is basically the source buffer minus emulation // bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in // section 7.3.1 of the H.264 standard. return ParseInternal(H264::ParseRbsp(data)); } -bool PpsParser::ParsePpsIds(ArrayView data, +bool PpsParser::ParsePpsIds(std::span data, uint32_t* pps_id, uint32_t* sps_id) { RTC_DCHECK(pps_id); @@ -55,7 +55,7 @@ bool PpsParser::ParsePpsIds(ArrayView data, } std::optional PpsParser::ParseSliceHeader( - ArrayView data) { + std::span data) { std::vector unpacked_buffer = H264::ParseRbsp(data); BitstreamReader slice_reader(unpacked_buffer); PpsParser::SliceHeader slice_header; @@ -76,7 +76,7 @@ std::optional PpsParser::ParseSliceHeader( } std::optional PpsParser::ParseInternal( - ArrayView buffer) { + std::span buffer) { BitstreamReader reader(buffer); PpsState pps; pps.id = reader.ReadExponentialGolomb(); diff --git a/common_video/h264/pps_parser.h b/common_video/h264/pps_parser.h index cdf3fb79a06..ef3ba21859a 100644 --- a/common_video/h264/pps_parser.h +++ b/common_video/h264/pps_parser.h @@ -15,8 +15,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -48,24 +47,24 @@ class PpsParser { }; // Unpack RBSP and parse PPS state from the supplied buffer. - static std::optional ParsePps(ArrayView data); + static std::optional ParsePps(std::span data); // TODO: bugs.webrtc.org/42225170 - Deprecate. static inline std::optional ParsePps(const uint8_t* data, size_t length) { - return ParsePps(MakeArrayView(data, length)); + return ParsePps(std::span(data, length)); } - static bool ParsePpsIds(ArrayView data, + static bool ParsePpsIds(std::span data, uint32_t* pps_id, uint32_t* sps_id); static std::optional ParseSliceHeader( - ArrayView data); + std::span data); protected: // Parse the PPS state, for a buffer where RBSP decoding has already been // performed. - static std::optional ParseInternal(ArrayView buffer); + static std::optional ParseInternal(std::span buffer); }; } // namespace webrtc diff --git a/common_video/h264/pps_parser_unittest.cc b/common_video/h264/pps_parser_unittest.cc index e0c1480152b..8bf1117bf52 100644 --- a/common_video/h264/pps_parser_unittest.cc +++ b/common_video/h264/pps_parser_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "rtc_base/bit_buffer.h" #include "rtc_base/buffer.h" @@ -138,7 +138,7 @@ void WritePps(const PpsParser::PpsState& pps, bit_buffer.GetCurrentOffset(&byte_offset, &bit_offset); } - H264::WriteRbsp(MakeArrayView(data, byte_offset), out_buffer); + H264::WriteRbsp(std::span(data, byte_offset), out_buffer); } class PpsParserTest : public ::testing::Test { @@ -223,7 +223,7 @@ TEST_F(PpsParserTest, MaxPps) { } TEST_F(PpsParserTest, ParseSliceHeader) { - ArrayView chunk(kH264BitstreamChunk); + std::span chunk(kH264BitstreamChunk); std::vector nalu_indices = H264::FindNaluIndices(chunk); EXPECT_EQ(nalu_indices.size(), 3ull); for (const auto& index : nalu_indices) { @@ -232,7 +232,7 @@ TEST_F(PpsParserTest, ParseSliceHeader) { if (nalu_type == H264::NaluType::kIdr) { // Skip NAL type header and parse slice header. std::optional slice_header = - PpsParser::ParseSliceHeader(chunk.subview( + PpsParser::ParseSliceHeader(chunk.subspan( index.payload_start_offset + 1, index.payload_size - 1)); ASSERT_TRUE(slice_header.has_value()); EXPECT_EQ(slice_header->first_mb_in_slice, 0u); diff --git a/common_video/h264/sps_parser.cc b/common_video/h264/sps_parser.cc index e789ec2ee4e..309f163b72d 100644 --- a/common_video/h264/sps_parser.cc +++ b/common_video/h264/sps_parser.cc @@ -13,19 +13,19 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "rtc_base/bitstream_reader.h" +namespace webrtc { + namespace { constexpr int kScalingDeltaMin = -128; constexpr int kScaldingDeltaMax = 127; } // namespace -namespace webrtc { - SpsParser::SpsState::SpsState() = default; SpsParser::SpsState::SpsState(const SpsState&) = default; SpsParser::SpsState::~SpsState() = default; @@ -36,7 +36,7 @@ SpsParser::SpsState::~SpsState() = default; // Unpack RBSP and parse SPS state from the supplied buffer. std::optional SpsParser::ParseSps( - ArrayView data) { + std::span data) { std::vector unpacked_buffer = H264::ParseRbsp(data); BitstreamReader reader(unpacked_buffer); return ParseSpsUpToVui(reader); diff --git a/common_video/h264/sps_parser.h b/common_video/h264/sps_parser.h index e5017ed515a..aaee5701f16 100644 --- a/common_video/h264/sps_parser.h +++ b/common_video/h264/sps_parser.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/system/rtc_export.h" @@ -47,7 +47,7 @@ class RTC_EXPORT SpsParser { }; // Unpack RBSP and parse SPS state from the supplied buffer. - static std::optional ParseSps(ArrayView data); + static std::optional ParseSps(std::span data); protected: // Parse the SPS state, up till the VUI part, for a buffer where RBSP diff --git a/common_video/h264/sps_parser_unittest.cc b/common_video/h264/sps_parser_unittest.cc index d2396d016a7..6ab21527255 100644 --- a/common_video/h264/sps_parser_unittest.cc +++ b/common_video/h264/sps_parser_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "rtc_base/bit_buffer.h" #include "rtc_base/buffer.h" @@ -111,7 +111,7 @@ void GenerateFakeSps(uint16_t width, } out_buffer->Clear(); - H264::WriteRbsp(MakeArrayView(rbsp, byte_count), out_buffer); + H264::WriteRbsp(std::span(rbsp, byte_count), out_buffer); } TEST(H264SpsParserTest, TestSampleSPSHdLandscape) { diff --git a/common_video/h264/sps_vui_rewriter.cc b/common_video/h264/sps_vui_rewriter.cc index beb32fa1489..be0cbf123db 100644 --- a/common_video/h264/sps_vui_rewriter.cc +++ b/common_video/h264/sps_vui_rewriter.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/video/color_space.h" #include "common_video/h264/h264_common.h" #include "common_video/h264/sps_parser.h" @@ -134,7 +134,7 @@ void SpsVuiRewriter::UpdateStats(ParseResult result, Direction direction) { } SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps( - ArrayView buffer, + std::span buffer, std::optional* sps, const ColorSpace* color_space, Buffer* destination) { @@ -211,7 +211,7 @@ SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps( } SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps( - ArrayView buffer, + std::span buffer, std::optional* sps, const ColorSpace* color_space, Buffer* destination, @@ -223,20 +223,20 @@ SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps( } Buffer SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite( - ArrayView buffer, + std::span buffer, const ColorSpace* color_space) { std::vector nalus = H264::FindNaluIndices(buffer); // Allocate some extra space for potentially adding a missing VUI. - Buffer output_buffer(/*size=*/0, /*capacity=*/buffer.size() + - nalus.size() * kMaxVuiSpsIncrease); + Buffer output_buffer = Buffer::CreateWithCapacity( + buffer.size() + nalus.size() * kMaxVuiSpsIncrease); for (const H264::NaluIndex& nalu_index : nalus) { // Copy NAL unit start code. - ArrayView start_code = buffer.subview( + std::span start_code = buffer.subspan( nalu_index.start_offset, nalu_index.payload_start_offset - nalu_index.start_offset); - ArrayView nalu = buffer.subview( + std::span nalu = buffer.subspan( nalu_index.payload_start_offset, nalu_index.payload_size); if (nalu.empty()) { continue; @@ -261,7 +261,7 @@ Buffer SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite( output_nalu.AppendData(nalu[0]); ParseResult result = - ParseAndRewriteSps(nalu.subview(H264::kNaluTypeSize), &sps, + ParseAndRewriteSps(nalu.subspan(H264::kNaluTypeSize), &sps, color_space, &output_nalu, Direction::kOutgoing); if (result == ParseResult::kVuiRewritten) { output_buffer.AppendData(start_code); diff --git a/common_video/h264/sps_vui_rewriter.h b/common_video/h264/sps_vui_rewriter.h index 665142f3a0e..d4e85cc9f4a 100644 --- a/common_video/h264/sps_vui_rewriter.h +++ b/common_video/h264/sps_vui_rewriter.h @@ -16,8 +16,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/video/color_space.h" #include "common_video/h264/sps_parser.h" #include "rtc_base/buffer.h" @@ -44,7 +44,7 @@ class SpsVuiRewriter : private SpsParser { // SPS state. This function assumes that any previous headers // (NALU start, type, Stap-A, etc) have already been parsed and that RBSP // decoding has been performed. - static ParseResult ParseAndRewriteSps(ArrayView buffer, + static ParseResult ParseAndRewriteSps(std::span buffer, std::optional* sps, const ColorSpace* color_space, Buffer* destination, @@ -53,11 +53,11 @@ class SpsVuiRewriter : private SpsParser { // Parses NAL units from `buffer`, strips AUD blocks and rewrites VUI in SPS // blocks if necessary. static Buffer ParseOutgoingBitstreamAndRewrite( - ArrayView buffer, + std::span buffer, const ColorSpace* color_space); private: - static ParseResult ParseAndRewriteSps(ArrayView buffer, + static ParseResult ParseAndRewriteSps(std::span buffer, std::optional* sps, const ColorSpace* color_space, Buffer* destination); diff --git a/common_video/h264/sps_vui_rewriter_unittest.cc b/common_video/h264/sps_vui_rewriter_unittest.cc index c1ea8038a33..ea31466a97e 100644 --- a/common_video/h264/sps_vui_rewriter_unittest.cc +++ b/common_video/h264/sps_vui_rewriter_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/video/color_space.h" #include "common_video/h264/h264_common.h" #include "common_video/h264/sps_parser.h" @@ -250,7 +250,7 @@ void GenerateFakeSps(const VuiHeader& vui, Buffer* out_buffer) { byte_count++; } - H264::WriteRbsp(MakeArrayView(rbsp, byte_count), out_buffer); + H264::WriteRbsp(std::span(rbsp, byte_count), out_buffer); } void TestSps(const VuiHeader& vui, diff --git a/common_video/h265/h265_bitstream_parser.cc b/common_video/h265/h265_bitstream_parser.cc index e150c584aed..362958db88f 100644 --- a/common_video/h265/h265_bitstream_parser.cc +++ b/common_video/h265/h265_bitstream_parser.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "common_video/h265/h265_pps_parser.h" #include "common_video/h265/h265_sps_parser.h" @@ -66,6 +66,8 @@ } \ } while (0) +namespace webrtc { + namespace { constexpr int kMaxAbsQpDeltaValue = 51; @@ -75,8 +77,6 @@ constexpr int kMaxRefIdxActive = 15; } // namespace -namespace webrtc { - H265BitstreamParser::H265BitstreamParser() = default; H265BitstreamParser::~H265BitstreamParser() = default; @@ -84,7 +84,7 @@ H265BitstreamParser::~H265BitstreamParser() = default; // section 7.3.6.1. You can find it on this page: // http://www.itu.int/rec/T-REC-H.265 H265BitstreamParser::Result H265BitstreamParser::ParseNonParameterSetNalu( - ArrayView source, + std::span source, uint8_t nalu_type) { last_slice_qp_delta_ = std::nullopt; last_slice_pps_id_ = std::nullopt; @@ -523,7 +523,7 @@ const H265SpsParser::SpsState* H265BitstreamParser::GetSPS(uint32_t id) const { return &it->second; } -void H265BitstreamParser::ParseSlice(ArrayView slice) { +void H265BitstreamParser::ParseSlice(std::span slice) { if (slice.empty()) { RTC_LOG(LS_WARNING) << "Empty slice in H265 bitstream."; return; @@ -534,7 +534,7 @@ void H265BitstreamParser::ParseSlice(ArrayView slice) { std::optional vps_state; if (slice.size() >= H265::kNaluHeaderSize) { vps_state = - H265VpsParser::ParseVps(slice.subview(H265::kNaluHeaderSize)); + H265VpsParser::ParseVps(slice.subspan(H265::kNaluHeaderSize)); } if (!vps_state) { @@ -548,7 +548,7 @@ void H265BitstreamParser::ParseSlice(ArrayView slice) { std::optional sps_state; if (slice.size() >= H265::kNaluHeaderSize) { sps_state = - H265SpsParser::ParseSps(slice.subview(H265::kNaluHeaderSize)); + H265SpsParser::ParseSps(slice.subspan(H265::kNaluHeaderSize)); } if (!sps_state) { RTC_LOG(LS_WARNING) << "Unable to parse SPS from H265 bitstream."; @@ -561,7 +561,7 @@ void H265BitstreamParser::ParseSlice(ArrayView slice) { std::optional pps_state; if (slice.size() >= H265::kNaluHeaderSize) { std::vector unpacked_buffer = - H265::ParseRbsp(slice.subview(H265::kNaluHeaderSize)); + H265::ParseRbsp(slice.subspan(H265::kNaluHeaderSize)); BitstreamReader slice_reader(unpacked_buffer); // pic_parameter_set_id: ue(v) uint32_t pps_id = slice_reader.ReadExponentialGolomb(); @@ -571,7 +571,7 @@ void H265BitstreamParser::ParseSlice(ArrayView slice) { IN_RANGE_OR_RETURN_VOID(sps_id, 0, 15); const H265SpsParser::SpsState* sps = GetSPS(sps_id); pps_state = - H265PpsParser::ParsePps(slice.subview(H265::kNaluHeaderSize), sps); + H265PpsParser::ParsePps(slice.subspan(H265::kNaluHeaderSize), sps); } if (!pps_state) { RTC_LOG(LS_WARNING) << "Unable to parse PPS from H265 bitstream."; @@ -597,7 +597,7 @@ void H265BitstreamParser::ParseSlice(ArrayView slice) { std::optional H265BitstreamParser::ParsePpsIdFromSliceSegmentLayerRbsp( - ArrayView data, + std::span data, uint8_t nalu_type) { std::vector unpacked_buffer = H265::ParseRbsp(data); BitstreamReader slice_reader(unpacked_buffer); @@ -625,7 +625,7 @@ H265BitstreamParser::ParsePpsIdFromSliceSegmentLayerRbsp( } std::optional H265BitstreamParser::IsFirstSliceSegmentInPic( - ArrayView data) { + std::span data) { std::vector unpacked_buffer = H265::ParseRbsp(data); BitstreamReader slice_reader(unpacked_buffer); @@ -638,12 +638,12 @@ std::optional H265BitstreamParser::IsFirstSliceSegmentInPic( return first_slice_segment_in_pic_flag; } -void H265BitstreamParser::ParseBitstream(ArrayView bitstream) { +void H265BitstreamParser::ParseBitstream(std::span bitstream) { lastHasBadRbsp_ = false; std::vector nalu_indices = H265::FindNaluIndices(bitstream); for (const H265::NaluIndex& index : nalu_indices) ParseSlice( - bitstream.subview(index.payload_start_offset, index.payload_size)); + bitstream.subspan(index.payload_start_offset, index.payload_size)); } std::optional H265BitstreamParser::GetLastSliceQp() const { diff --git a/common_video/h265/h265_bitstream_parser.h b/common_video/h265/h265_bitstream_parser.h index 3d90b75fa23..3ded24a13a3 100644 --- a/common_video/h265/h265_bitstream_parser.h +++ b/common_video/h265/h265_bitstream_parser.h @@ -15,8 +15,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/video_codecs/bitstream_parser.h" #include "common_video/h265/h265_pps_parser.h" #include "common_video/h265/h265_sps_parser.h" @@ -34,7 +34,7 @@ class RTC_EXPORT H265BitstreamParser : public BitstreamParser { ~H265BitstreamParser() override; // New interface. - void ParseBitstream(ArrayView bitstream) override; + void ParseBitstream(std::span bitstream) override; std::optional GetLastSliceQp() const override; std::optional GetLastSlicePpsId() const; @@ -42,13 +42,13 @@ class RTC_EXPORT H265BitstreamParser : public BitstreamParser { bool GetLastHasBadRbsp() const; static std::optional ParsePpsIdFromSliceSegmentLayerRbsp( - ArrayView data, + std::span data, uint8_t nalu_type); // Returns true if the slice segment is the first in the picture; otherwise // return false. If parse failed, return nullopt. static std::optional IsFirstSliceSegmentInPic( - ArrayView data); + std::span data); protected: enum Result { @@ -56,8 +56,8 @@ class RTC_EXPORT H265BitstreamParser : public BitstreamParser { kInvalidStream, kUnsupportedStream, }; - void ParseSlice(ArrayView slice); - Result ParseNonParameterSetNalu(ArrayView source, + void ParseSlice(std::span slice); + Result ParseNonParameterSetNalu(std::span source, uint8_t nalu_type); const H265PpsParser::PpsState* GetPPS(uint32_t id) const; diff --git a/common_video/h265/h265_bitstream_parser_unittest.cc b/common_video/h265/h265_bitstream_parser_unittest.cc index 7cfa1eabddb..e37b686f01d 100644 --- a/common_video/h265/h265_bitstream_parser_unittest.cc +++ b/common_video/h265/h265_bitstream_parser_unittest.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "test/gmock.h" #include "test/gtest.h" @@ -195,10 +194,8 @@ TEST(H265BitstreamParserTest, ReportsFirstSliceSegmentInPicFalse) { } TEST(H265BitstreamParserTest, ReportsFirstSliceSegmentInPicParseInvalidSlice) { - ArrayView slice_data(kH265SliceChunk); - EXPECT_THAT( - H265BitstreamParser::IsFirstSliceSegmentInPic(slice_data.subview(50)), - Eq(std::nullopt)); + EXPECT_THAT(H265BitstreamParser::IsFirstSliceSegmentInPic({}), + Eq(std::nullopt)); } } // namespace webrtc diff --git a/common_video/h265/h265_common.cc b/common_video/h265/h265_common.cc index 332bab7acc2..528145b055d 100644 --- a/common_video/h265/h265_common.cc +++ b/common_video/h265/h265_common.cc @@ -11,9 +11,9 @@ #include "common_video/h265/h265_common.h" #include +#include #include -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "common_video/h265/h265_inline.h" #include "rtc_base/buffer.h" @@ -23,7 +23,7 @@ namespace H265 { constexpr uint8_t kNaluTypeMask = 0x7E; -std::vector FindNaluIndices(ArrayView buffer) { +std::vector FindNaluIndices(std::span buffer) { std::vector indices = H264::FindNaluIndices(buffer); std::vector results; results.reserve(indices.size()); @@ -39,11 +39,11 @@ NaluType ParseNaluType(uint8_t data) { return static_cast((data & kNaluTypeMask) >> 1); } -std::vector ParseRbsp(ArrayView data) { +std::vector ParseRbsp(std::span data) { return H264::ParseRbsp(data); } -void WriteRbsp(ArrayView bytes, Buffer* destination) { +void WriteRbsp(std::span bytes, Buffer* destination) { H264::WriteRbsp(bytes, destination); } diff --git a/common_video/h265/h265_common.h b/common_video/h265/h265_common.h index a48297ebd8b..06c69a10c49 100644 --- a/common_video/h265/h265_common.h +++ b/common_video/h265/h265_common.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/system/rtc_export.h" @@ -82,12 +82,12 @@ struct NaluIndex { // Returns a vector of the NALU indices in the given buffer. RTC_EXPORT std::vector FindNaluIndices( - ArrayView buffer); + std::span buffer); // TODO: bugs.webrtc.org/42225170 - Deprecate. inline std::vector FindNaluIndices(const uint8_t* buffer, size_t buffer_size) { - return FindNaluIndices(MakeArrayView(buffer, buffer_size)); + return FindNaluIndices(std::span(buffer, buffer_size)); } // Get the NAL type from the header byte immediately following start sequence. @@ -107,23 +107,23 @@ RTC_EXPORT NaluType ParseNaluType(uint8_t data); // the 03 emulation byte. // Parse the given data and remove any emulation byte escaping. -std::vector ParseRbsp(ArrayView data); +std::vector ParseRbsp(std::span data); // TODO: bugs.webrtc.org/42225170 - Deprecate. inline std::vector ParseRbsp(const uint8_t* data, size_t length) { - return ParseRbsp(MakeArrayView(data, length)); + return ParseRbsp(std::span(data, length)); } // Write the given data to the destination buffer, inserting and emulation // bytes in order to escape any data the could be interpreted as a start // sequence. -void WriteRbsp(ArrayView bytes, Buffer* destination); +void WriteRbsp(std::span bytes, Buffer* destination); // TODO: bugs.webrtc.org/42225170 - Deprecate. inline void WriteRbsp(const uint8_t* bytes, size_t length, Buffer* destination) { - WriteRbsp(MakeArrayView(bytes, length), destination); + WriteRbsp(std::span(bytes, length), destination); } uint32_t Log2Ceiling(uint32_t value); diff --git a/common_video/h265/h265_pps_parser.cc b/common_video/h265/h265_pps_parser.cc index c94e362ec94..460e936a509 100644 --- a/common_video/h265/h265_pps_parser.cc +++ b/common_video/h265/h265_pps_parser.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "common_video/h265/h265_sps_parser.h" #include "rtc_base/bitstream_reader.h" @@ -52,20 +52,20 @@ } \ } while (0) +namespace webrtc { + namespace { constexpr int kMaxNumTileColumnWidth = 19; constexpr int kMaxNumTileRowHeight = 21; constexpr int kMaxRefIdxActive = 15; } // namespace -namespace webrtc { - // General note: this is based off the 08/2021 version of the H.265 standard. // You can find it on this page: // http://www.itu.int/rec/T-REC-H.265 std::optional H265PpsParser::ParsePps( - ArrayView data, + std::span data, const H265SpsParser::SpsState* sps) { // First, parse out rbsp, which is basically the source buffer minus emulation // bytes (the last byte of a 0x00 0x00 0x03 sequence). RBSP is defined in @@ -73,7 +73,7 @@ std::optional H265PpsParser::ParsePps( return ParseInternal(H265::ParseRbsp(data), sps); } -bool H265PpsParser::ParsePpsIds(ArrayView data, +bool H265PpsParser::ParsePpsIds(std::span data, uint32_t* pps_id, uint32_t* sps_id) { RTC_DCHECK(pps_id); @@ -91,7 +91,7 @@ bool H265PpsParser::ParsePpsIds(ArrayView data, } std::optional H265PpsParser::ParseInternal( - ArrayView buffer, + std::span buffer, const H265SpsParser::SpsState* sps) { BitstreamReader reader(buffer); PpsState pps; diff --git a/common_video/h265/h265_pps_parser.h b/common_video/h265/h265_pps_parser.h index 2cb3f67dabc..c73be46bcec 100644 --- a/common_video/h265/h265_pps_parser.h +++ b/common_video/h265/h265_pps_parser.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "common_video/h265/h265_sps_parser.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/system/rtc_export.h" @@ -46,17 +46,17 @@ class RTC_EXPORT H265PpsParser { }; // Unpack RBSP and parse PPS state from the supplied buffer. - static std::optional ParsePps(ArrayView data, + static std::optional ParsePps(std::span data, const H265SpsParser::SpsState* sps); // TODO: bugs.webrtc.org/42225170 - Deprecate. static inline std::optional ParsePps( const uint8_t* data, size_t length, const H265SpsParser::SpsState* sps) { - return ParsePps(MakeArrayView(data, length), sps); + return ParsePps(std::span(data, length), sps); } - static bool ParsePpsIds(ArrayView data, + static bool ParsePpsIds(std::span data, uint32_t* pps_id, uint32_t* sps_id); // TODO: bugs.webrtc.org/42225170 - Deprecate. @@ -64,14 +64,14 @@ class RTC_EXPORT H265PpsParser { size_t length, uint32_t* pps_id, uint32_t* sps_id) { - return ParsePpsIds(MakeArrayView(data, length), pps_id, sps_id); + return ParsePpsIds(std::span(data, length), pps_id, sps_id); } protected: // Parse the PPS state, for a bit buffer where RBSP decoding has already been // performed. static std::optional ParseInternal( - ArrayView buffer, + std::span buffer, const H265SpsParser::SpsState* sps); static bool ParsePpsIdsInternal(BitstreamReader& reader, uint32_t& pps_id, diff --git a/common_video/h265/h265_pps_parser_unittest.cc b/common_video/h265/h265_pps_parser_unittest.cc index 688e9e98d7c..668b89ace4d 100644 --- a/common_video/h265/h265_pps_parser_unittest.cc +++ b/common_video/h265/h265_pps_parser_unittest.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "common_video/h265/h265_sps_parser.h" #include "rtc_base/bit_buffer.h" @@ -164,7 +164,7 @@ void WritePps(const H265PpsParser::PpsState& pps, bit_buffer.GetCurrentOffset(&byte_offset, &bit_offset); } - H265::WriteRbsp(MakeArrayView(data, byte_offset), out_buffer); + H265::WriteRbsp(std::span(data, byte_offset), out_buffer); } class H265PpsParserTest : public ::testing::Test { diff --git a/common_video/h265/h265_sps_parser.cc b/common_video/h265/h265_sps_parser.cc index 1461b7400cd..da6f913f3c0 100644 --- a/common_video/h265/h265_sps_parser.cc +++ b/common_video/h265/h265_sps_parser.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/logging.h" @@ -53,20 +53,19 @@ } \ } while (0) +namespace webrtc { + namespace { -using OptionalSps = std::optional; +using OptionalSps = std::optional; using OptionalShortTermRefPicSet = - std::optional; -using OptionalProfileTierLevel = - std::optional; + std::optional; +using OptionalProfileTierLevel = std::optional; constexpr int kMaxNumSizeIds = 4; constexpr int kMaxNumMatrixIds = 6; constexpr int kMaxNumCoefs = 64; } // namespace -namespace webrtc { - H265SpsParser::ShortTermRefPicSet::ShortTermRefPicSet() = default; H265SpsParser::ProfileTierLevel::ProfileTierLevel() = default; @@ -108,7 +107,7 @@ size_t H265SpsParser::GetDpbMaxPicBuf(int general_profile_idc) { // Unpack RBSP and parse SPS state from the supplied buffer. std::optional H265SpsParser::ParseSps( - ArrayView data) { + std::span data) { return ParseSpsInternal(H265::ParseRbsp(data)); } @@ -389,7 +388,7 @@ H265SpsParser::ParseProfileTierLevel(bool profile_present, } std::optional H265SpsParser::ParseSpsInternal( - ArrayView buffer) { + std::span buffer) { BitstreamReader reader(buffer); // Now, we need to use a bit buffer to parse through the actual H265 SPS diff --git a/common_video/h265/h265_sps_parser.h b/common_video/h265/h265_sps_parser.h index 1bccf6adccc..03e588d267e 100644 --- a/common_video/h265/h265_sps_parser.h +++ b/common_video/h265/h265_sps_parser.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/system/rtc_export.h" @@ -106,11 +106,11 @@ class RTC_EXPORT H265SpsParser { }; // Unpack RBSP and parse SPS state from the supplied buffer. - static std::optional ParseSps(ArrayView data); + static std::optional ParseSps(std::span data); // TODO: bugs.webrtc.org/42225170 - Deprecate. static inline std::optional ParseSps(const uint8_t* data, size_t length) { - return ParseSps(MakeArrayView(data, length)); + return ParseSps(std::span(data, length)); } static bool ParseScalingListData(BitstreamReader& reader); @@ -131,7 +131,7 @@ class RTC_EXPORT H265SpsParser { // Parse the SPS state, for a bit buffer where RBSP decoding has already been // performed. static std::optional ParseSpsInternal( - ArrayView buffer); + std::span buffer); // From Table A.8 - General tier and level limits. static int GetMaxLumaPs(int general_level_idc); diff --git a/common_video/h265/h265_sps_parser_unittest.cc b/common_video/h265/h265_sps_parser_unittest.cc index 1d3e1b2859c..5a8e051554a 100644 --- a/common_video/h265/h265_sps_parser_unittest.cc +++ b/common_video/h265/h265_sps_parser_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "rtc_base/bit_buffer.h" #include "rtc_base/buffer.h" @@ -370,7 +370,7 @@ void WriteSps(uint16_t width, } out_buffer->Clear(); - H265::WriteRbsp(MakeArrayView(rbsp, byte_count), out_buffer); + H265::WriteRbsp(std::span(rbsp, byte_count), out_buffer); } class H265SpsParserTest : public ::testing::Test { diff --git a/common_video/h265/h265_vps_parser.cc b/common_video/h265/h265_vps_parser.cc index 8ebe5f96a15..83821c5ad34 100644 --- a/common_video/h265/h265_vps_parser.cc +++ b/common_video/h265/h265_vps_parser.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "rtc_base/bitstream_reader.h" @@ -27,12 +27,12 @@ H265VpsParser::VpsState::VpsState() = default; // Unpack RBSP and parse VPS state from the supplied buffer. std::optional H265VpsParser::ParseVps( - ArrayView data) { + std::span data) { return ParseInternal(H265::ParseRbsp(data)); } std::optional H265VpsParser::ParseInternal( - ArrayView buffer) { + std::span buffer) { BitstreamReader reader(buffer); // Now, we need to use a bit buffer to parse through the actual H265 VPS diff --git a/common_video/h265/h265_vps_parser.h b/common_video/h265/h265_vps_parser.h index 423ff28b2b3..895222fb247 100644 --- a/common_video/h265/h265_vps_parser.h +++ b/common_video/h265/h265_vps_parser.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -36,17 +36,17 @@ class RTC_EXPORT H265VpsParser { }; // Unpack RBSP and parse VPS state from the supplied buffer. - static std::optional ParseVps(ArrayView data); + static std::optional ParseVps(std::span data); // TODO: bugs.webrtc.org/42225170 - Deprecate. static inline std::optional ParseVps(const uint8_t* data, size_t length) { - return ParseVps(MakeArrayView(data, length)); + return ParseVps(std::span(data, length)); } protected: // Parse the VPS state, for a bit buffer where RBSP decoding has already been // performed. - static std::optional ParseInternal(ArrayView buffer); + static std::optional ParseInternal(std::span buffer); }; } // namespace webrtc diff --git a/common_video/include/bitrate_adjuster.h b/common_video/include/bitrate_adjuster.h index 966c35dd7e8..6cd92b7debd 100644 --- a/common_video/include/bitrate_adjuster.h +++ b/common_video/include/bitrate_adjuster.h @@ -16,10 +16,13 @@ #include +#include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "rtc_base/rate_statistics.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/thread_annotations.h" +#include "system_wrappers/include/clock.h" namespace webrtc { @@ -31,9 +34,16 @@ class RTC_EXPORT BitrateAdjuster { // min_adjusted_bitrate_pct and max_adjusted_bitrate_pct are the lower and // upper bound outputted adjusted bitrates as a percentage of the target // bitrate. - BitrateAdjuster(float min_adjusted_bitrate_pct, + BitrateAdjuster(Clock* absl_nonnull clock, + float min_adjusted_bitrate_pct, float max_adjusted_bitrate_pct); - virtual ~BitrateAdjuster() {} + ABSL_DEPRECATE_AND_INLINE() + BitrateAdjuster(float min_adjusted_bitrate_pct, + float max_adjusted_bitrate_pct) + : BitrateAdjuster(Clock::GetRealTimeClock(), + min_adjusted_bitrate_pct, + max_adjusted_bitrate_pct) {} + virtual ~BitrateAdjuster() = default; static const uint32_t kBitrateUpdateIntervalMs; static const uint32_t kBitrateUpdateFrameInterval; @@ -72,6 +82,7 @@ class RTC_EXPORT BitrateAdjuster { RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); mutable Mutex mutex_; + Clock& clock_; const float min_adjusted_bitrate_pct_; const float max_adjusted_bitrate_pct_; // The bitrate we want. diff --git a/common_video/test/BUILD.gn b/common_video/test/BUILD.gn index 10ebbaaae37..6e93eca8d87 100644 --- a/common_video/test/BUILD.gn +++ b/common_video/test/BUILD.gn @@ -17,7 +17,6 @@ if (rtc_include_tests) { ] deps = [ "../../api:rtp_packet_info", - "../../api/video:video_frame", "../../api/video:video_rtp_headers", ] } diff --git a/docs/native-code/rtp-hdrext/abs-send-time/README.md b/docs/native-code/rtp-hdrext/abs-send-time/README.md index 86c3c733dcf..ab34492cc81 100644 --- a/docs/native-code/rtp-hdrext/abs-send-time/README.md +++ b/docs/native-code/rtp-hdrext/abs-send-time/README.md @@ -26,6 +26,8 @@ Relation to NTP timestamps: abs_send_time_24 = (ntp_timestamp_64 >> 14) & 0x00ffffff ; NTP timestamp is 32 bits for whole seconds, 32 bits fraction of second. -Notes: Packets are time stamped when going out, preferably close to metal. +Notes: Packets are time stamped as close as possible to the time the packet is +sent on the wire. The current implementation allocates space before pacing +and stamps them after pacing and before encryption. Intermediate RTP relays (entities possibly altering the stream) should remove the extension or set its own timestamp. diff --git a/examples/BUILD.gn b/examples/BUILD.gn index 759b97c18f4..507b575764e 100644 --- a/examples/BUILD.gn +++ b/examples/BUILD.gn @@ -65,7 +65,6 @@ rtc_library("read_auth_file") { "turnserver/read_auth_file.h", ] deps = [ - "../api:array_view", "../rtc_base:stringutils", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -644,7 +643,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { ":AppRTCMobile_lib", ":apprtcmobile_test_sources", "../sdk:framework_objc", - "//test:test_support", + "//test:test_support_objc", ] ldflags = [ "-all_load" ] } @@ -677,7 +676,6 @@ if (is_linux || is_chromeos || is_win) { ] deps = [ - "../api:array_view", "../api:async_dns_resolver", "../api:audio_options_api", "../api:create_frame_generator", @@ -822,6 +820,7 @@ if (is_linux || is_chromeos || is_win) { "../pc:rtc_pc", "../rtc_base:async_udp_socket", "../rtc_base:ip_address", + "../rtc_base:net_helper", "../rtc_base:socket_address", "../rtc_base:socket_server", "../rtc_base:threading", diff --git a/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java b/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java index b00589439bc..df4f499e9d9 100644 --- a/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java +++ b/examples/androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java @@ -219,6 +219,7 @@ private List iceServersFromPCConfigJSON(String pcConfi } // Return the contents of an InputStream as a String. + @SuppressWarnings("ScannerUseDelimiter") private static String drainStream(InputStream in) { Scanner s = new Scanner(in, "UTF-8").useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; diff --git a/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java b/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java index 93028ae783e..bc655bac8b0 100644 --- a/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java +++ b/examples/androidapp/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java @@ -16,6 +16,7 @@ import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URL; +import java.nio.charset.Charset; import java.util.Scanner; /** @@ -59,7 +60,7 @@ private void sendHttpMessage() { HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); byte[] postData = new byte[0]; if (message != null) { - postData = message.getBytes("UTF-8"); + postData = message.getBytes(Charset.forName("UTF-8")); } connection.setRequestMethod(method); connection.setUseCaches(false); @@ -108,6 +109,7 @@ private void sendHttpMessage() { } // Return the contents of an InputStream as a String. + @SuppressWarnings("ScannerUseDelimiter") private static String drainStream(InputStream in) { Scanner s = new Scanner(in, "UTF-8").useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; diff --git a/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java b/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java index dcc79b1ec23..04b572bfed1 100644 --- a/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java +++ b/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java @@ -34,17 +34,17 @@ import java.util.List; import org.appspot.apprtc.AppRTCBluetoothManager.State; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLog; -import org.robolectric.RobolectricTestRunner; /** - * Verifies basic behavior of the AppRTCBluetoothManager class. - * Note that the test object uses an AppRTCAudioManager (injected in ctor), - * but a mocked version is used instead. Hence, the parts "driven" by the AppRTC - * audio manager are not included in this test. + * Verifies basic behavior of the AppRTCBluetoothManager class. Note that the test object uses an + * AppRTCAudioManager (injected in ctor), but a mocked version is used instead. Hence, the parts + * "driven" by the AppRTC audio manager are not included in this test. */ @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) @@ -76,54 +76,55 @@ public void setUp() { when(mockedAudioManager.isBluetoothScoAvailableOffCall()).thenReturn(true); // Create the test object and override protected methods for this test. - bluetoothManager = new AppRTCBluetoothManager(context, mockedAppRtcAudioManager) { - @Override - protected AudioManager getAudioManager(Context context) { - Log.d(TAG, "getAudioManager"); - return mockedAudioManager; - } + bluetoothManager = + new AppRTCBluetoothManager(context, mockedAppRtcAudioManager) { + @Override + protected AudioManager getAudioManager(Context context) { + Log.d(TAG, "getAudioManager"); + return mockedAudioManager; + } - @Override - protected void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { - Log.d(TAG, "registerReceiver"); - if (filter.hasAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) - && filter.hasAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { - // Gives access to the real broadcast receiver so the test can use it. - bluetoothHeadsetStateReceiver = receiver; - } - } + @Override + protected void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + Log.d(TAG, "registerReceiver"); + if (filter.hasAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) + && filter.hasAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { + // Gives access to the real broadcast receiver so the test can use it. + bluetoothHeadsetStateReceiver = receiver; + } + } - @Override - protected void unregisterReceiver(BroadcastReceiver receiver) { - Log.d(TAG, "unregisterReceiver"); - if (receiver == bluetoothHeadsetStateReceiver) { - bluetoothHeadsetStateReceiver = null; - } - } + @Override + protected void unregisterReceiver(BroadcastReceiver receiver) { + Log.d(TAG, "unregisterReceiver"); + if (receiver == bluetoothHeadsetStateReceiver) { + bluetoothHeadsetStateReceiver = null; + } + } - @Override - protected boolean getBluetoothProfileProxy( - Context context, BluetoothProfile.ServiceListener listener, int profile) { - Log.d(TAG, "getBluetoothProfileProxy"); - if (profile == BluetoothProfile.HEADSET) { - // Allows the test to access the real Bluetooth service listener object. - bluetoothServiceListener = listener; - } - return true; - } + @Override + protected boolean getBluetoothProfileProxy( + Context context, BluetoothProfile.ServiceListener listener, int profile) { + Log.d(TAG, "getBluetoothProfileProxy"); + if (profile == BluetoothProfile.HEADSET) { + // Allows the test to access the real Bluetooth service listener object. + bluetoothServiceListener = listener; + } + return true; + } - @Override - protected boolean hasPermission(Context context, String permission) { - Log.d(TAG, "hasPermission(" + permission + ")"); - // Ensure that the client asks for Bluetooth permission. - return android.Manifest.permission.BLUETOOTH.equals(permission); - } + @Override + protected boolean hasPermission(Context context, String permission) { + Log.d(TAG, "hasPermission(" + permission + ")"); + // Ensure that the client asks for Bluetooth permission. + return android.Manifest.permission.BLUETOOTH.equals(permission); + } - @Override - protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) { - // Do nothing in tests. No need to mock BluetoothAdapter. - } - }; + @Override + protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) { + // Do nothing in tests. No need to mock BluetoothAdapter. + } + }; } // Verify that Bluetooth service listener for headset profile is properly initialized. @@ -203,6 +204,7 @@ public void testBluetoothHeadsetConnected() { // Verify correct state sequence for a case when a BT headset is available, // followed by BT SCO audio being enabled and then stopped. @Test + @Ignore("https://issues.webrtc.org/481918423") public void testBluetoothScoAudioStartAndStop() { bluetoothManager.start(); assertEquals(State.HEADSET_UNAVAILABLE, bluetoothManager.getState()); @@ -214,15 +216,13 @@ public void testBluetoothScoAudioStartAndStop() { assertEquals(State.SCO_CONNECTED, bluetoothManager.getState()); bluetoothManager.stopScoAudio(); simulateBluetoothScoConnectionDisconnected(); - assertEquals(State.SCO_DISCONNECTING,bluetoothManager.getState()); + assertEquals(State.SCO_DISCONNECTING, bluetoothManager.getState()); bluetoothManager.stop(); assertEquals(State.UNINITIALIZED, bluetoothManager.getState()); verify(mockedAppRtcAudioManager, times(3)).updateAudioDeviceState(); } - /** - * Private helper methods. - */ + /** Private helper methods. */ private void simulateBluetoothServiceConnectedWithNoConnectedHeadset() { mockedBluetoothDeviceList.clear(); when(mockedBluetoothHeadset.getConnectedDevices()).thenReturn(mockedBluetoothDeviceList); diff --git a/examples/androidnativeapi/BUILD.gn b/examples/androidnativeapi/BUILD.gn index adc9a332a0c..ff4458e79fd 100644 --- a/examples/androidnativeapi/BUILD.gn +++ b/examples/androidnativeapi/BUILD.gn @@ -54,6 +54,8 @@ if (is_android) { "../../api:rtc_error", "../../api:scoped_refptr", "../../api:sequence_checker", + "../../api/environment", + "../../api/environment:environment_factory", "../../api/video:video_frame", "../../rtc_base:checks", "../../rtc_base:logging", diff --git a/examples/androidnativeapi/jni/android_call_client.cc b/examples/androidnativeapi/jni/android_call_client.cc index e2fa4e79b44..b81d159f05f 100644 --- a/examples/androidnativeapi/jni/android_call_client.cc +++ b/examples/androidnativeapi/jni/android_call_client.cc @@ -19,12 +19,12 @@ #include "api/create_modular_peer_connection_factory.h" #include "api/data_channel_interface.h" #include "api/enable_media_with_defaults.h" +#include "api/environment/environment_factory.h" #include "api/jsep.h" #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" -#include "api/rtc_event_log/rtc_event_log_factory.h" #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -93,7 +93,9 @@ class SetLocalSessionDescriptionObserver } // namespace AndroidCallClient::AndroidCallClient() - : call_started_(false), pc_observer_(std::make_unique(this)) { + : env_(webrtc::CreateEnvironment()), + call_started_(false), + pc_observer_(std::make_unique(this)) { thread_checker_.Detach(); CreatePeerConnectionFactory(); } @@ -115,9 +117,10 @@ void AndroidCallClient::Call(JNIEnv* env, local_sink_ = webrtc::JavaToNativeVideoSink(env, local_sink.obj()); remote_sink_ = webrtc::JavaToNativeVideoSink(env, remote_sink.obj()); - video_source_ = webrtc::CreateJavaVideoSource(env, signaling_thread_.get(), - /* is_screencast= */ false, - /* align_timestamps= */ true); + video_source_ = + webrtc::CreateJavaVideoSource(env, signaling_thread_.get(), + /* is_screencast= */ false, + /* align_timestamps= */ true, env_); CreatePeerConnection(); Connect(); @@ -168,10 +171,10 @@ void AndroidCallClient::CreatePeerConnectionFactory() { RTC_CHECK(signaling_thread_->Start()) << "Failed to start thread"; webrtc::PeerConnectionFactoryDependencies pcf_deps; + pcf_deps.env = env_; pcf_deps.network_thread = network_thread_.get(); pcf_deps.worker_thread = worker_thread_.get(); pcf_deps.signaling_thread = signaling_thread_.get(); - pcf_deps.event_log_factory = std::make_unique(); pcf_deps.video_encoder_factory = std::make_unique(); diff --git a/examples/androidnativeapi/jni/android_call_client.h b/examples/androidnativeapi/jni/android_call_client.h index 6fffdada8e5..5853ae622af 100644 --- a/examples/androidnativeapi/jni/android_call_client.h +++ b/examples/androidnativeapi/jni/android_call_client.h @@ -15,6 +15,7 @@ #include +#include "api/environment/environment.h" #include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -49,6 +50,7 @@ class AndroidCallClient { void CreatePeerConnection() RTC_RUN_ON(thread_checker_); void Connect() RTC_RUN_ON(thread_checker_); + const webrtc::Environment env_; webrtc::SequenceChecker thread_checker_; bool call_started_ RTC_GUARDED_BY(thread_checker_); diff --git a/examples/androidvoip/BUILD.gn b/examples/androidvoip/BUILD.gn index 19548750509..c26d2b2b50f 100644 --- a/examples/androidvoip/BUILD.gn +++ b/examples/androidvoip/BUILD.gn @@ -55,7 +55,6 @@ if (is_android) { deps = [ ":generated_jni", - "../../api:array_view", "../../api:sequence_checker", "../../api/audio:builtin_audio_processing_builder", "../../api/environment", diff --git a/examples/androidvoip/jni/android_voip_client.cc b/examples/androidvoip/jni/android_voip_client.cc index 2c1fd43d021..53489da5e2e 100644 --- a/examples/androidvoip/jni/android_voip_client.cc +++ b/examples/androidvoip/jni/android_voip_client.cc @@ -20,12 +20,12 @@ #include #include #include +#include #include #include #include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/audio/builtin_audio_processing_builder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" @@ -481,7 +481,7 @@ void AndroidVoipClient::SendRtpPacket(const std::vector& packet_copy) { } } -bool AndroidVoipClient::SendRtp(webrtc::ArrayView packet, +bool AndroidVoipClient::SendRtp(std::span packet, const webrtc::PacketOptions& options) { std::vector packet_copy(packet.begin(), packet.end()); voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { @@ -501,7 +501,7 @@ void AndroidVoipClient::SendRtcpPacket( } } -bool AndroidVoipClient::SendRtcp(webrtc::ArrayView packet, +bool AndroidVoipClient::SendRtcp(std::span packet, const webrtc::PacketOptions& options) { std::vector packet_copy(packet.begin(), packet.end()); voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { @@ -519,7 +519,7 @@ void AndroidVoipClient::ReadRTPPacket(const std::vector& packet_copy) { } webrtc::VoipResult result = voip_engine_->Network().ReceivedRTPPacket( *channel_, - webrtc::ArrayView(packet_copy.data(), packet_copy.size())); + std::span(packet_copy.data(), packet_copy.size())); RTC_CHECK(result == webrtc::VoipResult::kOk); } @@ -543,7 +543,7 @@ void AndroidVoipClient::ReadRTCPPacket( } webrtc::VoipResult result = voip_engine_->Network().ReceivedRTCPPacket( *channel_, - webrtc::ArrayView(packet_copy.data(), packet_copy.size())); + std::span(packet_copy.data(), packet_copy.size())); RTC_CHECK(result == webrtc::VoipResult::kOk); } diff --git a/examples/androidvoip/jni/android_voip_client.h b/examples/androidvoip/jni/android_voip_client.h index 7d9e499ae32..885921fd50a 100644 --- a/examples/androidvoip/jni/android_voip_client.h +++ b/examples/androidvoip/jni/android_voip_client.h @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/call/transport.h" #include "api/environment/environment.h" @@ -121,9 +121,9 @@ class AndroidVoipClient : public webrtc::Transport { void Delete(JNIEnv* env); // Implementation for Transport. - bool SendRtp(webrtc::ArrayView packet, + bool SendRtp(std::span packet, const webrtc::PacketOptions& options) override; - bool SendRtcp(webrtc::ArrayView packet, + bool SendRtcp(std::span packet, const webrtc::PacketOptions& options) override; void OnSignalReadRTPPacket(webrtc::AsyncPacketSocket* socket, diff --git a/examples/peerconnection/client/conductor.cc b/examples/peerconnection/client/conductor.cc index e71a900e52f..1fe65744ea0 100644 --- a/examples/peerconnection/client/conductor.cc +++ b/examples/peerconnection/client/conductor.cc @@ -245,6 +245,8 @@ bool Conductor::CreatePeerConnection() { config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; webrtc::PeerConnectionInterface::IceServer server; server.uri = GetPeerConnectionString(); + server.username = GetTurnUserName(); + server.password = GetTurnPassword(); config.servers.push_back(server); webrtc::PeerConnectionDependencies pc_dependencies(this); diff --git a/examples/peerconnection/client/defaults.cc b/examples/peerconnection/client/defaults.cc index faf86cf6422..3da10727b28 100644 --- a/examples/peerconnection/client/defaults.cc +++ b/examples/peerconnection/client/defaults.cc @@ -47,6 +47,14 @@ std::string GetDefaultServerName() { return GetEnvVarOrDefault("WEBRTC_SERVER", "localhost"); } +std::string GetTurnUserName() { + return GetEnvVarOrDefault("WEBRTC_TURN_USER", ""); +} + +std::string GetTurnPassword() { + return GetEnvVarOrDefault("WEBRTC_TURN_PASSWORD", ""); +} + std::string GetPeerName() { char computer_name[256]; std::string ret(GetEnvVarOrDefault("USERNAME", "user")); diff --git a/examples/peerconnection/client/defaults.h b/examples/peerconnection/client/defaults.h index 30936fd9d4a..47f61c877bc 100644 --- a/examples/peerconnection/client/defaults.h +++ b/examples/peerconnection/client/defaults.h @@ -26,4 +26,7 @@ std::string GetPeerConnectionString(); std::string GetDefaultServerName(); std::string GetPeerName(); +std::string GetTurnUserName(); +std::string GetTurnPassword(); + #endif // EXAMPLES_PEERCONNECTION_CLIENT_DEFAULTS_H_ diff --git a/examples/peerconnection/client/linux/main.cc b/examples/peerconnection/client/linux/main.cc index 60b7b53a344..358d28ffe1e 100644 --- a/examples/peerconnection/client/linux/main.cc +++ b/examples/peerconnection/client/linux/main.cc @@ -92,7 +92,9 @@ int main(int argc, char* argv[]) { wnd.Create(); CustomSocketServer socket_server(&wnd); - webrtc::AutoSocketServerThread thread(&socket_server); + std::unique_ptr thread = + std::make_unique(&socket_server); + webrtc::ThreadManager::Instance()->SetCurrentThread(thread.get()); webrtc::InitializeSSL(); // Must be constructed after we set the socketserver. @@ -101,7 +103,8 @@ int main(int argc, char* argv[]) { socket_server.set_client(&client); socket_server.set_conductor(conductor.get()); - thread.Run(); + thread->Run(); + webrtc::ThreadManager::Instance()->SetCurrentThread(nullptr); // gtk_main(); wnd.Destroy(); diff --git a/examples/peerconnection/client/linux/main_wnd.h b/examples/peerconnection/client/linux/main_wnd.h index cee1d3ab640..db730e662ef 100644 --- a/examples/peerconnection/client/linux/main_wnd.h +++ b/examples/peerconnection/client/linux/main_wnd.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/media_stream_interface.h" #include "api/scoped_refptr.h" #include "api/video/video_frame.h" @@ -94,7 +94,7 @@ class GtkMainWnd : public MainWindow { // VideoSinkInterface implementation void OnFrame(const webrtc::VideoFrame& frame) override; - webrtc::ArrayView image() const { return image_; } + std::span image() const { return image_; } int width() const { return width_; } diff --git a/examples/peerconnection/client/main.cc b/examples/peerconnection/client/main.cc index 9320fd9909f..83876e4f393 100644 --- a/examples/peerconnection/client/main.cc +++ b/examples/peerconnection/client/main.cc @@ -84,7 +84,9 @@ int PASCAL wWinMain(HINSTANCE instance, int cmd_show) { webrtc::WinsockInitializer winsock_init; webrtc::PhysicalSocketServer ss; - webrtc::AutoSocketServerThread main_thread(&ss); + std::unique_ptr main_thread = + std::make_unique(&ss); + webrtc::ThreadManager::Instance()->SetCurrentThread(main_thread.get()); WindowsCommandLineArguments win_args; int argc = win_args.argc(); @@ -140,5 +142,6 @@ int PASCAL wWinMain(HINSTANCE instance, } webrtc::CleanupSSL(); + webrtc::ThreadManager::Instance()->SetCurrentThread(nullptr); return 0; } diff --git a/examples/turnserver/read_auth_file.cc b/examples/turnserver/read_auth_file.cc index c1b6eff2d60..65d1e419719 100644 --- a/examples/turnserver/read_auth_file.cc +++ b/examples/turnserver/read_auth_file.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/string_encode.h" namespace webrtc_examples { @@ -28,7 +28,7 @@ std::map ReadAuthFile(std::istream* s) { if (sep == std::string::npos) continue; char buf[32]; - size_t len = webrtc::hex_decode(webrtc::ArrayView(buf), + size_t len = webrtc::hex_decode(std::span(buf), absl::string_view(line).substr(sep + 1)); if (len > 0) { name_to_key.emplace(line.substr(0, sep), std::string(buf, len)); diff --git a/examples/turnserver/turnserver_main.cc b/examples/turnserver/turnserver_main.cc index 3bbf406f4d4..41d767254d6 100644 --- a/examples/turnserver/turnserver_main.cc +++ b/examples/turnserver/turnserver_main.cc @@ -20,10 +20,10 @@ #include "api/environment/environment_factory.h" #include "examples/turnserver/read_auth_file.h" #include "p2p/base/basic_packet_socket_factory.h" -#include "p2p/base/port_interface.h" #include "p2p/test/turn_server.h" #include "rtc_base/async_udp_socket.h" #include "rtc_base/ip_address.h" +#include "rtc_base/net_helper.h" #include "rtc_base/physical_socket_server.h" #include "rtc_base/socket_address.h" #include "rtc_base/thread.h" @@ -75,7 +75,9 @@ int main(int argc, char* argv[]) { const webrtc::Environment env = webrtc::CreateEnvironment(); webrtc::PhysicalSocketServer socket_server; - webrtc::AutoSocketServerThread main(&socket_server); + std::unique_ptr main = + std::make_unique(&socket_server); + webrtc::ThreadManager::Instance()->SetCurrentThread(main.get()); std::unique_ptr int_socket = webrtc::AsyncUDPSocket::Create(env, int_addr, socket_server); if (!int_socket) { @@ -84,7 +86,7 @@ int main(int argc, char* argv[]) { return 1; } - webrtc::TurnServer server(env, &main); + webrtc::TurnServer server(env, main.get()); std::fstream auth_file(argv[4], std::fstream::in); TurnFileAuth auth(auth_file.is_open() @@ -100,6 +102,7 @@ int main(int argc, char* argv[]) { std::cout << "Listening internally at " << int_addr.ToString() << std::endl; - main.Run(); + main->Run(); + webrtc::ThreadManager::Instance()->SetCurrentThread(nullptr); return 0; } diff --git a/experiments/field_trials.py b/experiments/field_trials.py index 1654f02b789..679fa3c4d69 100755 --- a/experiments/field_trials.py +++ b/experiments/field_trials.py @@ -146,6 +146,9 @@ def bug_url(self) -> str: FieldTrial('WebRTC-LibvpxVp8Encoder-AndroidSpecificThreadingSettings', 42226191, date(2024, 9, 1)), + FieldTrial('WebRTC-MediaTaskQueuePriorities', + 470337728, + date(2027, 4, 1)), FieldTrial('WebRTC-MixedCodecSimulcast', 362277533, date(2025, 9, 1)), @@ -197,9 +200,6 @@ def bug_url(self) -> str: FieldTrial('WebRTC-ReceiveBufferSize', 42225927, date(2024, 4, 1)), - FieldTrial('WebRTC-RtcEventLogEncodeDependencyDescriptor', - 42225280, - date(2024, 4, 1)), FieldTrial('WebRTC-RtcEventLogEncodeNetEqSetMinimumDelayKillSwitch', 42225058, date(2024, 4, 1)), @@ -221,6 +221,9 @@ def bug_url(self) -> str: FieldTrial('WebRTC-TimestampExtrapolatorConfig', 424739326, date(2026, 6, 30)), + FieldTrial('WebRTC-UnifiedCommunications', + 466507512, + date(2026, 6, 30)), FieldTrial('WebRTC-UseAbsCapTimeForG2gMetric', 401512883, date(2025, 9, 10)), diff --git a/extensions/android/audio-processing/src/external_processing_factory_jni.cc b/extensions/android/audio-processing/src/external_processing_factory_jni.cc index 2f1cd0aeb21..3878313e6dd 100644 --- a/extensions/android/audio-processing/src/external_processing_factory_jni.cc +++ b/extensions/android/audio-processing/src/external_processing_factory_jni.cc @@ -45,7 +45,8 @@ static jlong JNI_NativeExternalAudioProcessingFactory_CreateAudioProcessingModul .Build(webrtc::CreateEnvironment()); webrtc::AudioProcessing::Config config; config.echo_canceller.enabled = false; - config.echo_canceller.mobile_mode = true; + // M148 removed AECM (mobile echo canceller); Config::EchoCanceller no longer + // has mobile_mode. AEC is disabled here anyway, so nothing to preserve. apm->ApplyConfig(config); apm_ptr = apm.release(); return webrtc::jni::jlongFromPointer(apm_ptr); diff --git a/extensions/android/simulcast/BUILD.gn b/extensions/android/simulcast/BUILD.gn index 27c96729281..a98cbdbd17e 100644 --- a/extensions/android/simulcast/BUILD.gn +++ b/extensions/android/simulcast/BUILD.gn @@ -5,7 +5,12 @@ import("//third_party/jni_zero/jni_zero.gni") rtc_library("simulcast_jni") { visibility = [ "*" ] - allow_poison = [ "software_video_codecs" ] + allow_poison = [ + "software_video_codecs", + + # M148: video_jni -> environment_jni -> api:field_trials carries this poison. + "environment_construction", + ] sources = [ "src/simulcast_video_encoder.cc", "src/simulcast_video_encoder.h", diff --git a/g3doc/abseil-in-webrtc.md b/g3doc/abseil-in-webrtc.md index af305cb1c55..7a2f8deb107 100644 --- a/g3doc/abseil-in-webrtc.md +++ b/g3doc/abseil-in-webrtc.md @@ -40,6 +40,7 @@ on a monolithic Abseil build target that will generate a shared library. * The macros in `absl/base/attributes.h`, `absl/base/config.h` and `absl/base/macros.h`. * `absl/numeric/bits.h` +* `absl/crc` * Single argument `absl::StrCat` * ABSL_FLAG is allowed in tests and tools, but disallowed in in non-test code. @@ -62,13 +63,10 @@ on a monolithic Abseil build target that will generate a shared library. ### `absl::Span` -*Use `webrtc::ArrayView` instead.* +*Use `std::span` instead.* -`absl::Span` differs from `webrtc::ArrayView` on several points, and both -of them differ from the `std::span` introduced in C++20. We should just keep -using `webrtc::ArrayView` and avoid `absl::Span`. Note that we are planning -to replace `webrtc::ArrayView` with `std::span` rather than with `absl::Span`, -see https://bugs.webrtc.org/439801349 +`absl::Span` differs from `std::span` on several points, in particular lacks +static extent template parameter that WebRTC relies on. ### `absl::StrCat`, `absl::StrAppend`, `absl::StrJoin`, `absl::StrSplit` diff --git a/g3doc/dtls_stun_piggyback.md b/g3doc/dtls_stun_piggyback.md new file mode 100644 index 00000000000..cb9e3962404 --- /dev/null +++ b/g3doc/dtls_stun_piggyback.md @@ -0,0 +1,47 @@ +Piggybacking DTLS on Stun is a mechanism whereby we send the initial DTLS +handshake over the STUN exchange that opens up the data path, thereby saving round +trips. + +The implementation is in dtls_stun_piggyback_controller.{h,cc} and is tested by +dtls_ice_integration_test.cc. + +The state machine is as follows: + +Initial state: +CLIENT: TENTATIVE +SERVER: TENTATIVE + +A "flight" is a set of one or more DTLS messages (see resp. RFC). + +*** DTLS1.2 (RFC 6347) *** + +Flight 1: CLIENT => SERVER +SERVER when receiving Flight 1: TENTATIVE => CONFIRMED + +Flight 2: SERVER => CLIENT +CLIENT when receiving Flight 2: TENTATIVE => CONFIRMED + +Flight 3: CLIENT => SERVER +SERVER when receiving Flight 3: CONFIRMED => PENDING (dtls writable) + +Flight 4: SERVER => CLIENT +Client when receiving Flight 4: CONFIRMED => COMPLETE (dtls writable) + +SERVER will switch to COMPLETE when one of +- Flight 4 is acked +- It receives decryptable data from CLIENT (i.e. it learns that CLIENT is dtls writable) + +*** DTLS1.3 (RFC 9147) *** + +Flight 1: CLIENT => SERVER +SERVER when receiving Flight 1: TENTATIVE => CONFIRMED + +Flight 2: SERVER => CLIENT +CLIENT when receiving Flight 2: TENTATIVE => PENDING (dtls writable) + +Flight 3: CLIENT => SERVER +SERVER when receiving Flight 3: CONFIRMED => COMPLETE (dtls writable) + +CLIENT will switch to COMPLETE when one of +- Flight 3 is acked +- It receives decryptable data from SERVER (i.e. it learns that SERVER is dtls writable) diff --git a/g3doc/implementation_basics.md b/g3doc/implementation_basics.md index a1102a09799..6860638a94b 100644 --- a/g3doc/implementation_basics.md +++ b/g3doc/implementation_basics.md @@ -157,3 +157,20 @@ Before uploading changes to gerrit for review, make sure to complete the followi * Ensure that a *full build* of all binaries passes. This will save time and resources. * If at any of the above steps changes had to be made, go back the first step and repeat until no changes are required at every step. + +## Pointer Nullability Annotations +To ensure high-quality, machine-checkable C++ code in WebRTC, adhere to the following rules regarding pointer nullability. + +* **Core Principle**: Prevent accidental use of invalid null values by explicitly declaring the nullability contract of all raw and smart pointers. + +* **Classify Every Pointer**: Avoid leaving pointers in the "Unknown" (unannotated) state. Actively classify whether null is a valid semantic state for every pointer you write or modify. + +* **Annotate Nullable Exceptions (`absl_nullable`)**: When null is a valid and expected value at the point of use, you must explicitly annotate the pointer. + * Raw Pointers: Use the format `T* absl_nullable` (e.g., `Widget* absl_nullable widget`). + * Smart Pointers: Use the format `absl_nullable std::unique_ptr` (e.g., `absl_nullable std::unique_ptr widget`). + +* **Enforce Strict Requirements (`absl_nonnull`)**: When a pointer must fundamentally never be null, enforce this contract explicitly. + * Raw Pointers: Use the format `T* absl_nonnull` but for non-null raw pointers prefer to use a reference instead. + * Smart Pointers: Use the format `absl_nonnull std::unique_ptr`. + +* **Leverage Machine Checking**: Treat these annotations as strict contracts. Rely on them to surface potential null-dereference errors or contract violations during static analysis and compilation. \ No newline at end of file diff --git a/g3doc/plan_b_removal.md b/g3doc/plan_b_removal.md new file mode 100644 index 00000000000..6946a65b4bf --- /dev/null +++ b/g3doc/plan_b_removal.md @@ -0,0 +1,66 @@ + + + +# Plan B Deprecation and Removal + +This document describes the tooling and process for deprecating and eventually removing Plan B SDP semantics from the WebRTC codebase. + +## Build Configuration + +To support a gradual deprecation, a new GN argument and C++ macro have been introduced. + +### GN Argument: `deprecate_plan_b` + +Defined in `webrtc.gni`, this argument controls whether Plan B specific APIs are marked as deprecated at compile time. + +* **`false` (default):** Plan B APIs are available without warnings. +* **`true`:** Plan B APIs are annotated with `[[deprecated]]`. In WebRTC's default build configuration (which treats warnings as errors), this will cause compilation to fail if any Plan B APIs are used. + +To enable this in your local build, add the following to your `args.gn`: +```gn +deprecate_plan_b = true +``` + +### C++ Macro: `PLAN_B_ONLY` + +Defined in `rtc_base/system/plan_b_only.h`, this macro should be used to annotate functions, classes, or variables that are only needed for Plan B semantics. + +```cpp +#include "rtc_base/system/plan_b_only.h" + +PLAN_B_ONLY void MyPlanBSpecificFunction(); +``` + +- When `deprecate_plan_b` is `true`, `PLAN_B_ONLY` expands to `[[deprecated]]`. +- When `deprecate_plan_b` is `false`, `PLAN_B_ONLY` expands to an empty string. + +### Suppression Macros + +To allow existing Plan B code to compile when `deprecate_plan_b = true`, the following macros are provided in `rtc_base/system/plan_b_only.h`: + +- `RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN()` +- `RTC_ALLOW_PLAN_B_DEPRECATION_END()` + +These macros use compiler pragmas to locally disable deprecation warnings. + +## Usage Guidelines + +### Annotating New Plan B Code +Avoid adding new Plan B code. If it is absolutely necessary, ensure it is annotated with the `PLAN_B_ONLY` macro. + +### Identifying Plan B Dependencies +By setting `deprecate_plan_b = true`, developers can identify all call sites that still rely on Plan B. This is the primary method for auditing the progress of the migration to Unified Plan. + +### Suppressing Deprecation Warnings in Legacy Code +Existing Plan B call sites in "glue code" (e.g., `PeerConnection` delegation logic) should be wrapped with the suppression macros to allow the build to pass. + +```cpp +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); +sdp_handler_->RemoveStream(local_stream); +RTC_ALLOW_PLAN_B_DEPRECATION_END(); +``` + +This ensures that the build only fails on *new*, unwrapped Plan B usage, while clearly marking legacy code that requires migration. + +### Transitioning to Unified Plan +Functions that are compatible with both Plan B and Unified Plan should **not** be annotated. Only those that are functionally incorrect or redundant under Unified Plan (often guarded by `RTC_DCHECK(!IsUnifiedPlan())`) should be marked. diff --git a/g3doc/rtp_receive_flow.md b/g3doc/rtp_receive_flow.md new file mode 100644 index 00000000000..24ce1c9e0e3 --- /dev/null +++ b/g3doc/rtp_receive_flow.md @@ -0,0 +1,116 @@ +# WebRTC incoming RTP packet flow + +This document details the flow of incoming (receive) RTP packets across the WebRTC codebase, tracing the path from the network socket to the video receive stream. + +## Threading Model +* **Network Thread**: The majority of the initial receive and demuxing flow occurs synchronously on the network thread to minimize delays. +* **Worker Thread**: Packet processing, dispatching encoded frames/buffers to jitter buffers. Actual decoding tends to happen on different threads however. + +--- + +## Call Stack and Component Flow + +### 1. Network / Socket Layer 🌐 *(Network Thread)* +Packets arrive from the network via UDP or TCP sockets. Platform-specific socket implementations trigger an event when bytes are ready to be read. +* **`AsyncUDPSocket` / `PhysicalSocket`** + * `OnReadEvent` + +### 2. Transport Routing 🛤️ *(Network Thread)* +The bytes move up through the ICE/STUN/TURN connection channels and DTLS layer. +* **`P2PTransportChannel`** -> `OnReadPacket` + * Routes via **`Connection::OnReadPacket`** +* **`DtlsTransport`** -> `OnReadPacket` + * Decrypts SRTP packets into cleartext bytes if necessary. + +### 3. RTP Transport & Parsing 📦 *(Network Thread)* +The raw bytes reach the RTP core structures where they are parsed into proper C++ WebRTC objects. +* **`RtpTransport::OnRtpPacketReceived(const ReceivedIpPacket&)`** + * Converts the byte buffer string into a `RtpPacketReceived`. + * Extracts header extensions based on the active `RtpHeaderExtensionMap`. +* **`RtpTransport::DemuxPacket`** + * Passes the parsed object down to the demuxer tree. + +### 4. RTP Demuxer (Level 1) 🔀 *(Network Thread)* +* **`RtpDemuxer::OnRtpPacket(const RtpPacketReceived&)`** + * **Demuxing Happens Here**: This demuxer maps the packet using rules like **MID**, **RSID**, or **SSRC**. + * Routes the packet to the matched `RtpPacketSinkInterface` (usually a `BaseChannel`). + +### 5. BaseChannel & Media Layer 📺 *(Network Thread)* +* **`BaseChannel::OnRtpPacket(const RtpPacketReceived&)`** + * Implements `RtpPacketSinkInterface`. Serves as the base conduit for specific Media Channels (audio or video). +* **`WebRtcVideoReceiveChannel::OnPacketReceived(RtpPacketReceived)`** + * Delegates the received packet directly up to the `Call` interface: `call_->Receiver()->DeliverRtpPacket(...)` + +### 6. Call Interface & Demuxer (Level 2) 📞 *(Network Thread -> Worker Thread)* +* **`Call::DeliverRtpPacket`** + * Evaluates `receive_time_calculator_` directly on the Network Thread to avoid jitter/delay. + * **Thread Hop**: Dispatches a task to the **Worker Thread** and invokes `Call::DeliverRtpPacket_w`. +* **`Call::DeliverRtpPacket_w`** + * Executes on the Worker Thread. + * Employs a secondary, internal demuxer logic to find the specific receive stream that matches the SSRC. + * Calls the appropriate video or audio receive stream. + +### 7. Stream Receiver 📥 *(Worker Thread)* +* **`RtpVideoStreamReceiver2::OnRtpPacket(const RtpPacketReceived&)`** + * Executes on the Worker Thread. + * Extracts metadata, processes NACKs/RTCP feedback based on the incoming sequence numbers. + * Inserts the payload into the `PacketBuffer` (Jitter Buffer) to wait for frame completion. + * Once a complete video frame is assembled (or for deeper stream operations), the `VideoFrame` is decoded by `VCMGenericDecoder`. + +--- + +## RTP Receive Packet Flow Diagram + +```text + [Network] + | + v ++-----------------------+ +| AsyncUDPSocket / | (Network Thread) +| PhysicalSocket | ++-----------------------+ + | + v ++-----------------------+ +| P2PTransportChannel | (Network Thread) +| / DtlsTransport | ++-----------------------+ + | + v ++-----------------------+ +| RtpTransport | (Network Thread) ++-----------------------+ + | + v ++-----------------------+ +| RtpDemuxer | (Network Thread) ++-----------------------+ + | + v ++-----------------------+ +| BaseChannel | (Network Thread) +| & MediaReceiveChannel| ++-----------------------+ + | + v ++-----------------------+ +| Call::DeliverRtpPacket| (Network Thread) ++-----------------------+ + | + | Thread Hop + v ++-----------------------+ +| Call:: | (Worker Thread) +| DeliverRtpPacket_w | ++-----------------------+ + | + v ++-----------------------+ +| Stream Receiver | (Worker Thread) +| (e.g., RtpVideo- | +| StreamReceiver2) | ++-----------------------+ + | + v + [Decoder] +``` diff --git a/g3doc/style-guide.md b/g3doc/style-guide.md index 1fb03bcc66e..b44b4168920 100644 --- a/g3doc/style-guide.md +++ b/g3doc/style-guide.md @@ -9,6 +9,10 @@ Some older parts of the code violate the style guide in various ways. If making large changes to such code, consider first cleaning it up in a separate CL. +WebRTC's policy about AI coding matches [Chromium's policy][ai-policy]. + +[ai-policy]: https://chromium.googlesource.com/chromium/src.git/+/HEAD/agents/ai_policy.md + ## C++ WebRTC follows the [Chromium C++ style guide][chr-style] and the @@ -126,9 +130,11 @@ call it using the `DEPRECATED_`-prefixed name don't get the warning. [DEPRECATED]: https://en.cppreference.com/w/cpp/language/attributes/deprecated [ABSL_DEPRECATE_AND_INLINE]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/abseil-cpp/absl/base/macros.h?q=ABSL_DEPRECATE_AND_INLINE -### ArrayView +### std::span + +std::span is allowed and encouraged in WebRTC. -When passing an array of values to a function, use `ArrayView` +When passing an array of values to a function, use `std::span` whenever possible—that is, whenever you're not passing ownership of the array, and don't allow the callee to change the array size. @@ -136,14 +142,20 @@ For example, | instead of | use | |-------------------------------------|----------------------| -| `const std::vector&` | `ArrayView` | -| `const T* ptr, size_t num_elements` | `ArrayView` | -| `T* ptr, size_t num_elements` | `ArrayView` | +| `const std::vector&` | `std::span` | +| `const T* ptr, size_t num_elements` | `std::span` | +| `T* ptr, size_t num_elements` | `std::span` | + +See the [cpp reference][span] for more detailed docs. + +std::span represents the same concept as base::span in Chromium and absl::Span +in Abseil. WebRTC can't use base::span from Chromium, and prefers std::span over absl::Span. -See the [source code for `ArrayView`][ArrayView] for more detailed -docs. + +In the past WebRTC used own ArrayView type to represent a span, however that +type has been migrated to std::span. -[ArrayView]: https://webrtc.googlesource.com/src/+/refs/heads/main/api/array_view.h +[span]: https://en.cppreference.com/w/cpp/container/span.html ### Strings diff --git a/g3doc/todo/byte_order_safety.md b/g3doc/todo/byte_order_safety.md new file mode 100644 index 00000000000..ee774f8beba --- /dev/null +++ b/g3doc/todo/byte_order_safety.md @@ -0,0 +1,35 @@ +# Objective +Migrate the `rtc_base/byte_order.h` API from unsafe `void*` pointers to `webrtc::ArrayView` to improve type safety and bounds checking. This migration follows a multi-phase approach to ensure backward compatibility for external consumers, provide clear deprecation warnings, and eventually remove the unsafe API. + +# Key Files & Context +- `rtc_base/byte_order.h`: The target API for conversion. +- `api/array_view.h`: The replacement type for buffer spans. +- 19 identified files with internal usages of the legacy API. + +# Implementation Steps + +## Phase 0: Documentation +- Create `g3doc/todo/byte_order_safety.md` and store this migration plan there for tracking. + +## Phase 1: Introduce New API +- Modify `rtc_base/byte_order.h` to include `#include "api/array_view.h"`. +- Implement `ArrayView` overloads for all `Get/Set` functions (`Get8`, `Set8`, `GetBE16`, etc.). +- Ensure new implementations include `RTC_DCHECK_GE(data.size(), ...)` for safety. +- **Note**: Do not add deprecation attributes yet. + +## Phase 2: Migrate Internal Usages +- Replace all internal usages of the legacy `void*` API in the WebRTC codebase with the new `ArrayView` API. +- This ensures that WebRTC itself does not trigger deprecation warnings/errors once the attributes are added. + +## Phase 3: Deprecation and External Notice +- Add `ABSL_DEPRECATE_AND_INLINE()` to the legacy `void*` overloads in `rtc_base/byte_order.h`. +- **External Action (Human)**: Send a deprecation notice to relevant mailing lists (e.g., `discuss-webrtc`) informing external consumers of the new `ArrayView` API and the impending removal of the `void*` API. +- Allow for a transition period for external projects to update their code. + +## Phase 4: Final Deletion +- After the transition period, remove the deprecated `void*` functions and the associated deprecation attributes, leaving only the `ArrayView` API. + +# Verification & Testing +- **Compilation**: Perform a full build (`autoninja -C out/Default`) to ensure no internal usages of deprecated functions remain. +- **Unit Tests**: Run `out/Default/rtc_unittests --gtest_filter=ByteOrderTest.*` and verify all tests pass with the new API. +- **Presubmit**: Run `git cl format` and `git cl presubmit -u --force`. diff --git a/g3doc/todo/payload_type_redesign.md b/g3doc/todo/payload_type_redesign.md new file mode 100644 index 00000000000..5fa5f12bcae --- /dev/null +++ b/g3doc/todo/payload_type_redesign.md @@ -0,0 +1,127 @@ +# Payload type allocation redesign + +## Background: What a payload type is + +The payload type is a property of a codec that is established between a sender +and a receiver using SDP offer/answer. + +Each end of the connection independently indicates the list of codecs it is +willing to send and/or receive, and assigns a payload type to each. It is +conventional but not required to use the same payload types in an answer as +those suggested in an offer. + +For SDP sendonly media sections, it indicates a willingness to send; for +recvonly media sections, it indicates a willingness to receive; for sendrecv +media sections, it indicates a willingness to receive AND a willingness to send. +Presence in a media section does not guarantee that it will be used. (RFC 3264) + +## Current allocation strategy + +The payload types are assigned by the VideoEngine and VoiceEngine according to a +fixed list. If the assignments collide with already established payload types, +the assignments are munged in the CodecVendor class so that there are no +colliding payload types. + +The PayloadTypeRecorder class records what payload types are assigned - there is +one case (PR-answer followed by a later Answer that has different PTs) where +they are changed, but otherwise, once the PayloadTypeRecorder has assigned them, +that PT is permanent for that transport and that direction. + +Picking a payload type is done in the PayloadTypePicker class. This is preloaded +with some assignments, so that if a PT is free, the commonly used PT for that +codec can be used. + +## Desired future strategy + +There should be no assignment of payload types in VideoEngine and VoiceEngine. +Payload types should be picked only when creating an offer or answer; they are +assigned permanently in SetLocalDescription / SetRemoteDescription. + +Once an assignment is made, it should never need to change (apart from the case +noted above). + +## Dealing with resiliency mechanisms + +Resiliency mechanisms (RED, RTX) are signaled using "codecs", and are +traditionally represented as such within the library. They associate with the +codec they are resiliency for by using the "apt=" parameter in their a=fmtp +lines. However, this complicates assignment, since that parameter cannot be +filled in until the payload type for the "protected" codec is assigned. + +## Backwards compatibility issues + +Experience has shown that changing the PT allocation strategy often trips up +applications that have depended on the result of the old allocation strategy, so +user-visible changes should be avoided. One example is that it's preferred (but +not strictly required) to have the RTX PT assigned the PT number of 1 more than +the PT of the codec it refers to. + +## Current implementation status + +The new strategy is implemented for audio codecs. Several issues that caused +test failures when enabling the `WebRTC-PayloadTypesInTransport` field trial +have been identified and fixed: + +- **Audio/Video RED Collision:** RED codecs of different media types were + incorrectly matching, leading to payload type conflicts. + `MatchesWithCodecRules` now enforces media type equality. +- **MID Recycling:** When a MID was recycled for a different media type (e.g., + Audio -> Video), `CodecVendor` was incorrectly merging codecs from the old + description. This has been fixed by validating the media type before merging. +- **RED Matching Logic:** Relaxed the matching rules for RED to allow + negotiation to proceed even when parameters (linking RED to primary codecs) + are not yet populated, as this linking now happens late in the `CodecVendor`. + +The new strategy is not yet implemented for video codecs. + +## Implementation Strategy for Video + +The goal is to transition video codec handling to the same late-assignment model +used for audio. + +### 1. Unified Codec Collection + +- Implement `VideoCodecsFromFactory` in `pc/typed_codec_vendor.cc`. This will + query the `VideoEncoderFactory` and `VideoDecoderFactory` to gather supported + `SdpVideoFormat`s. +- These codecs will be initialized with `kIdNotSet`, allowing + `SdpPayloadTypeSuggester` to assign payload types only during the creation of + an offer or answer. + +### 2. Payload Type Suggester Integration + +- Ensure `PayloadTypePicker` has a complete list of preferred payload types for + video codecs (VP8, VP9, H.264, AV1, etc.) to maintain stable and conventional + assignments. +- Update `CodecVendor` to use the unassigned video codec list when the + `WebRTC-PayloadTypesInTransport` trial is active. + +### 3. Resiliency and Parameter Linking + +- Refine `MergeCodecs` and `AssignCodecIdsAndLinkRed` to handle video-specific + resiliency: + - **RTX:** Correctly link RTX codecs to their primary video codecs by matching + names and specific parameters (like `profile-level-id` for H.264). + - **RED/ULPFEC:** Ensure parameters for video redundancy are correctly + populated once the primary codec PTs are assigned. + +### 4. Verification and Testing + +- **Integration Tests:** Enable the `WebRTC-PayloadTypesInTransport` trial in + `peerconnection_unittests` and `rtc_unittests` to identify any video-specific + regressions. +- **Stable PT Tests:** Add coverage to ensure that payload types remain stable + across renegotiations, even when the order of codecs in the transceiver + preferences changes. +- **MID Recycling:** Verify that MID recycling for video-to-audio and vice-versa + works correctly without PT collisions or crashes. + +## Desired next steps + +Analyze the current situation. (Done) + +### Test, isolate and fix failures + +The identified failures in `PeerConnectionEncodingsIntegrationTest` and +`PeerConnectionIntegrationTest` have been resolved. Next, focus on implementing +the video strategy outlined above. diff --git a/g3doc/todo/plan_b_senders_receivers.md b/g3doc/todo/plan_b_senders_receivers.md new file mode 100644 index 00000000000..dcae1de7ea1 --- /dev/null +++ b/g3doc/todo/plan_b_senders_receivers.md @@ -0,0 +1,31 @@ + +# Plan: Restrict RtpTransceiver sender/receiver access to PLAN_B_ONLY + +## Objective +Restrict access to `RtpTransceiver::senders_` and `RtpTransceiver::receivers_` such that access to all elements except the first one is only possible via functions marked `PLAN_B_ONLY`. This aligns the implementation with Unified Plan requirements where only one sender and one receiver are used, while preserving legacy Plan B functionality. + +## Proposed Changes + +### 1. Header Refactoring (`pc/rtp_transceiver.h`) +* **Deprecate Vector Accessors**: Mark `senders()` and `receivers()` with the `PLAN_B_ONLY` macro. This triggers deprecation warnings for any code accessing the full lists, making Plan B dependencies explicit. +* **Add Unified Plan Accessors**: + * Add `internal_first_sender()` and `internal_first_receiver()` private helpers. + * These helpers will return the first element (or `nullptr`) and will be used by Unified Plan paths. + +### 2. Implementation Update (`pc/rtp_transceiver.cc`) +* **Shared Logic Branching**: Methods that currently iterate over `senders_` or `receivers_` (e.g., `SetMediaChannels`, `GetStopSendingAndReceiving`, `GetDeleteChannelWorkerTask`, `OnNegotiationUpdate`) must be updated: + * **Unified Plan**: Use `internal_first_...` helpers to process only the primary element. + * **Plan B**: Wrap iterations in `RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN` and `RTC_ALLOW_PLAN_B_DEPRECATION_END`. +* **Protect Index Access**: Ensure `senders_[0]` or `receivers_[0]` are only used in Unified Plan paths (guarded by `RTC_DCHECK(unified_plan_)`). + +### 3. External Caller Migration +* Update components that iterate over senders/receivers: + * `pc/legacy_stats_collector.cc` + * `pc/peer_connection.cc` + * `pc/rtc_stats_collector.cc` + * `pc/rtp_transmission_manager.cc` +* Wrap these iterations in `RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN/END` to acknowledge the legacy dependency. + +## Verification Plan +* **Compilation**: Ensure all targets compile with and without `WEBRTC_DEPRECATE_PLAN_B` defined. +* **Unit Tests**: Run `pc_unittests` and `peerconnection_unittests` to ensure no regression in both Unified Plan and Plan B modes. diff --git a/infra/config/builders.star b/infra/config/builders.star index 8eb0a39d2af..8816ef06fa9 100644 --- a/infra/config/builders.star +++ b/infra/config/builders.star @@ -20,8 +20,6 @@ WEBRTC_GIT = "https://webrtc.googlesource.com/src" # useful when a failure can be safely ignored while fixing it without # blocking the LKGR finder on it. skipped_lkgr_bots = [ - # TODO: https://issues.webrtc.org/460264453 - Re-enable when reliable - "iOS Debug (simulator)", ] lkgr_builders = [] @@ -342,8 +340,7 @@ ios_builder("iOS64 Release", "iOS|arm64|rel") ios_try_job("ios_compile_arm64_rel") ios_builder("iOS Debug (simulator)", "iOS|x64|sim") -# TODO: https://issues.webrtc.org/460264453 - Re-enable when reliable -ios_try_job("ios_dbg_simulator", cq = None) +ios_try_job("ios_dbg_simulator") ios_builder("iOS API Framework Builder", "iOS|fat|size", recipe = "ios_api_framework", prioritized = True) ios_try_job("ios_api_framework", recipe = "ios_api_framework") @@ -397,9 +394,7 @@ try_builder("mac_compile_dbg") ci_builder("Mac64 Release", "Mac|x64|rel") try_builder("mac_rel") try_builder("mac_compile_rel", cq = None) -ci_builder("Mac64 Builder", ci_cat = None, perf_cat = "Mac|x64|Builder|") ci_builder("MacArm64 Builder", ci_cat = None, perf_cat = "Mac|arm64|Builder|") -perf_builder("Perf Mac 11", "Mac|x64|Tester|11", triggered_by = ["Mac64 Builder"]) perf_builder("Perf Mac M1 Arm64 12", "Mac|arm64|Tester|12", triggered_by = ["MacArm64 Builder"]) ci_builder("Mac Asan", "Mac|x64|asan") try_builder("mac_asan") diff --git a/infra/config/config.star b/infra/config/config.star index 592e2d4521f..49718cf05f8 100755 --- a/infra/config/config.star +++ b/infra/config/config.star @@ -18,6 +18,13 @@ load("@chromium-luci//recipe_experiments.star", "register_recipe_experiments") WEBRTC_GIT = "https://webrtc.googlesource.com/src" WEBRTC_GERRIT = "https://webrtc-review.googlesource.com/src" WEBRTC_TROOPER_EMAIL = "webrtc-troopers-robots@google.com" +TREE_CLOSING_STEPS_REGEXP = [ + "bot_update", + "compile", + "gclient runhooks", + "generate_build_files", + "update", +] # Use LUCI Scheduler BBv2 names and add Scheduler realms configs. lucicfg.enable_experiment("crbug.com/1182002") @@ -301,6 +308,7 @@ luci.bucket( "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com", ]), acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + "project-webrtc-ci-schedulers", # Allow Pinpoint to trigger builds for bisection "service-account-chromeperf", ]), @@ -313,6 +321,11 @@ luci.bucket( luci.bucket( name = "cron", + acls = [ + acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + "project-webrtc-ci-schedulers", + ]), + ], ) # Commit queue definitions: @@ -423,22 +436,7 @@ luci.buildbucket_notification_topic( luci.tree_closer( name = "webrtc_tree_closer", tree_status_host = "webrtc-status.appspot.com", - # TODO: These step filters are copied verbatim from Gatekeeper, for testing - # that LUCI-Notify would take the exact same actions. Once we've switched - # over, this should be updated - several of these steps don't exist in - # WebRTC recipes. - failed_step_regexp = [ - "bot_update", - "compile", - "gclient runhooks", - "runhooks", - "update", - "extract build", - "cleanup_temp", - "taskkill", - "compile", - "gn", - ], + failed_step_regexp = TREE_CLOSING_STEPS_REGEXP, failed_step_regexp_exclude = ".*\\(experimental\\).*", ) diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg index 7834a2f9f69..b7c3a0c9600 100644 --- a/infra/config/generated/luci/commit-queue.cfg +++ b/infra/config/generated/luci/commit-queue.cfg @@ -90,6 +90,9 @@ config_groups { builders { name: "webrtc/try/ios_compile_arm64_rel" } + builders { + name: "webrtc/try/ios_dbg_simulator" + } builders { name: "webrtc/try/iwyu_verifier" } @@ -267,6 +270,9 @@ config_groups { builders { name: "webrtc/try/ios_compile_arm64_rel" } + builders { + name: "webrtc/try/ios_dbg_simulator" + } builders { name: "webrtc/try/iwyu_verifier" } diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index fb756f5ddb5..43bf68c18b5 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg @@ -1185,7 +1185,7 @@ buckets { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" cipd_version: "refs/heads/main" properties_j: "$recipe_engine/resultdb/test_presentation:{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]}" - properties_j: "config:{\"allowed_gap\":150,\"allowed_lag\":9,\"buckets\":{\"chromium/webrtc.fyi\":{\"builders\":[\"WebRTC Chromium FYI Android Builder (dbg)\",\"WebRTC Chromium FYI Android Builder\",\"WebRTC Chromium FYI Android Tester\",\"WebRTC Chromium FYI Linux Builder (dbg)\",\"WebRTC Chromium FYI Linux Builder\",\"WebRTC Chromium FYI Linux Tester\",\"WebRTC Chromium FYI Mac Builder (dbg)\",\"WebRTC Chromium FYI Mac Builder\",\"WebRTC Chromium FYI Mac Tester\",\"WebRTC Chromium FYI Win Builder (dbg)\",\"WebRTC Chromium FYI Win Builder\",\"WebRTC Chromium FYI Win Tester\"]},\"webrtc/ci\":{\"builders\":[\"Android32\",\"Android32 (dbg)\",\"Android32 (more configs)\",\"Android32 Builder x86\",\"Android32 Builder x86 (dbg)\",\"Android64\",\"Android64 Builder x64 (dbg)\",\"Fuchsia Release\",\"Linux (more configs)\",\"Linux Asan\",\"Linux MSan\",\"Linux Tsan v2\",\"Linux UBSan\",\"Linux UBSan vptr\",\"Linux32 Debug\",\"Linux32 Debug (ARM)\",\"Linux32 Release\",\"Linux32 Release (ARM)\",\"Linux64 Debug\",\"Linux64 Debug (ARM)\",\"Linux64 Release\",\"Linux64 Release (ARM)\",\"Linux64 Release (Libfuzzer)\",\"Mac Asan\",\"Mac64 Debug\",\"Mac64 Release\",\"MacARM64 M1 Release\",\"Win (more configs)\",\"Win32 Debug (Clang)\",\"Win32 Release (Clang)\",\"Win64 ASan\",\"Win64 Debug (Clang)\",\"Win64 Release (Clang)\",\"iOS API Framework Builder\",\"iOS64 Debug\",\"iOS64 Release\"]}},\"project\":\"webrtc\",\"source_url\":\"https://webrtc.googlesource.com/src\",\"status_url\":\"https://webrtc-status.appspot.com\"}" + properties_j: "config:{\"allowed_gap\":150,\"allowed_lag\":9,\"buckets\":{\"chromium/webrtc.fyi\":{\"builders\":[\"WebRTC Chromium FYI Android Builder (dbg)\",\"WebRTC Chromium FYI Android Builder\",\"WebRTC Chromium FYI Android Tester\",\"WebRTC Chromium FYI Linux Builder (dbg)\",\"WebRTC Chromium FYI Linux Builder\",\"WebRTC Chromium FYI Linux Tester\",\"WebRTC Chromium FYI Mac Builder (dbg)\",\"WebRTC Chromium FYI Mac Builder\",\"WebRTC Chromium FYI Mac Tester\",\"WebRTC Chromium FYI Win Builder (dbg)\",\"WebRTC Chromium FYI Win Builder\",\"WebRTC Chromium FYI Win Tester\"]},\"webrtc/ci\":{\"builders\":[\"Android32\",\"Android32 (dbg)\",\"Android32 (more configs)\",\"Android32 Builder x86\",\"Android32 Builder x86 (dbg)\",\"Android64\",\"Android64 Builder x64 (dbg)\",\"Fuchsia Release\",\"Linux (more configs)\",\"Linux Asan\",\"Linux MSan\",\"Linux Tsan v2\",\"Linux UBSan\",\"Linux UBSan vptr\",\"Linux32 Debug\",\"Linux32 Debug (ARM)\",\"Linux32 Release\",\"Linux32 Release (ARM)\",\"Linux64 Debug\",\"Linux64 Debug (ARM)\",\"Linux64 Release\",\"Linux64 Release (ARM)\",\"Linux64 Release (Libfuzzer)\",\"Mac Asan\",\"Mac64 Debug\",\"Mac64 Release\",\"MacARM64 M1 Release\",\"Win (more configs)\",\"Win32 Debug (Clang)\",\"Win32 Release (Clang)\",\"Win64 ASan\",\"Win64 Debug (Clang)\",\"Win64 Release (Clang)\",\"iOS API Framework Builder\",\"iOS Debug (simulator)\",\"iOS64 Debug\",\"iOS64 Release\"]}},\"project\":\"webrtc\",\"source_url\":\"https://webrtc.googlesource.com/src\",\"status_url\":\"https://webrtc-status.appspot.com\"}" properties_j: "lkgr_status_gs_path:\"chromium-webrtc/lkgr-status\"" properties_j: "project:\"webrtc\"" properties_j: "ref:\"refs/heads/lkgr\"" @@ -1358,36 +1358,6 @@ buckets { } } } - builders { - name: "Mac64 Builder" - swarming_host: "chromium-swarm.appspot.com" - swarming_tags: "vpython:native-python-wrapper" - dimensions: "builderless:1" - dimensions: "cpu:x86-64" - dimensions: "os:Mac" - dimensions: "pool:luci.webrtc.ci" - recipe { - name: "webrtc/standalone" - cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" - cipd_version: "refs/heads/main" - properties_j: "$build/siso:{\"configs\":[\"builder\"],\"enable_cloud_profiler\":true,\"enable_cloud_trace\":true,\"enable_monitoring\":true,\"project\":\"rbe-webrtc-trusted\"}" - properties_j: "$recipe_engine/resultdb/test_presentation:{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]}" - properties_j: "builder_group:\"client.webrtc\"" - } - priority: 30 - execution_timeout_secs: 7200 - build_numbers: YES - service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - resultdb { - enable: true - bq_exports { - project: "webrtc-ci" - dataset: "resultdb" - table: "perf_test_results" - test_results {} - } - } - } builders { name: "MacArm64 Builder" swarming_host: "chromium-swarm.appspot.com" @@ -1530,34 +1500,6 @@ buckets { } } } - builders { - name: "Perf Mac 11" - swarming_host: "chromium-swarm.appspot.com" - swarming_tags: "vpython:native-python-wrapper" - dimensions: "os:Linux" - dimensions: "pool:luci.webrtc.perf" - recipe { - name: "webrtc/standalone" - cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" - cipd_version: "refs/heads/main" - properties_j: "$build/siso:{\"configs\":[\"builder\"],\"enable_cloud_profiler\":true,\"enable_cloud_trace\":true,\"enable_monitoring\":true,\"project\":\"rbe-webrtc-trusted\"}" - properties_j: "$recipe_engine/resultdb/test_presentation:{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]}" - properties_j: "builder_group:\"client.webrtc.perf\"" - } - priority: 30 - execution_timeout_secs: 10800 - build_numbers: YES - service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - resultdb { - enable: true - bq_exports { - project: "webrtc-ci" - dataset: "resultdb" - table: "perf_test_results" - test_results {} - } - } - } builders { name: "Perf Mac M1 Arm64 12" swarming_host: "chromium-swarm.appspot.com" diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index 4dff326fdfa..a2311a61f8d 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg @@ -313,19 +313,10 @@ consoles { name: "buildbucket/luci.webrtc.perf/Perf Fuchsia" category: "Fuchsia|x64|Tester" } - builders { - name: "buildbucket/luci.webrtc.perf/Mac64 Builder" - category: "Mac|x64|Builder" - } builders { name: "buildbucket/luci.webrtc.perf/MacArm64 Builder" category: "Mac|arm64|Builder" } - builders { - name: "buildbucket/luci.webrtc.perf/Perf Mac 11" - category: "Mac|x64|Tester" - short_name: "11" - } builders { name: "buildbucket/luci.webrtc.perf/Perf Mac M1 Arm64 12" category: "Mac|arm64|Tester" diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg index 3cc8bc05bf0..309b46196df 100644 --- a/infra/config/generated/luci/luci-notify.cfg +++ b/infra/config/generated/luci/luci-notify.cfg @@ -27,7 +27,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -54,7 +54,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -81,7 +81,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -108,7 +108,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -135,7 +135,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -162,7 +162,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -189,7 +189,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -216,7 +216,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -243,7 +243,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -270,7 +270,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -297,7 +297,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -324,7 +324,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -351,7 +351,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -378,7 +378,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -405,7 +405,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -432,7 +432,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -459,7 +459,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -486,7 +486,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -513,7 +513,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -540,7 +540,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -567,7 +567,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -594,7 +594,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -621,7 +621,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -648,7 +648,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -675,7 +675,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -702,7 +702,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -729,7 +729,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -756,7 +756,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -783,7 +783,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -810,7 +810,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -837,7 +837,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -864,7 +864,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -891,7 +891,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -918,7 +918,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -943,6 +943,11 @@ notifiers { name: "iOS Debug (simulator)" repository: "https://webrtc.googlesource.com/src" } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } } notifiers { notifications { @@ -967,7 +972,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -994,7 +999,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -1063,7 +1068,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -1090,7 +1095,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -1117,7 +1122,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -1144,34 +1149,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" - failed_step_regexp_exclude: ".*\\(experimental\\).*" - } -} -notifiers { - notifications { - on_new_status: INFRA_FAILURE - email { - recipients: "webrtc-troopers-robots@google.com" - } - template: "infra_failure" - } - notifications { - on_new_status: FAILURE - email { - recipients: "webrtc-troopers-robots@google.com" - } - template: "build_failure" - notify_blamelist {} - } - builders { - bucket: "perf" - name: "Mac64 Builder" - repository: "https://webrtc.googlesource.com/src" - } - tree_closers { - tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -1198,7 +1176,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } @@ -1290,28 +1268,6 @@ notifiers { repository: "https://webrtc.googlesource.com/src" } } -notifiers { - notifications { - on_new_status: INFRA_FAILURE - email { - recipients: "webrtc-troopers-robots@google.com" - } - template: "infra_failure" - } - notifications { - on_new_status: FAILURE - email { - recipients: "webrtc-troopers-robots@google.com" - } - template: "build_failure" - notify_blamelist {} - } - builders { - bucket: "perf" - name: "Perf Mac 11" - repository: "https://webrtc.googlesource.com/src" - } -} notifiers { notifications { on_new_status: INFRA_FAILURE @@ -1379,7 +1335,7 @@ notifiers { } tree_closers { tree_status_host: "webrtc-status.appspot.com" - failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp: "bot_update|compile|gclient runhooks|generate_build_files|update" failed_step_regexp_exclude: ".*\\(experimental\\).*" } } diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index 6787a55f0d2..dee7bc52fa0 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg @@ -286,15 +286,6 @@ job { builder: "Mac Asan" } } -job { - id: "Mac64 Builder" - realm: "perf" - buildbucket { - server: "cr-buildbucket.appspot.com" - bucket: "perf" - builder: "Mac64 Builder" - } -} job { id: "Mac64 Debug" realm: "ci" @@ -383,19 +374,6 @@ job { builder: "Perf Linux" } } -job { - id: "Perf Mac 11" - realm: "perf" - triggering_policy { - kind: LOGARITHMIC_BATCHING - log_base: 1.7 - } - buildbucket { - server: "cr-buildbucket.appspot.com" - bucket: "perf" - builder: "Perf Mac 11" - } -} job { id: "Perf Mac M1 Arm64 12" realm: "perf" @@ -590,7 +568,6 @@ trigger { triggers: "Android64 Builder arm64" triggers: "Fuchsia Builder" triggers: "Linux64 Builder" - triggers: "Mac64 Builder" triggers: "MacArm64 Builder" triggers: "Win64 Builder (Clang)" gitiles { diff --git a/infra/config/generated/luci/project.cfg b/infra/config/generated/luci/project.cfg index 6ec459c9741..aee5e5ca689 100644 --- a/infra/config/generated/luci/project.cfg +++ b/infra/config/generated/luci/project.cfg @@ -7,7 +7,7 @@ name: "webrtc" access: "group:all" lucicfg { - version: "1.46.1" + version: "1.46.2" package_name: "@webrtc-project" package_dir: "../.." config_dir: "generated/luci" diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg index ebd33bd4ed4..7540cb13120 100644 --- a/infra/config/generated/luci/realms.cfg +++ b/infra/config/generated/luci/realms.cfg @@ -82,6 +82,10 @@ realms { principals: "user:chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" principals: "user:webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com" } + bindings { + role: "role/buildbucket.triggerer" + principals: "group:project-webrtc-ci-schedulers" + } } realms { name: "debug-bot-acls" @@ -111,6 +115,7 @@ realms { } bindings { role: "role/buildbucket.triggerer" + principals: "group:project-webrtc-ci-schedulers" principals: "group:service-account-chromeperf" principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" } @@ -128,7 +133,6 @@ realms { values: "Perf Android64 (R Pixel5)" values: "Perf Fuchsia" values: "Perf Linux" - values: "Perf Mac 11" values: "Perf Mac M1 Arm64 12" values: "Perf Win 10" } diff --git a/infra/specs/client.webrtc.json b/infra/specs/client.webrtc.json index ed5e51be7c1..a5e80532b82 100644 --- a/infra/specs/client.webrtc.json +++ b/infra/specs/client.webrtc.json @@ -7,6 +7,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -27,6 +28,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -47,6 +49,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -67,6 +70,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -87,6 +112,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -107,6 +133,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -127,6 +154,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -148,6 +176,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -169,6 +198,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -190,6 +220,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -210,6 +241,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -230,6 +262,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -250,6 +283,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -270,6 +304,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -291,6 +326,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -311,6 +347,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -332,6 +369,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -352,6 +390,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -372,6 +411,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -392,6 +432,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -413,6 +454,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -433,6 +475,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -453,6 +496,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -478,6 +522,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -498,6 +543,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -522,6 +568,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -542,6 +589,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -562,6 +610,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -582,6 +631,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -602,6 +673,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -622,6 +694,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -642,6 +715,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -663,6 +737,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -684,6 +759,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -705,6 +781,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -725,6 +802,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -745,6 +823,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -765,6 +844,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -785,6 +865,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -806,6 +887,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -826,6 +908,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -847,6 +930,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -867,6 +951,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -887,6 +972,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -907,6 +993,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -928,6 +1015,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -948,6 +1036,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -968,6 +1057,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -993,6 +1083,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -1013,6 +1104,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -1037,6 +1129,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -1065,6 +1158,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -1085,6 +1179,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -1105,6 +1200,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -1125,6 +1221,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -1145,6 +1263,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -1165,6 +1284,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -1185,6 +1305,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -1206,6 +1327,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -1227,6 +1349,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -1248,6 +1371,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -1268,6 +1392,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -1288,6 +1413,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -1308,6 +1434,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -1328,6 +1455,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -1349,6 +1477,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -1369,6 +1498,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -1390,6 +1520,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -1410,6 +1541,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -1430,6 +1562,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -1450,6 +1583,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -1471,6 +1605,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -1491,6 +1626,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -1511,6 +1647,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -1536,6 +1673,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -1556,6 +1694,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -1580,6 +1719,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -1600,6 +1740,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -1620,6 +1761,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -1640,6 +1782,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -1660,6 +1824,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -1680,6 +1845,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -1700,6 +1866,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -1721,6 +1888,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -1742,6 +1910,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -1763,6 +1932,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -1783,6 +1953,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -1803,6 +1974,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -1823,6 +1995,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -1843,6 +2016,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -1864,6 +2038,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -1884,6 +2059,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -1905,6 +2081,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -1925,6 +2102,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -1945,6 +2123,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -1965,6 +2144,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -1986,6 +2166,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -2006,6 +2187,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -2026,6 +2208,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -2051,6 +2234,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -2071,6 +2255,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -2104,6 +2289,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2128,6 +2314,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2152,6 +2339,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2176,6 +2364,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2200,6 +2389,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2224,6 +2414,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2248,6 +2439,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2272,6 +2464,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2297,6 +2490,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2321,6 +2515,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2346,6 +2541,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2370,6 +2566,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2392,6 +2589,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -2414,6 +2612,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -2431,6 +2630,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -2448,6 +2666,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -2465,6 +2684,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -2482,6 +2702,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -2500,6 +2721,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -2518,6 +2740,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -2536,6 +2759,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -2553,6 +2777,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -2570,6 +2795,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -2587,6 +2813,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -2604,6 +2831,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -2622,6 +2850,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -2639,6 +2868,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -2656,6 +2886,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -2674,6 +2905,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -2691,6 +2923,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -2709,6 +2942,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -2726,6 +2960,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -2744,6 +2979,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -2761,6 +2997,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -2778,6 +3015,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -2799,6 +3037,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -2816,6 +3055,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -2833,6 +3091,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -2850,6 +3109,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -2867,6 +3127,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -2885,6 +3146,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -2903,6 +3165,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -2921,6 +3184,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -2938,6 +3202,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -2955,6 +3220,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -2972,6 +3238,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -2989,6 +3256,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -3007,6 +3275,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -3024,6 +3293,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -3041,6 +3311,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -3059,6 +3330,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -3076,6 +3348,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -3094,6 +3367,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -3111,6 +3385,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -3129,6 +3404,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -3146,6 +3422,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -3163,6 +3440,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -3184,6 +3462,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -3201,6 +3480,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -3218,6 +3516,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -3235,6 +3534,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -3252,6 +3552,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -3270,6 +3571,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -3288,6 +3590,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -3306,6 +3609,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -3323,6 +3627,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -3340,6 +3645,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -3357,6 +3663,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -3374,6 +3681,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -3392,6 +3700,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -3409,6 +3718,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -3426,6 +3736,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -3444,6 +3755,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -3461,6 +3773,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -3479,6 +3792,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -3496,6 +3810,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -3514,6 +3829,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -3531,6 +3847,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -3548,6 +3865,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -3569,6 +3887,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -3586,6 +3905,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -3603,6 +3941,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -3620,6 +3959,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -3637,6 +3977,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -3655,6 +3996,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -3673,6 +4015,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -3691,6 +4034,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -3708,6 +4052,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -3725,6 +4070,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -3742,6 +4088,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -3759,6 +4106,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -3777,6 +4125,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -3794,6 +4143,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -3811,6 +4161,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -3829,6 +4180,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -3846,6 +4198,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -3864,6 +4217,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -3881,6 +4235,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -3899,6 +4254,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -3916,6 +4272,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -3933,6 +4290,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -3954,6 +4312,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -3971,6 +4330,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -3988,6 +4366,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -4005,6 +4384,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -4022,6 +4402,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -4040,6 +4421,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -4058,6 +4440,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -4076,6 +4459,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -4093,6 +4477,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -4110,6 +4495,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -4127,6 +4513,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -4144,6 +4531,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -4162,6 +4550,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -4179,6 +4568,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -4196,6 +4586,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -4214,6 +4605,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -4231,6 +4623,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -4249,6 +4642,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -4266,6 +4660,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -4284,6 +4679,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -4301,6 +4697,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -4318,6 +4715,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -4339,6 +4737,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -4356,6 +4755,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -4373,6 +4791,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -4390,6 +4809,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -4407,6 +4827,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -4425,6 +4846,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -4443,6 +4865,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -4461,6 +4884,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -4478,6 +4902,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -4495,6 +4920,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -4512,6 +4938,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -4529,6 +4956,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -4547,6 +4975,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -4564,6 +4993,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -4582,6 +5012,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -4599,6 +5030,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -4617,6 +5049,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -4634,6 +5067,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -4652,6 +5086,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -4669,6 +5104,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -4686,6 +5122,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -4708,6 +5145,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -4725,6 +5163,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -4742,6 +5199,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -4759,6 +5217,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -4776,6 +5235,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -4794,6 +5254,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -4812,6 +5273,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -4830,6 +5292,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -4847,6 +5310,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -4864,6 +5328,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -4881,6 +5346,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -4898,6 +5364,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -4916,6 +5383,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -4933,6 +5401,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -4951,6 +5420,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -4968,6 +5438,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -4986,6 +5457,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -5003,6 +5475,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -5021,6 +5494,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -5038,6 +5512,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -5055,6 +5530,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -5078,6 +5554,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -5095,6 +5572,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -5112,6 +5608,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -5129,6 +5626,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -5146,6 +5644,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -5164,6 +5663,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -5182,6 +5682,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -5200,6 +5701,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -5217,6 +5719,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -5234,6 +5737,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -5251,6 +5755,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -5268,6 +5773,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -5286,6 +5792,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -5303,6 +5810,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -5320,6 +5828,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -5338,6 +5847,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -5355,6 +5865,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -5373,6 +5884,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -5390,6 +5902,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -5408,6 +5921,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -5425,6 +5939,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -5442,6 +5957,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -5464,6 +5980,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -5481,6 +5998,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -5498,6 +6034,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -5515,6 +6052,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -5532,6 +6070,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -5550,6 +6089,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -5568,6 +6108,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -5586,6 +6127,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -5603,6 +6145,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -5620,6 +6163,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -5637,6 +6181,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -5654,6 +6199,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -5672,6 +6218,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -5689,6 +6236,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -5706,6 +6254,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -5724,6 +6273,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -5741,6 +6291,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -5759,6 +6310,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -5776,6 +6328,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -5794,6 +6347,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -5811,6 +6365,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -5828,6 +6383,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -5851,6 +6407,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -5869,6 +6426,26 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -5887,6 +6464,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -5905,6 +6483,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -5923,6 +6502,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -5942,6 +6522,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -5961,6 +6542,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -5980,6 +6562,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -5998,6 +6581,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -6016,6 +6600,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -6034,6 +6619,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -6052,6 +6638,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -6071,6 +6658,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -6089,6 +6677,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -6108,6 +6697,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -6126,6 +6716,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -6145,6 +6736,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -6163,6 +6755,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -6182,6 +6775,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -6200,6 +6794,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -6218,6 +6813,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -6234,13 +6830,13 @@ } ] }, - "Mac64 Builder": {}, "Mac64 Debug": { "isolated_scripts": [ { "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -6259,6 +6855,26 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -6277,6 +6893,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -6295,6 +6912,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -6313,6 +6931,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -6332,6 +6951,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -6351,6 +6971,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -6370,6 +6991,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -6388,6 +7010,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -6406,6 +7029,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -6424,6 +7048,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -6442,6 +7067,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -6461,6 +7087,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -6479,6 +7106,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -6498,6 +7126,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -6516,6 +7145,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -6535,6 +7165,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -6553,6 +7184,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -6572,6 +7204,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -6590,6 +7223,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -6608,6 +7242,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -6630,6 +7265,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -6647,6 +7283,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -6664,6 +7319,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -6681,6 +7337,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -6698,6 +7355,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -6716,6 +7374,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -6734,6 +7393,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -6752,6 +7412,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -6769,6 +7430,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -6786,6 +7448,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -6803,6 +7466,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -6820,6 +7484,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -6838,6 +7503,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -6855,6 +7521,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -6873,6 +7540,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -6890,6 +7558,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -6908,6 +7577,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -6925,6 +7595,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -6943,6 +7614,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -6960,6 +7632,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -6977,6 +7650,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -6998,6 +7672,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -7015,6 +7690,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -7032,6 +7726,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -7049,6 +7744,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -7066,6 +7762,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -7084,6 +7781,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -7102,6 +7800,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -7120,6 +7819,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -7137,6 +7837,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -7154,6 +7855,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -7171,6 +7873,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -7188,6 +7891,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -7206,6 +7910,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -7223,6 +7928,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -7241,6 +7947,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -7258,6 +7965,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -7276,6 +7984,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -7293,6 +8002,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -7311,6 +8021,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -7328,6 +8039,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -7345,6 +8057,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -7367,6 +8080,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -7391,6 +8105,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -7408,6 +8123,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -7425,6 +8159,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -7442,6 +8177,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -7459,6 +8195,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -7477,6 +8214,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -7495,6 +8233,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -7513,6 +8252,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -7530,6 +8270,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -7547,6 +8288,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -7564,6 +8306,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -7581,6 +8324,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -7599,6 +8343,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -7616,6 +8361,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -7634,6 +8380,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -7651,6 +8398,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -7669,6 +8417,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -7686,6 +8435,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -7704,6 +8454,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -7721,6 +8472,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -7738,6 +8490,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -7759,6 +8512,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -7776,6 +8530,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -7793,6 +8566,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -7810,6 +8584,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -7827,6 +8602,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -7845,6 +8621,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -7863,6 +8640,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -7881,6 +8659,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -7898,6 +8677,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -7915,6 +8695,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -7932,6 +8713,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -7949,6 +8731,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -7967,6 +8750,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -7984,6 +8768,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -8002,6 +8787,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -8019,6 +8805,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -8037,6 +8824,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -8054,6 +8842,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -8072,6 +8861,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -8089,6 +8879,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -8106,6 +8897,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -8127,6 +8919,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -8144,6 +8937,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -8161,6 +8973,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -8178,6 +8991,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -8195,6 +9009,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -8213,6 +9028,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -8231,6 +9047,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -8249,6 +9066,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -8266,6 +9084,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -8283,6 +9102,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -8300,6 +9120,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -8317,6 +9138,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -8335,6 +9157,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -8352,6 +9175,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -8370,6 +9194,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -8387,6 +9212,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -8405,6 +9231,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -8422,6 +9249,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -8440,6 +9268,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -8457,6 +9286,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -8474,6 +9304,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -8495,6 +9326,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -8512,6 +9344,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -8529,6 +9380,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -8546,6 +9398,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -8563,6 +9416,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -8581,6 +9435,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -8599,6 +9454,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -8617,6 +9473,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -8634,6 +9491,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -8651,6 +9509,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -8668,6 +9527,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -8685,6 +9545,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -8703,6 +9564,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -8720,6 +9582,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -8738,6 +9601,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -8755,6 +9619,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -8773,6 +9638,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -8790,6 +9656,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -8808,6 +9675,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -8825,6 +9693,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -8842,6 +9711,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -8875,6 +9745,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:apprtcmobile_tests", "name": "apprtcmobile_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -8924,6 +9795,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:apprtcmobile_tests", "name": "apprtcmobile_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -8972,6 +9844,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9020,6 +9893,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9068,6 +9942,105 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests iPhone 14 17.5", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" + } + ], + "dimensions": { + "cpu": "arm64", + "os": "Mac-15" + }, + "named_caches": [ + { + "name": "runtime_ios_17_5", + "path": "Runtime-ios-17.5" + }, + { + "name": "xcode_ios_17a400", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/", + "variant_id": "iPhone 14 17.5" + }, + { + "args": [ + "--platform", + "iPhone 15", + "--version", + "18.2", + "--xcode-build-version", + "17a400", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xctest" + ], + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests iPhone 15 18.2", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" + } + ], + "dimensions": { + "cpu": "arm64", + "os": "Mac-15" + }, + "named_caches": [ + { + "name": "runtime_ios_18_2", + "path": "Runtime-ios-18.2" + }, + { + "name": "xcode_ios_17a400", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/", + "variant_id": "iPhone 15 18.2" + }, + { + "args": [ + "--platform", + "iPhone 14", + "--version", + "17.5", + "--xcode-build-version", + "17a400", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xctest" + ], + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9116,6 +10089,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9164,6 +10138,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9212,6 +10187,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9260,6 +10236,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9308,6 +10285,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9356,6 +10334,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9405,6 +10384,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9454,6 +10434,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9504,6 +10485,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9554,6 +10536,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9602,6 +10585,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9650,6 +10634,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9698,6 +10683,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9746,6 +10732,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9794,6 +10781,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9842,6 +10830,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9890,6 +10879,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -9939,6 +10929,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_framework_unittests", "name": "sdk_framework_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -9988,6 +10979,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_framework_unittests", "name": "sdk_framework_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10037,6 +11029,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_unittests", "name": "sdk_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10086,6 +11079,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_unittests", "name": "sdk_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10134,6 +11128,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10184,6 +11179,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10234,6 +11230,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10282,6 +11279,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10330,6 +11328,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10378,6 +11377,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10426,6 +11426,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10474,6 +11475,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10522,6 +11524,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10571,6 +11574,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10620,6 +11624,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10668,6 +11673,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10716,6 +11722,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10764,6 +11771,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -10812,6 +11820,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -10860,6 +11869,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests iPhone 15 18.2", "resultdb": { "enable": true, diff --git a/infra/specs/client.webrtc.perf.json b/infra/specs/client.webrtc.perf.json index 390ba9d724f..f70010c56e8 100644 --- a/infra/specs/client.webrtc.perf.json +++ b/infra/specs/client.webrtc.perf.json @@ -14,6 +14,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "enable": true, @@ -48,6 +49,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "enable": true, @@ -85,6 +87,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "enable": true, @@ -119,6 +122,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "enable": true, @@ -160,6 +164,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -196,6 +201,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -231,6 +237,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -264,6 +271,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -285,75 +293,6 @@ } ] }, - "Perf Mac 11": { - "isolated_scripts": [ - { - "args": [ - "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" - ], - "merge": { - "args": [ - "--test-suite", - "video_codec_perf_tests" - ], - "script": "//tools_webrtc/perf/process_perf_results.py" - }, - "name": "video_codec_perf_tests", - "resultdb": { - "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", - "result_format": "gtest_json" - }, - "swarming": { - "dimensions": { - "cpu": "x86-64", - "gce": "0", - "os": "Mac-11", - "pool": "WebRTC-perf" - }, - "expiration": 10800, - "hard_timeout": 10800, - "idempotent": false, - "io_timeout": 10800 - }, - "test": "video_codec_perf_tests", - "test_id_prefix": "ninja://modules/video_coding:video_codec_perf_tests/" - }, - { - "args": [ - "--test_artifacts_dir=${ISOLATED_OUTDIR}", - "--save_worst_frame", - "--nologs", - "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" - ], - "merge": { - "args": [ - "--test-suite", - "webrtc_perf_tests" - ], - "script": "//tools_webrtc/perf/process_perf_results.py" - }, - "name": "webrtc_perf_tests", - "resultdb": { - "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", - "result_format": "gtest_json" - }, - "swarming": { - "dimensions": { - "cpu": "x86-64", - "gce": "0", - "os": "Mac-11", - "pool": "WebRTC-perf" - }, - "expiration": 10800, - "hard_timeout": 10800, - "idempotent": false, - "io_timeout": 10800 - }, - "test": "webrtc_perf_tests", - "test_id_prefix": "ninja://:webrtc_perf_tests/" - } - ] - }, "Perf Mac M1 Arm64 12": { "isolated_scripts": [ { @@ -367,6 +306,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -401,6 +341,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -436,6 +377,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -469,6 +411,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", diff --git a/infra/specs/gn_isolate_map.pyl b/infra/specs/gn_isolate_map.pyl index 7e751ba049e..b73c31b147e 100644 --- a/infra/specs/gn_isolate_map.pyl +++ b/infra/specs/gn_isolate_map.pyl @@ -43,6 +43,10 @@ "label": "//modules/audio_coding:audio_decoder_unittests", "type": "console_test_launcher", }, + "audio_engine_tests": { + "label": "//:audio_engine_tests", + "type": "console_test_launcher", + }, "common_audio_unittests": { "label": "//common_audio:common_audio_unittests", "type": "console_test_launcher", diff --git a/infra/specs/internal.client.webrtc.json b/infra/specs/internal.client.webrtc.json index 9db8708b26b..fb25df4748c 100644 --- a/infra/specs/internal.client.webrtc.json +++ b/infra/specs/internal.client.webrtc.json @@ -14,127 +14,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, - "name": "common_audio_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "cipd_packages": [ - { - "cipd_package": "infra/tools/mac_toolchain/${platform}", - "location": ".", - "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" - } - ], - "dimensions": { - "os": "iOS-18", - "pool": "chrome.tests" - }, - "named_caches": [ - { - "name": "xcode_ios_17a400", - "path": "Xcode.app" - } - ], - "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "common_audio_unittests", - "test_id_prefix": "ninja://common_audio:common_audio_unittests/" - }, - { - "args": [ - "--xctest", - "--xcode-build-version", - "17a400", - "--out-dir", - "${ISOLATED_OUTDIR}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "common_video_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "cipd_packages": [ - { - "cipd_package": "infra/tools/mac_toolchain/${platform}", - "location": ".", - "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" - } - ], - "dimensions": { - "os": "iOS-18", - "pool": "chrome.tests" - }, - "named_caches": [ - { - "name": "xcode_ios_17a400", - "path": "Xcode.app" - } - ], - "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "common_video_unittests", - "test_id_prefix": "ninja://common_video:common_video_unittests/" - }, - { - "args": [ - "--readline-timeout=1200", - "--xctest", - "--xcode-build-version", - "17a400", - "--out-dir", - "${ISOLATED_OUTDIR}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "modules_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "cipd_packages": [ - { - "cipd_package": "infra/tools/mac_toolchain/${platform}", - "location": ".", - "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" - } - ], - "dimensions": { - "os": "iOS-18", - "pool": "chrome.tests" - }, - "hard_timeout": 7200, - "io_timeout": 7200, - "named_caches": [ - { - "name": "xcode_ios_17a400", - "path": "Xcode.app" - } - ], - "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "modules_tests", - "test_id_prefix": "ninja://modules:modules_tests/" - }, - { - "args": [ - "--xctest", - "--xcode-build-version", - "17a400", - "--out-dir", - "${ISOLATED_OUTDIR}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -175,6 +55,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -214,6 +95,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -253,6 +135,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -292,6 +175,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -331,6 +215,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -370,6 +255,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -409,6 +295,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -459,6 +346,7 @@ ], "script": "//tools_webrtc/perf/process_perf_results.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "enable": true, @@ -506,127 +394,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, - "name": "common_audio_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "cipd_packages": [ - { - "cipd_package": "infra/tools/mac_toolchain/${platform}", - "location": ".", - "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" - } - ], - "dimensions": { - "os": "iOS-18", - "pool": "chrome.tests" - }, - "named_caches": [ - { - "name": "xcode_ios_17a400", - "path": "Xcode.app" - } - ], - "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "common_audio_unittests", - "test_id_prefix": "ninja://common_audio:common_audio_unittests/" - }, - { - "args": [ - "--xctest", - "--xcode-build-version", - "17a400", - "--out-dir", - "${ISOLATED_OUTDIR}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "common_video_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "cipd_packages": [ - { - "cipd_package": "infra/tools/mac_toolchain/${platform}", - "location": ".", - "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" - } - ], - "dimensions": { - "os": "iOS-18", - "pool": "chrome.tests" - }, - "named_caches": [ - { - "name": "xcode_ios_17a400", - "path": "Xcode.app" - } - ], - "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "common_video_unittests", - "test_id_prefix": "ninja://common_video:common_video_unittests/" - }, - { - "args": [ - "--readline-timeout=1200", - "--xctest", - "--xcode-build-version", - "17a400", - "--out-dir", - "${ISOLATED_OUTDIR}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "modules_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "cipd_packages": [ - { - "cipd_package": "infra/tools/mac_toolchain/${platform}", - "location": ".", - "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" - } - ], - "dimensions": { - "os": "iOS-18", - "pool": "chrome.tests" - }, - "hard_timeout": 7200, - "io_timeout": 7200, - "named_caches": [ - { - "name": "xcode_ios_17a400", - "path": "Xcode.app" - } - ], - "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "modules_tests", - "test_id_prefix": "ninja://modules:modules_tests/" - }, - { - "args": [ - "--xctest", - "--xcode-build-version", - "17a400", - "--out-dir", - "${ISOLATED_OUTDIR}" - ], - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -667,6 +435,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -706,6 +475,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -745,6 +515,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -784,6 +555,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -823,6 +595,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -862,6 +635,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -901,6 +675,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, diff --git a/infra/specs/mixins.pyl b/infra/specs/mixins.pyl index 095aa1223f2..d69dd1ee0e3 100644 --- a/infra/specs/mixins.pyl +++ b/infra/specs/mixins.pyl @@ -18,19 +18,6 @@ } } }, - 'arm64': { - 'swarming': { - 'dimensions': { - 'cpu': 'arm64' - } - } - }, - 'chrome-tester-service-account': { - 'swarming': { - 'service_account': - 'chrome-tester@chops-service-accounts.iam.gserviceaccount.com' - } - }, 'chromium-tester-service-account': { 'fail_if_unused': False, 'swarming': { @@ -66,23 +53,6 @@ 'has_native_resultdb_integration': True } }, - 'ios-device-18': { - 'swarming': { - 'dimensions': { - 'os': 'iOS-18', - 'pool': 'chrome.tests' - } - } - }, - 'ios-device-perf': { - 'swarming': { - 'idempotent': False, - 'dimensions': { - 'os': 'iOS-17.6.1', - 'pool': 'WebRTC' - } - } - }, 'ios_runtime_cache_17_5': { 'swarming': { 'named_caches': [{ @@ -122,13 +92,6 @@ } } }, - 'mac11': { - 'swarming': { - 'dimensions': { - 'os': 'Mac-11' - } - } - }, 'mac_12_arm64': { 'swarming': { 'dimensions': { @@ -247,12 +210,6 @@ 'shards': 8 } }, - 'timeout-2h': { - 'swarming': { - 'hard_timeout': 7200, - 'io_timeout': 7200 - } - }, 'timeout-3h': { 'swarming': { 'hard_timeout': 10800, diff --git a/infra/specs/mixins_webrtc.pyl b/infra/specs/mixins_webrtc.pyl index 4ff80cec962..5ff53a34246 100644 --- a/infra/specs/mixins_webrtc.pyl +++ b/infra/specs/mixins_webrtc.pyl @@ -14,13 +14,6 @@ }, }, }, - 'arm64': { - 'swarming': { - 'dimensions': { - 'cpu': 'arm64' - } - } - }, 'cores-12': { 'swarming': { 'dimensions': { @@ -45,24 +38,6 @@ '--test-arg=--undefok=test_launcher_summary_output' ], }, - 'ios-device-18': { - 'swarming': { - 'dimensions': { - 'os': 'iOS-18', - 'pool': 'chrome.tests', - } - } - }, - 'ios-device-perf': { - 'swarming': { - 'idempotent': False, - 'dimensions': { - 'os': 'iOS-17.6.1', - 'pool': 'WebRTC', - #'device_status': 'available' - }, - }, - }, 'ios_runtime_cache_17_5': { 'swarming': { 'named_caches': [ @@ -125,13 +100,6 @@ } } }, - 'mac11': { - 'swarming': { - 'dimensions': { - 'os': 'Mac-11' - } - } - }, 'perf-output': { 'args': [ '--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb', @@ -219,12 +187,6 @@ 'shards': 8, }, }, - 'timeout-2h': { - 'swarming': { - 'hard_timeout': 7200, - 'io_timeout': 7200, - }, - }, 'timeout-3h': { 'swarming': { 'hard_timeout': 10800, diff --git a/infra/specs/test_suites.pyl b/infra/specs/test_suites.pyl index e8a45c59759..b3645f5f362 100644 --- a/infra/specs/test_suites.pyl +++ b/infra/specs/test_suites.pyl @@ -22,6 +22,7 @@ 'AppRTCMobile_test_apk': {}, 'android_instrumentation_test_apk': {}, 'audio_decoder_unittests': {}, + 'audio_engine_tests': {}, 'common_audio_unittests': {}, 'common_video_unittests': {}, 'dcsctp_unittests': {}, @@ -66,6 +67,7 @@ }, 'desktop_tests': { 'audio_decoder_unittests': {}, + 'audio_engine_tests': {}, 'common_audio_unittests': {}, 'common_video_unittests': {}, 'dcsctp_unittests': {}, @@ -142,39 +144,12 @@ # 'tools_unittests': {}, # }, - 'ios_device_tests': { - # TODO(bugs.webrtc.org/11362): Real XCTests fail to start on devices. - #'apprtcmobile_tests': {'mixins': ['xcodebuild-device-runner']}, - 'common_audio_unittests': {}, - 'common_video_unittests': {}, - 'modules_tests': { - 'mixins': ['shards-2', 'timeout-2h'], - 'args': [ - # Some tests exceed the default 180 seconds readline timeout. - '--readline-timeout=1200', - ] - }, - 'modules_unittests': { - 'mixins': ['shards-6'], - }, - 'rtc_p2p_unittests': {}, - 'rtc_pc_unittests': {}, - 'rtc_stats_unittests': {}, - # TODO(bugs.webrtc.org/11362): Real XCTests fail to start on devices. - #'sdk_framework_unittests': {'mixins': ['xcodebuild-device-runner']}, - #'sdk_unittests': {'mixins': ['xcodebuild-device-runner']}, - 'system_wrappers_unittests': {}, - 'test_support_unittests': {}, - 'tools_unittests': {}, - 'video_engine_tests': { - 'mixins': ['shards-4'], - }, - }, 'ios_simulator_tests': { 'apprtcmobile_tests': { 'mixins': ['xcodebuild_sim_runner'] }, 'audio_decoder_unittests': {}, + 'audio_engine_tests': {}, 'common_audio_unittests': {}, 'common_video_unittests': {}, 'dcsctp_unittests': {}, @@ -226,11 +201,6 @@ 'mixins': ['perf-webrtc-perf-tests'], }, }, - 'perf_tests_no_video_codec': { - 'webrtc_perf_tests': { - 'mixins': ['perf-webrtc-perf-tests'], - }, - }, 'perf_tests_save_worst_frame': { 'video_codec_perf_tests': { 'mixins': ['perf-video-codec-perf-tests'], diff --git a/infra/specs/tryserver.webrtc.json b/infra/specs/tryserver.webrtc.json index 9134a9198d1..afe1d37ab99 100644 --- a/infra/specs/tryserver.webrtc.json +++ b/infra/specs/tryserver.webrtc.json @@ -7,6 +7,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -27,6 +28,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -47,6 +49,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -67,6 +70,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -87,6 +112,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -107,6 +133,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -127,6 +154,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -148,6 +176,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -169,6 +198,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -190,6 +220,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -210,6 +241,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -230,6 +262,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -250,6 +283,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -270,6 +304,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -291,6 +326,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -311,6 +347,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -332,6 +369,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -352,6 +390,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -372,6 +411,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -396,6 +436,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "enable": true, @@ -417,6 +458,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -438,6 +480,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -458,6 +501,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -478,6 +522,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -502,6 +547,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "enable": true, @@ -527,6 +573,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -547,6 +594,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -571,6 +619,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -591,6 +640,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -611,6 +661,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -631,6 +682,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -651,6 +724,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -671,6 +745,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -691,6 +766,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -712,6 +788,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -733,6 +810,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -754,6 +832,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -774,6 +853,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -794,6 +874,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -814,6 +895,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -834,6 +916,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -855,6 +938,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -875,6 +959,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -896,6 +981,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -916,6 +1002,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -936,6 +1023,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -960,6 +1048,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "enable": true, @@ -981,6 +1070,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -1002,6 +1092,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -1022,6 +1113,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -1042,6 +1134,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -1066,6 +1159,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "enable": true, @@ -1091,6 +1185,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -1111,6 +1206,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -1135,6 +1231,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -1155,6 +1252,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -1175,6 +1273,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -1195,6 +1294,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -1215,6 +1336,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -1235,6 +1357,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -1255,6 +1378,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -1276,6 +1400,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -1297,6 +1422,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -1318,6 +1444,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -1338,6 +1465,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -1358,6 +1486,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -1378,6 +1507,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -1398,6 +1528,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -1419,6 +1550,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -1439,6 +1571,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -1460,6 +1593,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -1480,6 +1614,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -1500,6 +1635,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -1524,6 +1660,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "enable": true, @@ -1545,6 +1682,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -1566,6 +1704,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -1586,6 +1725,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -1606,6 +1746,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -1630,6 +1771,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "enable": true, @@ -1655,6 +1797,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -1675,6 +1818,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -1699,6 +1843,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -1724,6 +1869,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//examples:AppRTCMobile_test_apk", "name": "AppRTCMobile_test_apk", "resultdb": { "enable": true, @@ -1744,6 +1890,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//sdk/android:android_instrumentation_test_apk", "name": "android_instrumentation_test_apk", "resultdb": { "enable": true, @@ -1764,6 +1911,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "enable": true, @@ -1784,6 +1932,28 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "dimensions": { + "android_devices": "1", + "device_type": "walleye", + "os": "Android" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "enable": true, @@ -1804,6 +1974,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "enable": true, @@ -1824,6 +1995,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "enable": true, @@ -1844,6 +2016,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "enable": true, @@ -1865,6 +2038,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "enable": true, @@ -1886,6 +2060,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "enable": true, @@ -1907,6 +2082,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "enable": true, @@ -1927,6 +2103,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "enable": true, @@ -1947,6 +2124,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "enable": true, @@ -1967,6 +2145,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "enable": true, @@ -1987,6 +2166,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "enable": true, @@ -2008,6 +2188,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "enable": true, @@ -2028,6 +2209,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "enable": true, @@ -2049,6 +2231,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "enable": true, @@ -2069,6 +2252,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "enable": true, @@ -2089,6 +2273,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "enable": true, @@ -2113,6 +2298,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "enable": true, @@ -2134,6 +2320,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "enable": true, @@ -2155,6 +2342,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "enable": true, @@ -2175,6 +2363,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "enable": true, @@ -2195,6 +2384,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "enable": true, @@ -2219,6 +2409,7 @@ "merge": { "script": "//testing/merge_scripts/standard_gtest_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "enable": true, @@ -2244,6 +2435,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:android_examples_junit_tests", "name": "android_examples_junit_tests", "resultdb": { "enable": true, @@ -2264,6 +2456,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk/android:android_sdk_junit_tests", "name": "android_sdk_junit_tests", "resultdb": { "enable": true, @@ -2302,6 +2495,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2326,6 +2520,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2350,6 +2545,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2374,6 +2570,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2398,6 +2595,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2422,6 +2620,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2446,6 +2645,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2470,6 +2670,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2495,6 +2696,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2519,6 +2721,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2544,6 +2747,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2568,6 +2772,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -2604,6 +2809,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:apprtcmobile_tests", "name": "apprtcmobile_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -2653,6 +2859,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//examples:apprtcmobile_tests", "name": "apprtcmobile_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -2701,6 +2908,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -2749,6 +2957,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -2797,6 +3006,105 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests iPhone 14 17.5", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" + } + ], + "dimensions": { + "cpu": "arm64", + "os": "Mac-15" + }, + "named_caches": [ + { + "name": "runtime_ios_17_5", + "path": "Runtime-ios-17.5" + }, + { + "name": "xcode_ios_17a400", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/", + "variant_id": "iPhone 14 17.5" + }, + { + "args": [ + "--platform", + "iPhone 15", + "--version", + "18.2", + "--xcode-build-version", + "17a400", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xctest" + ], + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests iPhone 15 18.2", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:4c7290150d1c360cecc6a93c0214dc531585c3ab" + } + ], + "dimensions": { + "cpu": "arm64", + "os": "Mac-15" + }, + "named_caches": [ + { + "name": "runtime_ios_18_2", + "path": "Runtime-ios-18.2" + }, + { + "name": "xcode_ios_17a400", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/", + "variant_id": "iPhone 15 18.2" + }, + { + "args": [ + "--platform", + "iPhone 14", + "--version", + "17.5", + "--xcode-build-version", + "17a400", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xctest" + ], + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -2845,6 +3153,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -2893,6 +3202,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -2941,6 +3251,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -2989,6 +3300,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3037,6 +3349,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3085,6 +3398,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3134,6 +3448,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3183,6 +3498,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3233,6 +3549,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3283,6 +3600,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3331,6 +3649,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3379,6 +3698,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3427,6 +3747,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3475,6 +3796,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3523,6 +3845,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3571,6 +3894,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3619,6 +3943,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3668,6 +3993,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_framework_unittests", "name": "sdk_framework_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3717,6 +4043,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_framework_unittests", "name": "sdk_framework_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3766,6 +4093,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_unittests", "name": "sdk_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3815,6 +4143,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//sdk:sdk_unittests", "name": "sdk_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3863,6 +4192,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -3913,6 +4243,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -3963,6 +4294,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -4011,6 +4343,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -4059,6 +4392,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -4107,6 +4441,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -4155,6 +4490,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -4203,6 +4539,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -4251,6 +4588,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -4300,6 +4638,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -4349,6 +4688,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -4397,6 +4737,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -4445,6 +4786,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests iPhone 14 17.5", "resultdb": { "enable": true, @@ -4493,6 +4835,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests iPhone 15 18.2", "resultdb": { "enable": true, @@ -4541,6 +4884,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests iPhone 14 17.5", "resultdb": { "enable": true, @@ -4589,6 +4933,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests iPhone 15 18.2", "resultdb": { "enable": true, @@ -4631,6 +4976,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -4648,6 +4994,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -4665,6 +5030,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -4682,6 +5048,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -4699,6 +5066,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -4717,6 +5085,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -4735,6 +5104,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -4753,6 +5123,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -4770,6 +5141,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -4787,6 +5159,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -4804,6 +5177,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -4821,6 +5195,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -4839,6 +5214,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -4856,6 +5232,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -4873,6 +5250,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -4891,6 +5269,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -4908,6 +5287,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -4926,6 +5306,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -4943,6 +5324,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -4961,6 +5343,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -4978,6 +5361,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -4995,6 +5379,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -5023,6 +5408,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -5041,6 +5427,26 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "isolate_profile_data": true, + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -5059,6 +5465,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -5077,6 +5484,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -5095,6 +5503,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -5114,6 +5523,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -5133,6 +5543,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -5152,6 +5563,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -5170,6 +5582,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -5188,6 +5601,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -5206,6 +5620,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -5224,6 +5639,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -5243,6 +5659,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -5261,6 +5678,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -5279,6 +5697,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -5298,6 +5717,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -5316,6 +5736,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -5335,6 +5756,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -5358,6 +5780,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -5377,6 +5800,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -5396,6 +5820,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -5414,6 +5839,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -5432,6 +5858,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -5455,6 +5882,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -5477,6 +5905,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -5494,6 +5923,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -5511,6 +5959,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -5528,6 +5977,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -5545,6 +5995,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -5563,6 +6014,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -5581,6 +6033,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -5599,6 +6052,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -5616,6 +6070,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -5633,6 +6088,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -5650,6 +6106,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -5667,6 +6124,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -5685,6 +6143,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -5702,6 +6161,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -5719,6 +6179,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -5737,6 +6198,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -5754,6 +6216,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -5772,6 +6235,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -5789,6 +6253,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -5807,6 +6272,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -5824,6 +6290,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -5841,6 +6308,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -5863,6 +6331,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -5885,6 +6354,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -5902,6 +6372,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -5919,6 +6408,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -5936,6 +6426,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -5953,6 +6444,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -5971,6 +6463,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -5989,6 +6482,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -6007,6 +6501,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -6024,6 +6519,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -6041,6 +6537,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -6058,6 +6555,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -6075,6 +6573,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -6093,6 +6592,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -6110,6 +6610,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -6127,6 +6628,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -6145,6 +6647,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -6162,6 +6665,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -6180,6 +6684,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -6197,6 +6702,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -6215,6 +6721,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -6232,6 +6739,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -6249,6 +6757,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -6270,6 +6779,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -6287,6 +6797,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -6304,6 +6833,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -6321,6 +6851,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -6338,6 +6869,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -6356,6 +6888,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -6374,6 +6907,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -6392,6 +6926,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -6409,6 +6944,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -6426,6 +6962,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -6443,6 +6980,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -6460,6 +6998,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -6478,6 +7017,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -6495,6 +7035,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -6512,6 +7053,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -6530,6 +7072,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -6547,6 +7090,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -6565,6 +7109,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -6587,6 +7132,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -6605,6 +7151,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -6623,6 +7170,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -6640,6 +7188,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -6657,6 +7206,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -6679,6 +7229,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -6701,6 +7252,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -6718,6 +7270,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -6735,6 +7306,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -6752,6 +7324,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -6769,6 +7342,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -6787,6 +7361,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -6805,6 +7380,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -6823,6 +7399,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -6840,6 +7417,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -6857,6 +7435,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -6874,6 +7453,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -6891,6 +7471,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -6909,6 +7490,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -6926,6 +7508,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -6943,6 +7526,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -6961,6 +7545,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -6978,6 +7563,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -6996,6 +7582,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -7013,6 +7600,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -7031,6 +7619,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -7048,6 +7637,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -7065,6 +7655,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -7086,6 +7677,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -7103,6 +7695,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -7120,6 +7731,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -7137,6 +7749,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -7154,6 +7767,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -7172,6 +7786,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -7190,6 +7805,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -7208,6 +7824,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -7225,6 +7842,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -7242,6 +7860,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -7259,6 +7878,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -7276,6 +7896,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -7294,6 +7915,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -7311,6 +7933,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -7328,6 +7951,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -7346,6 +7970,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -7363,6 +7988,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -7381,6 +8007,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -7398,6 +8025,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -7416,6 +8044,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -7433,6 +8062,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -7450,6 +8080,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -7471,6 +8102,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -7488,6 +8120,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -7505,6 +8156,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -7522,6 +8174,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -7539,6 +8192,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -7557,6 +8211,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -7575,6 +8230,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -7593,6 +8249,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -7610,6 +8267,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -7627,6 +8285,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -7644,6 +8303,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -7661,6 +8321,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -7679,6 +8340,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/desktop_capture:shared_screencast_stream_test", "name": "shared_screencast_stream_test", "resultdb": { "result_format": "json" @@ -7696,6 +8358,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -7713,6 +8376,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -7731,6 +8395,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -7748,6 +8413,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -7766,6 +8432,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -7783,6 +8450,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -7801,6 +8469,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -7818,6 +8487,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -7835,6 +8505,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -7856,6 +8527,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -7873,6 +8545,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -7890,6 +8581,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -7907,6 +8599,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -7924,6 +8617,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -7942,6 +8636,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -7960,6 +8655,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -7978,6 +8674,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -7995,6 +8692,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -8012,6 +8710,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -8029,6 +8728,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -8046,6 +8746,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -8064,6 +8765,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -8081,6 +8783,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -8099,6 +8802,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -8116,6 +8820,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -8134,6 +8839,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -8151,6 +8857,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -8169,6 +8876,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -8186,6 +8894,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -8203,6 +8912,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -8224,6 +8934,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -8241,6 +8952,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Ubuntu-22.04" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -8258,6 +8988,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -8275,6 +9006,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -8292,6 +9024,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -8310,6 +9043,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -8328,6 +9062,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -8346,6 +9081,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -8363,6 +9099,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -8380,6 +9117,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -8397,6 +9135,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -8414,6 +9153,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -8432,6 +9172,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -8449,6 +9190,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -8467,6 +9209,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -8484,6 +9227,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -8502,6 +9246,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -8519,6 +9264,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -8537,6 +9283,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -8554,6 +9301,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -8571,6 +9319,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -8592,6 +9341,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -8610,6 +9360,26 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -8628,6 +9398,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -8646,6 +9417,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -8664,6 +9436,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -8683,6 +9456,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -8702,6 +9476,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -8721,6 +9496,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -8739,6 +9515,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -8757,6 +9534,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -8775,6 +9553,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -8793,6 +9572,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -8812,6 +9592,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -8830,6 +9611,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -8849,6 +9631,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -8867,6 +9650,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -8886,6 +9670,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -8904,6 +9689,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -8923,6 +9709,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -8941,6 +9728,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -8959,6 +9747,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -8983,6 +9772,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -9001,6 +9791,26 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -9019,6 +9829,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -9037,6 +9848,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -9055,6 +9867,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -9074,6 +9887,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -9093,6 +9907,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -9112,6 +9927,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -9130,6 +9946,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -9148,6 +9965,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -9166,6 +9984,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -9184,6 +10003,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -9203,6 +10023,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -9221,6 +10042,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -9240,6 +10062,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -9258,6 +10081,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -9277,6 +10101,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -9295,6 +10120,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -9314,6 +10140,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -9332,6 +10159,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -9350,6 +10178,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -9372,6 +10201,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -9389,6 +10219,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -9406,6 +10255,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -9423,6 +10273,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -9440,6 +10291,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -9458,6 +10310,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -9476,6 +10329,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -9494,6 +10348,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -9511,6 +10366,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -9528,6 +10384,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -9545,6 +10402,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -9562,6 +10420,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -9580,6 +10439,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -9597,6 +10457,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -9615,6 +10476,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -9632,6 +10494,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -9650,6 +10513,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -9667,6 +10531,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -9685,6 +10550,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -9702,6 +10568,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -9719,6 +10586,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -9740,6 +10608,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -9757,6 +10626,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -9774,6 +10662,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -9791,6 +10680,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -9808,6 +10698,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -9826,6 +10717,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -9844,6 +10736,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -9862,6 +10755,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -9879,6 +10773,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -9896,6 +10791,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -9913,6 +10809,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -9930,6 +10827,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -9948,6 +10846,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -9965,6 +10864,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -9983,6 +10883,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -10000,6 +10901,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -10018,6 +10920,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -10040,6 +10943,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -10058,6 +10962,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -10076,6 +10981,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -10093,6 +10999,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -10110,6 +11017,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -10132,6 +11040,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -10154,6 +11063,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -10171,6 +11081,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-15" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -10188,6 +11117,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -10205,6 +11135,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -10222,6 +11153,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -10240,6 +11172,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -10258,6 +11191,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -10276,6 +11210,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -10293,6 +11228,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -10310,6 +11246,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -10327,6 +11264,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -10344,6 +11282,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -10362,6 +11301,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -10379,6 +11319,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -10397,6 +11338,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -10414,6 +11356,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -10432,6 +11375,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -10449,6 +11393,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -10467,6 +11412,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -10484,6 +11430,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -10501,6 +11448,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -10522,6 +11470,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -10539,6 +11488,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-11-22000" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -10556,6 +11524,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -10573,6 +11542,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -10590,6 +11560,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -10608,6 +11579,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -10626,6 +11598,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -10644,6 +11617,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -10661,6 +11635,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -10678,6 +11653,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -10695,6 +11671,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -10712,6 +11689,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -10730,6 +11708,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -10747,6 +11726,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -10765,6 +11745,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -10782,6 +11763,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -10800,6 +11782,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -10817,6 +11800,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -10835,6 +11819,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -10852,6 +11837,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -10869,6 +11855,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -10890,6 +11877,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -10907,6 +11895,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-11-22000" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -10924,6 +11931,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -10941,6 +11949,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -10958,6 +11967,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -10976,6 +11986,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -10994,6 +12005,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -11012,6 +12024,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -11029,6 +12042,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -11046,6 +12060,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -11063,6 +12078,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -11080,6 +12096,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -11098,6 +12115,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -11115,6 +12133,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -11133,6 +12152,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -11150,6 +12170,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -11168,6 +12189,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -11190,6 +12212,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -11208,6 +12231,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -11226,6 +12250,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -11243,6 +12268,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -11260,6 +12286,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -11282,6 +12309,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -11304,6 +12332,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -11321,6 +12350,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -11338,6 +12386,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -11355,6 +12404,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -11372,6 +12422,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -11390,6 +12441,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -11408,6 +12460,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -11426,6 +12479,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -11443,6 +12497,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -11460,6 +12515,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -11477,6 +12533,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -11494,6 +12551,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -11512,6 +12570,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -11529,6 +12588,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -11547,6 +12607,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -11564,6 +12625,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -11582,6 +12644,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -11599,6 +12662,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -11617,6 +12681,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -11634,6 +12699,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -11651,6 +12717,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -11676,6 +12743,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -11693,6 +12761,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -11710,6 +12797,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -11727,6 +12815,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -11744,6 +12833,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -11762,6 +12852,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -11780,6 +12871,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -11798,6 +12890,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -11815,6 +12908,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -11832,6 +12926,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -11849,6 +12944,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -11866,6 +12962,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -11884,6 +12981,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -11901,6 +12999,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -11919,6 +13018,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -11936,6 +13036,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -11954,6 +13055,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -11971,6 +13073,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -11989,6 +13092,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -12006,6 +13110,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -12023,6 +13128,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -12044,6 +13150,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -12061,6 +13168,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -12078,6 +13204,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -12095,6 +13222,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -12112,6 +13240,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -12130,6 +13259,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -12148,6 +13278,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -12166,6 +13297,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -12183,6 +13315,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -12200,6 +13333,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -12217,6 +13351,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -12234,6 +13369,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -12252,6 +13388,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -12269,6 +13406,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -12287,6 +13425,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -12304,6 +13443,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -12322,6 +13462,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -12339,6 +13480,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -12357,6 +13499,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -12374,6 +13517,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -12391,6 +13535,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -12412,6 +13557,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -12429,6 +13575,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -12446,6 +13611,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -12463,6 +13629,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -12480,6 +13647,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -12498,6 +13666,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -12516,6 +13685,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -12534,6 +13704,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -12551,6 +13722,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -12568,6 +13740,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -12585,6 +13758,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -12602,6 +13776,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -12620,6 +13795,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -12637,6 +13813,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -12655,6 +13832,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -12672,6 +13850,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -12690,6 +13869,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -12707,6 +13887,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -12725,6 +13906,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -12742,6 +13924,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -12759,6 +13942,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -12780,6 +13964,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/audio_coding:audio_decoder_unittests", "name": "audio_decoder_unittests", "resultdb": { "result_format": "json" @@ -12797,6 +13982,25 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:audio_engine_tests", + "name": "audio_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "dimensions": { + "cpu": "x86-64", + "os": "Windows-10-19045" + } + }, + "test": "audio_engine_tests", + "test_id_prefix": "ninja://:audio_engine_tests/" + }, + { + "merge": { + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "module_name": "//common_audio:common_audio_unittests", "name": "common_audio_unittests", "resultdb": { "result_format": "json" @@ -12814,6 +14018,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//common_video:common_video_unittests", "name": "common_video_unittests", "resultdb": { "result_format": "json" @@ -12831,6 +14036,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//net/dcsctp:dcsctp_unittests", "name": "dcsctp_unittests", "resultdb": { "result_format": "json" @@ -12848,6 +14054,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_tests", "name": "modules_tests", "resultdb": { "result_format": "json" @@ -12866,6 +14073,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules:modules_unittests", "name": "modules_unittests", "resultdb": { "result_format": "json" @@ -12884,6 +14092,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" @@ -12902,6 +14111,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//media:rtc_media_unittests", "name": "rtc_media_unittests", "resultdb": { "result_format": "json" @@ -12919,6 +14129,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_p2p_unittests", "name": "rtc_p2p_unittests", "resultdb": { "result_format": "json" @@ -12936,6 +14147,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:rtc_pc_unittests", "name": "rtc_pc_unittests", "resultdb": { "result_format": "json" @@ -12953,6 +14165,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//stats:rtc_stats_unittests", "name": "rtc_stats_unittests", "resultdb": { "result_format": "json" @@ -12970,6 +14183,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:rtc_unittests", "name": "rtc_unittests", "resultdb": { "result_format": "json" @@ -12988,6 +14202,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:slow_peer_connection_unittests", "name": "slow_peer_connection_unittests", "resultdb": { "result_format": "json" @@ -13005,6 +14220,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:svc_tests", "name": "svc_tests", "resultdb": { "result_format": "json" @@ -13023,6 +14239,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//system_wrappers:system_wrappers_unittests", "name": "system_wrappers_unittests", "resultdb": { "result_format": "json" @@ -13040,6 +14257,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//test:test_support_unittests", "name": "test_support_unittests", "resultdb": { "result_format": "json" @@ -13058,6 +14276,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//rtc_tools:tools_unittests", "name": "tools_unittests", "resultdb": { "result_format": "json" @@ -13080,6 +14299,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//modules/video_coding:video_codec_perf_tests", "name": "video_codec_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -13098,6 +14318,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:video_engine_tests", "name": "video_engine_tests", "resultdb": { "result_format": "json" @@ -13116,6 +14337,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//video/timing:video_timing_tests", "name": "video_timing_tests", "resultdb": { "result_format": "json" @@ -13133,6 +14355,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:voip_unittests", "name": "voip_unittests", "resultdb": { "result_format": "json" @@ -13150,6 +14373,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_nonparallel_tests", "name": "webrtc_nonparallel_tests", "resultdb": { "result_format": "json" @@ -13172,6 +14396,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//:webrtc_perf_tests", "name": "webrtc_perf_tests", "resultdb": { "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", @@ -13194,6 +14419,7 @@ "merge": { "script": "//testing/merge_scripts/standard_isolated_script_merge.py" }, + "module_name": "//pc:peerconnection_unittests", "name": "peerconnection_unittests", "resultdb": { "result_format": "json" diff --git a/infra/specs/waterfalls.pyl b/infra/specs/waterfalls.pyl index bddb19c0fb5..a716f5e96f9 100644 --- a/infra/specs/waterfalls.pyl +++ b/infra/specs/waterfalls.pyl @@ -160,7 +160,6 @@ 'isolated_scripts': 'desktop_tests', }, }, - 'Mac64 Builder': {}, 'Mac64 Debug': { 'os_type': 'mac', 'mixins': ['mac_15_x64', 'cores-12', 'resultdb-json-format'], @@ -277,17 +276,6 @@ 'isolated_scripts': 'perf_tests_save_worst_frame', }, }, - 'Perf Mac 11': { - 'os_type': - 'mac', - 'mixins': [ - 'mac11', 'x86-64', 'perf-pool', 'timeout-3h', - 'resultdb-gtest-json-format' - ], - 'test_suites': { - 'isolated_scripts': 'perf_tests_save_worst_frame', - }, - }, 'Perf Mac M1 Arm64 12': { 'os_type': 'mac', @@ -310,45 +298,6 @@ }, }, }, - { - 'name': 'internal.client.webrtc', - 'mixins': [], - 'machines': { - 'iOS64 Debug': { - 'mixins': [ - 'ios-device-18', 'webrtc-xctest', 'chrome-tester-service-account', - 'xcode_26_main', 'mac_toolchain', 'has_native_resultdb_integration', - 'out_dir_arg' - ], - 'test_suites': { - 'isolated_scripts': 'ios_device_tests', - }, - }, - 'iOS64 Perf': { - 'mixins': [ - 'arm64', 'ios-device-perf', 'webrtc-xctest', 'timeout-3h', - 'chrome-tester-service-account', 'xcode_26_main', 'mac_toolchain', - 'has_native_resultdb_integration', 'out_dir_arg' - ], - 'test_suites': { - 'isolated_scripts': 'perf_tests_no_video_codec', - }, - 'args': [ - '--write_perf_output_on_ios', - ], - }, - 'iOS64 Release': { - 'mixins': [ - 'ios-device-18', 'webrtc-xctest', 'chrome-tester-service-account', - 'xcode_26_main', 'mac_toolchain', 'has_native_resultdb_integration', - 'out_dir_arg' - ], - 'test_suites': { - 'isolated_scripts': 'ios_device_tests', - }, - }, - }, - }, { 'name': 'tryserver.webrtc', 'mixins': [], diff --git a/logging/BUILD.gn b/logging/BUILD.gn index 9f5b865ba12..40914333ebc 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -58,7 +58,6 @@ rtc_library("rtc_event_field") { deps = [ ":rtc_event_log_parse_status", ":rtc_event_number_encodings", - "../api:array_view", "../api/rtc_event_log", "../api/units:timestamp", "../rtc_base:bitstream_reader", @@ -92,7 +91,6 @@ rtc_library("rtc_event_pacing") { deps = [ ":rtc_event_field", ":rtc_event_log_parse_status", - "../api:array_view", "../api/rtc_event_log", "../api/units:timestamp", "//third_party/abseil-cpp/absl/memory", @@ -119,7 +117,6 @@ rtc_library("rtc_event_audio") { ":rtc_event_field", ":rtc_event_log_parse_status", ":rtc_stream_config", - "../api:array_view", "../api/rtc_event_log", "../api/units:timestamp", "../modules/audio_coding:audio_network_adaptor_config", @@ -140,7 +137,6 @@ rtc_library("rtc_event_begin_end") { deps = [ ":rtc_event_field", ":rtc_event_log_parse_status", - "../api:array_view", "../api/rtc_event_log", "../api/units:timestamp", "//third_party/abseil-cpp/absl/strings:string_view", @@ -168,7 +164,6 @@ rtc_library("rtc_event_bwe") { deps = [ ":rtc_event_field", ":rtc_event_log_parse_status", - "../api:array_view", "../api/rtc_event_log", "../api/transport:bandwidth_usage", "../api/units:data_rate", @@ -188,7 +183,6 @@ rtc_library("rtc_event_bwe_update_scream") { deps = [ ":rtc_event_log_parse_status", - "../api:array_view", "../api/rtc_event_log", "../api/units:data_rate", "../api/units:data_size", @@ -209,7 +203,6 @@ rtc_library("rtc_event_frame_events") { deps = [ ":rtc_event_field", ":rtc_event_log_parse_status", - "../api:array_view", "../api/rtc_event_log", "../api/units:timestamp", "../api/video:video_frame", @@ -236,7 +229,6 @@ rtc_library("rtc_event_rtp_rtcp") { deps = [ ":rtc_event_field", ":rtc_event_log_parse_status", - "../api:array_view", "../api:rtp_headers", "../api/rtc_event_log", "../api/units:timestamp", @@ -261,7 +253,6 @@ rtc_library("rtc_event_video") { ":rtc_event_field", ":rtc_event_log_parse_status", ":rtc_stream_config", - "../api:array_view", "../api/rtc_event_log", "../api/units:timestamp", "../rtc_base:checks", @@ -373,7 +364,6 @@ rtc_library("rtc_event_log_impl_encoder") { ":rtc_event_rtp_rtcp", ":rtc_event_video", ":rtc_stream_config", - "../api:array_view", "../api:candidate", "../api:dtls_transport_interface", "../api/rtc_event_log", @@ -407,7 +397,6 @@ if (rtc_enable_protobuf) { ":rtc_event_log_optional_blob_encoding", ":rtc_event_log_parse_status", ":rtc_event_log_proto", # Why does this need to be included here? - "../api:array_view", "../rtc_base:bitstream_reader", "../rtc_base:checks", "../rtc_base:logging", @@ -598,7 +587,6 @@ if (rtc_enable_protobuf) { ":rtc_event_rtp_rtcp", ":rtc_event_video", ":rtc_stream_config", - "../api:array_view", "../api:candidate", "../api:dtls_transport_interface", "../api:field_trials", @@ -650,7 +638,6 @@ if (rtc_enable_protobuf) { deps = [ ":rtc_event_log_parser", ":rtc_event_rtp_rtcp", - "../api:array_view", "../api:rtp_headers", "../api/rtc_event_log", "../modules/rtp_rtcp", @@ -688,7 +675,6 @@ rtc_library("ice_log") { deps = [ ":rtc_event_field", ":rtc_event_log_parse_status", - "../api:array_view", "../api:candidate", "../api:dtls_transport_interface", "../api:libjingle_logging_api", diff --git a/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc index eaa75331c80..995524780dd 100644 --- a/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc +++ b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "logging/rtc_event_log/encoder/delta_encoding.h" #include "logging/rtc_event_log/encoder/optional_blob_encoding.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -30,7 +30,7 @@ namespace webrtc { // static std::optional RtcEventLogDependencyDescriptorEncoderDecoder::Encode( - const std::vector>& raw_dd_data) { + const std::vector>& raw_dd_data) { if (raw_dd_data.empty()) { return {}; } @@ -43,9 +43,8 @@ RtcEventLogDependencyDescriptorEncoderDecoder::Encode( } rtclog2::DependencyDescriptorsWireInfo res; - const ArrayView& base_dd = raw_dd_data[0]; - auto delta_dds = - MakeArrayView(raw_dd_data.data(), raw_dd_data.size()).subview(1); + const std::span& base_dd = raw_dd_data[0]; + auto delta_dds = std::span(raw_dd_data.data(), raw_dd_data.size()).subspan(1); // Start and end bit. { @@ -117,7 +116,7 @@ RtcEventLogDependencyDescriptorEncoderDecoder::Encode( std::vector> values(raw_dd_data.size()); for (size_t i = 0; i < raw_dd_data.size(); ++i) { if (raw_dd_data[i].size() > 3) { - auto extended_info = raw_dd_data[i].subview(3); + auto extended_info = raw_dd_data[i].subspan(3); values[i] = {reinterpret_cast(extended_info.data()), extended_info.size()}; } diff --git a/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h index 80f35f22f6e..d0867e90aed 100644 --- a/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h +++ b/logging/rtc_event_log/dependency_descriptor_encoder_decoder.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" #include "logging/rtc_event_log/rtc_event_log2_proto_include.h" // IWYU pragma: keep @@ -25,7 +25,7 @@ namespace webrtc { class RtcEventLogDependencyDescriptorEncoderDecoder { public: static std::optional Encode( - const std::vector>& raw_dd_data); + const std::vector>& raw_dd_data); static RtcEventLogParseStatusOr>> Decode( const rtclog2::DependencyDescriptorsWireInfo& dd_wire_info, size_t num_packets); diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc index 5434a4092d7..c225616d741 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/candidate.h" #include "api/rtc_event_log/rtc_event.h" #include "api/rtp_headers.h" @@ -753,7 +753,7 @@ std::string RtcEventLogEncoderLegacy::EncodeRtcpPacket(int64_t timestamp_us, std::string RtcEventLogEncoderLegacy::EncodeRtpPacket( int64_t timestamp_us, - ArrayView header, + std::span header, size_t packet_length, int probe_cluster_id, bool is_incoming) { diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h index c03d3dbb4a1..98252cce9c9 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" #include "rtc_base/buffer.h" @@ -100,7 +100,7 @@ class RtcEventLogEncoderLegacy final : public RtcEventLogEncoder { const Buffer& packet, bool is_incoming); std::string EncodeRtpPacket(int64_t timestamp_us, - ArrayView header, + std::span header, size_t packet_length, int probe_cluster_id, bool is_incoming); diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc index 47396b71744..67667fcf29f 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc @@ -17,11 +17,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/candidate.h" #include "api/dtls_transport_interface.h" #include "api/field_trials_view.h" @@ -340,7 +340,7 @@ size_t RemoveNonAllowlistedRtcpBlocks(const Buffer& packet, uint8_t* buffer) { } template -void EncodeRtcpPacket(ArrayView batch, +void EncodeRtcpPacket(std::span batch, ProtoType* proto_batch) { if (batch.empty()) { return; @@ -465,23 +465,18 @@ void RtcEventLogEncoderNewFormat::EncodeRtpPacket(const Batch& batch, } { - // TODO(webrtc:14975) Remove this kill switch after DD in RTC event log has - // been rolled out. - if (encode_dependency_descriptor_) { - std::vector> raw_dds(batch.size()); - bool has_dd = false; - for (size_t i = 0; i < batch.size(); ++i) { - raw_dds[i] = - batch[i] - ->template GetRawExtension(); - has_dd |= !raw_dds[i].empty(); - } - if (has_dd) { - if (auto dd_encoded = - RtcEventLogDependencyDescriptorEncoderDecoder::Encode( - raw_dds)) { - *proto_batch->mutable_dependency_descriptor() = *dd_encoded; - } + std::vector> raw_dds(batch.size()); + bool has_dd = false; + for (size_t i = 0; i < batch.size(); ++i) { + raw_dds[i] = + batch[i] + ->template GetRawExtension(); + has_dd |= !raw_dds[i].empty(); + } + if (has_dd) { + if (auto dd_encoded = + RtcEventLogDependencyDescriptorEncoderDecoder::Encode(raw_dds)) { + *proto_batch->mutable_dependency_descriptor() = *dd_encoded; } } } @@ -704,9 +699,7 @@ void RtcEventLogEncoderNewFormat::EncodeRtpPacket(const Batch& batch, RtcEventLogEncoderNewFormat::RtcEventLogEncoderNewFormat( const FieldTrialsView& field_trials) : encode_neteq_set_minimum_delay_kill_switch_(field_trials.IsEnabled( - "WebRTC-RtcEventLogEncodeNetEqSetMinimumDelayKillSwitch")), - encode_dependency_descriptor_(!field_trials.IsDisabled( - "WebRTC-RtcEventLogEncodeDependencyDescriptor")) {} + "WebRTC-RtcEventLogEncodeNetEqSetMinimumDelayKillSwitch")) {} std::string RtcEventLogEncoderNewFormat::EncodeLogStart(int64_t timestamp_us, int64_t utc_time_us) { @@ -973,7 +966,7 @@ std::string RtcEventLogEncoderNewFormat::EncodeBatch( } void RtcEventLogEncoderNewFormat::EncodeAlrState( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventAlrState* base_event : batch) { rtclog2::AlrState* proto_batch = event_stream->add_alr_states(); @@ -984,7 +977,7 @@ void RtcEventLogEncoderNewFormat::EncodeAlrState( } void RtcEventLogEncoderNewFormat::EncodeAudioNetworkAdaptation( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) return; @@ -1135,7 +1128,7 @@ void RtcEventLogEncoderNewFormat::EncodeAudioNetworkAdaptation( } void RtcEventLogEncoderNewFormat::EncodeAudioPlayout( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) return; @@ -1177,7 +1170,7 @@ void RtcEventLogEncoderNewFormat::EncodeAudioPlayout( } void RtcEventLogEncoderNewFormat::EncodeNetEqSetMinimumDelay( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (encode_neteq_set_minimum_delay_kill_switch_) { return; @@ -1235,7 +1228,7 @@ void RtcEventLogEncoderNewFormat::EncodeNetEqSetMinimumDelay( } void RtcEventLogEncoderNewFormat::EncodeAudioRecvStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventAudioReceiveStreamConfig* base_event : batch) { rtclog2::AudioRecvStreamConfig* proto_batch = @@ -1254,7 +1247,7 @@ void RtcEventLogEncoderNewFormat::EncodeAudioRecvStreamConfig( } void RtcEventLogEncoderNewFormat::EncodeAudioSendStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventAudioSendStreamConfig* base_event : batch) { rtclog2::AudioSendStreamConfig* proto_batch = @@ -1272,7 +1265,7 @@ void RtcEventLogEncoderNewFormat::EncodeAudioSendStreamConfig( } void RtcEventLogEncoderNewFormat::EncodeBweUpdateDelayBased( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) return; @@ -1329,7 +1322,7 @@ void RtcEventLogEncoderNewFormat::EncodeBweUpdateDelayBased( } void RtcEventLogEncoderNewFormat::EncodeBweUpdateLossBased( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) return; @@ -1393,7 +1386,7 @@ void RtcEventLogEncoderNewFormat::EncodeBweUpdateLossBased( } void RtcEventLogEncoderNewFormat::EncodeBweUpdateScream( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) return; @@ -1490,7 +1483,7 @@ void RtcEventLogEncoderNewFormat::EncodeBweUpdateScream( } void RtcEventLogEncoderNewFormat::EncodeDtlsTransportState( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventDtlsTransportState* base_event : batch) { rtclog2::DtlsTransportStateEvent* proto_batch = @@ -1502,7 +1495,7 @@ void RtcEventLogEncoderNewFormat::EncodeDtlsTransportState( } void RtcEventLogEncoderNewFormat::EncodeDtlsWritableState( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventDtlsWritableState* base_event : batch) { rtclog2::DtlsWritableState* proto_batch = @@ -1513,7 +1506,7 @@ void RtcEventLogEncoderNewFormat::EncodeDtlsWritableState( } void RtcEventLogEncoderNewFormat::EncodeProbeClusterCreated( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventProbeClusterCreated* base_event : batch) { rtclog2::BweProbeCluster* proto_batch = event_stream->add_probe_clusters(); @@ -1526,7 +1519,7 @@ void RtcEventLogEncoderNewFormat::EncodeProbeClusterCreated( } void RtcEventLogEncoderNewFormat::EncodeProbeResultFailure( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventProbeResultFailure* base_event : batch) { rtclog2::BweProbeResultFailure* proto_batch = @@ -1540,7 +1533,7 @@ void RtcEventLogEncoderNewFormat::EncodeProbeResultFailure( } void RtcEventLogEncoderNewFormat::EncodeProbeResultSuccess( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventProbeResultSuccess* base_event : batch) { rtclog2::BweProbeResultSuccess* proto_batch = @@ -1553,7 +1546,7 @@ void RtcEventLogEncoderNewFormat::EncodeProbeResultSuccess( } void RtcEventLogEncoderNewFormat::EncodeRouteChange( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventRouteChange* base_event : batch) { rtclog2::RouteChange* proto_batch = event_stream->add_route_changes(); @@ -1565,7 +1558,7 @@ void RtcEventLogEncoderNewFormat::EncodeRouteChange( } void RtcEventLogEncoderNewFormat::EncodeRemoteEstimate( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) return; @@ -1637,7 +1630,7 @@ void RtcEventLogEncoderNewFormat::EncodeRemoteEstimate( } void RtcEventLogEncoderNewFormat::EncodeRtcpPacketIncoming( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) { return; @@ -1646,7 +1639,7 @@ void RtcEventLogEncoderNewFormat::EncodeRtcpPacketIncoming( } void RtcEventLogEncoderNewFormat::EncodeRtcpPacketOutgoing( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) { return; @@ -1665,7 +1658,7 @@ void RtcEventLogEncoderNewFormat::EncodeRtpPacketIncoming( } void RtcEventLogEncoderNewFormat::EncodeFramesDecoded( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { if (batch.empty()) { return; @@ -1774,7 +1767,7 @@ void RtcEventLogEncoderNewFormat::EncodeRtpPacketOutgoing( } void RtcEventLogEncoderNewFormat::EncodeVideoRecvStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventVideoReceiveStreamConfig* base_event : batch) { rtclog2::VideoRecvStreamConfig* proto_batch = @@ -1794,7 +1787,7 @@ void RtcEventLogEncoderNewFormat::EncodeVideoRecvStreamConfig( } void RtcEventLogEncoderNewFormat::EncodeVideoSendStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventVideoSendStreamConfig* base_event : batch) { rtclog2::VideoSendStreamConfig* proto_batch = @@ -1813,7 +1806,7 @@ void RtcEventLogEncoderNewFormat::EncodeVideoSendStreamConfig( } void RtcEventLogEncoderNewFormat::EncodeIceCandidatePairConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventIceCandidatePairConfig* base_event : batch) { rtclog2::IceCandidatePairConfig* proto_batch = @@ -1842,7 +1835,7 @@ void RtcEventLogEncoderNewFormat::EncodeIceCandidatePairConfig( } void RtcEventLogEncoderNewFormat::EncodeIceCandidatePairEvent( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream) { for (const RtcEventIceCandidatePair* base_event : batch) { rtclog2::IceCandidatePairEvent* proto_batch = diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h index 57e6f8dea88..85a7515fb8e 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" @@ -73,66 +73,66 @@ class RtcEventLogEncoderNewFormat final : public RtcEventLogEncoder { private: // Encoding entry-point for the various RtcEvent subclasses. - void EncodeAlrState(ArrayView batch, + void EncodeAlrState(std::span batch, rtclog2::EventStream* event_stream); void EncodeAudioNetworkAdaptation( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); - void EncodeAudioPlayout(ArrayView batch, + void EncodeAudioPlayout(std::span batch, rtclog2::EventStream* event_stream); void EncodeAudioRecvStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeAudioSendStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeBweUpdateDelayBased( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeBweUpdateLossBased( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); - void EncodeBweUpdateScream(ArrayView batch, + void EncodeBweUpdateScream(std::span batch, rtclog2::EventStream* event_stream); void EncodeDtlsTransportState( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeDtlsWritableState( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); - void EncodeFramesDecoded(ArrayView batch, + void EncodeFramesDecoded(std::span batch, rtclog2::EventStream* event_stream); void EncodeIceCandidatePairConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeIceCandidatePairEvent( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); - void EncodeLoggingStarted(ArrayView batch, + void EncodeLoggingStarted(std::span batch, rtclog2::EventStream* event_stream); - void EncodeLoggingStopped(ArrayView batch, + void EncodeLoggingStopped(std::span batch, rtclog2::EventStream* event_stream); void EncodeNetEqSetMinimumDelay( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeProbeClusterCreated( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeProbeResultFailure( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeProbeResultSuccess( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); - void EncodeRouteChange(ArrayView batch, + void EncodeRouteChange(std::span batch, rtclog2::EventStream* event_stream); - void EncodeRemoteEstimate(ArrayView batch, + void EncodeRemoteEstimate(std::span batch, rtclog2::EventStream* event_stream); void EncodeRtcpPacketIncoming( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeRtcpPacketOutgoing( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeRtpPacketIncoming( const std::map>& @@ -143,16 +143,15 @@ class RtcEventLogEncoderNewFormat final : public RtcEventLogEncoder { batch, rtclog2::EventStream* event_stream); void EncodeVideoRecvStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); void EncodeVideoSendStreamConfig( - ArrayView batch, + std::span batch, rtclog2::EventStream* event_stream); template void EncodeRtpPacket(const Batch& batch, ProtoType* proto_batch); const bool encode_neteq_set_minimum_delay_kill_switch_; - const bool encode_dependency_descriptor_; }; } // namespace webrtc diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc index 9e604f03da5..409dfc0efba 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc @@ -23,7 +23,6 @@ #include #include -#include "api/field_trials.h" #include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/rtc_event_log/rtc_event_log.h" @@ -1308,23 +1307,6 @@ TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketOutgoing) { TestRtpPackets(*encoder); } -TEST_P(RtcEventLogEncoderTest, - RtcEventRtpPacketIncomingNoDependencyDescriptor) { - FieldTrials no_dd = CreateTestFieldTrials( - "WebRTC-RtcEventLogEncodeDependencyDescriptor/Disabled/"); - std::unique_ptr encoder = CreateEncoder(no_dd); - verifier_.ExpectDependencyDescriptorExtensionIsSet(false); - TestRtpPackets(*encoder); -} - -TEST_P(RtcEventLogEncoderTest, - RtcEventRtpPacketOutgoingNoDependencyDescriptor) { - FieldTrials no_dd = CreateTestFieldTrials( - "WebRTC-RtcEventLogEncodeDependencyDescriptor/Disabled/"); - std::unique_ptr encoder = CreateEncoder(no_dd); - verifier_.ExpectDependencyDescriptorExtensionIsSet(false); - TestRtpPackets(*encoder); -} // TODO(eladalon/terelius): Test with multiple events in the batch. TEST_P(RtcEventLogEncoderTest, RtcEventVideoReceiveStreamConfig) { diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h index d5d2b6e3424..c77c65a09ad 100644 --- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h +++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/encoder/rtc_event_log_encoder.h" @@ -39,7 +39,7 @@ class RtcEventLogEncoderV3 final : public RtcEventLogEncoder { private: std::map)>> + std::function)>> encoders_; }; diff --git a/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc index fa3fadde9bd..33530775ce9 100644 --- a/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc +++ b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "logging/rtc_event_log/events/rtc_event_field_extraction.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -29,12 +29,12 @@ namespace webrtc { FixedLengthEncodingParametersV3 FixedLengthEncodingParametersV3::CalculateParameters( uint64_t base, - const ArrayView values, + const std::span values, uint64_t value_bit_width, bool values_optional) { // As a special case, if all of the elements are identical to the base // we just encode the base value with a special delta header. - if (std::all_of(values.cbegin(), values.cend(), + if (std::all_of(values.begin(), values.end(), [base](uint64_t val) { return val == base; })) { // Delta header with signed=true and delta_bitwidth=64 return FixedLengthEncodingParametersV3(/*delta_bit_width=*/64, diff --git a/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h index 9e4f5298f4c..044e37e2994 100644 --- a/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h +++ b/logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "logging/rtc_event_log/events/rtc_event_field_extraction.h" namespace webrtc { @@ -34,7 +34,7 @@ class FixedLengthEncodingParametersV3 final { static FixedLengthEncodingParametersV3 CalculateParameters( uint64_t base, - ArrayView values, + std::span values, uint64_t value_bit_width, bool values_optional); static std::optional ParseDeltaHeader( diff --git a/logging/rtc_event_log/events/rtc_event_alr_state.h b/logging/rtc_event_log/events/rtc_event_alr_state.h index a7321873f72..09fc1295881 100644 --- a/logging/rtc_event_log/events/rtc_event_alr_state.h +++ b/logging/rtc_event_log/events/rtc_event_alr_state.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_definition.h" @@ -53,7 +53,7 @@ class RtcEventAlrState final : public RtcEvent { bool in_alr() const { return in_alr_; } - static std::string Encode(ArrayView batch) { + static std::string Encode(std::span batch) { return RtcEventAlrState::definition_.EncodeBatch(batch); } diff --git a/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h b/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h index ea78ef4b4ad..b0fa86e6bf0 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h +++ b/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -66,7 +66,7 @@ class RtcEventAudioNetworkAdaptation final : public RtcEvent { const std::optional& enable_dtx() const { return enable_dtx_; } const std::optional& num_channels() const { return num_channels_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_audio_playout.h b/logging/rtc_event_log/events/rtc_event_audio_playout.h index d9683ed449d..e05830d2868 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_playout.h +++ b/logging/rtc_event_log/events/rtc_event_audio_playout.h @@ -15,11 +15,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_definition.h" @@ -55,7 +55,7 @@ class RtcEventAudioPlayout final : public RtcEvent { uint32_t ssrc() const { return ssrc_; } - static std::string Encode(ArrayView batch) { + static std::string Encode(std::span batch) { return RtcEventAudioPlayout::definition_.EncodeBatch(batch); } diff --git a/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h b/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h index 5e747549725..965d974f205 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -53,7 +53,7 @@ class RtcEventAudioReceiveStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h b/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h index 861d067b03d..5978cf2d78e 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -53,7 +53,7 @@ class RtcEventAudioSendStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_begin_log.cc b/logging/rtc_event_log/events/rtc_event_begin_log.cc index acdb0f8bbed..1f3d290e4b0 100644 --- a/logging/rtc_event_log/events/rtc_event_begin_log.cc +++ b/logging/rtc_event_log/events/rtc_event_begin_log.cc @@ -11,11 +11,11 @@ #include "logging/rtc_event_log/events/rtc_event_begin_log.h" #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding.h" @@ -30,7 +30,7 @@ RtcEventBeginLog::RtcEventBeginLog(Timestamp timestamp, RtcEventBeginLog::~RtcEventBeginLog() = default; -std::string RtcEventBeginLog::Encode(ArrayView batch) { +std::string RtcEventBeginLog::Encode(std::span batch) { EventEncoder encoder(event_params_, batch); encoder.EncodeField( @@ -49,7 +49,7 @@ RtcEventLogParseStatus RtcEventBeginLog::Parse( if (!status.ok()) return status; - ArrayView output_batch = + std::span output_batch = ExtendLoggedBatch(output, parser.NumEventsInBatch()); constexpr FieldParameters timestamp_params{ @@ -57,7 +57,7 @@ RtcEventLogParseStatus RtcEventBeginLog::Parse( .field_id = FieldParameters::kTimestampField, .field_type = FieldType::kVarInt, .value_width = 64}; - RtcEventLogParseStatusOr> result = + RtcEventLogParseStatusOr> result = parser.ParseNumericField(timestamp_params); if (!result.ok()) return result.status(); diff --git a/logging/rtc_event_log/events/rtc_event_begin_log.h b/logging/rtc_event_log/events/rtc_event_begin_log.h index 0cbad4c821c..7136833d693 100644 --- a/logging/rtc_event_log/events/rtc_event_begin_log.h +++ b/logging/rtc_event_log/events/rtc_event_begin_log.h @@ -12,11 +12,11 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_BEGIN_LOG_H_ #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding.h" @@ -53,7 +53,7 @@ class RtcEventBeginLog final : public RtcEvent { Type GetType() const override { return kType; } bool IsConfigEvent() const override { return false; } - static std::string Encode(ArrayView batch); + static std::string Encode(std::span batch); static RtcEventLogParseStatus Parse(absl::string_view encoded_bytes, bool batched, diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h index 417d24828aa..0fb1041850b 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h @@ -15,11 +15,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/transport/bandwidth_usage.h" #include "api/units/timestamp.h" @@ -104,7 +104,7 @@ class RtcEventBweUpdateDelayBased final : public RtcEvent { int32_t bitrate_bps() const { return bitrate_bps_; } BandwidthUsage detector_state() const { return detector_state_; } - static std::string Encode(ArrayView batch) { + static std::string Encode(std::span batch) { return RtcEventBweUpdateDelayBased::definition_.EncodeBatch(batch); } diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h b/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h index b8548004a6f..9f10f93018c 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -64,7 +64,7 @@ class RtcEventBweUpdateLossBased final : public RtcEvent { uint8_t fraction_loss() const { return fraction_loss_; } int32_t total_packets() const { return total_packets_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_scream.h b/logging/rtc_event_log/events/rtc_event_bwe_update_scream.h index 538e7977f4f..0580ffa7c2a 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_scream.h +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_scream.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" @@ -82,7 +82,7 @@ class RtcEventBweUpdateScream final : public RtcEvent { uint32_t avg_queue_delay_ms() const { return avg_queue_delay_ms_; } uint32_t l4s_marked_permille() const { return l4s_marked_permille_; } - static std::string Encode(ArrayView batch) { + static std::string Encode(std::span batch) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_definition.h b/logging/rtc_event_log/events/rtc_event_definition.h index f924c7cdc73..0c221edfb3a 100644 --- a/logging/rtc_event_log/events/rtc_event_definition.h +++ b/logging/rtc_event_log/events/rtc_event_definition.h @@ -12,11 +12,11 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DEFINITION_H_ #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" @@ -35,8 +35,8 @@ struct RtcEventFieldDefinition { template class RtcEventDefinitionImpl { public: - void EncodeImpl(EventEncoder&, ArrayView) const {} - RtcEventLogParseStatus ParseImpl(EventParser&, ArrayView) const { + void EncodeImpl(EventEncoder&, std::span) const {} + RtcEventLogParseStatus ParseImpl(EventParser&, std::span) const { return RtcEventLogParseStatus::Success(); } }; @@ -51,15 +51,15 @@ class RtcEventDefinitionImpl { : field_(field), rest_(rest...) {} void EncodeImpl(EventEncoder& encoder, - ArrayView batch) const { + std::span batch) const { auto values = ExtractRtcEventMember(batch, field_.event_member); encoder.EncodeField(field_.params, values); rest_.EncodeImpl(encoder, batch); } RtcEventLogParseStatus ParseImpl(EventParser& parser, - ArrayView output_batch) const { - RtcEventLogParseStatusOr> result = + std::span output_batch) const { + RtcEventLogParseStatusOr> result = parser.ParseNumericField(field_.params); if (!result.ok()) return result.status(); @@ -106,7 +106,7 @@ class RtcEventDefinition { RtcEventFieldDefinition... fields) : params_(params), fields_(fields...) {} - std::string EncodeBatch(ArrayView batch) const { + std::string EncodeBatch(std::span batch) const { EventEncoder encoder(params_, batch); fields_.EncodeImpl(encoder, batch); return encoder.AsString(); @@ -120,7 +120,7 @@ class RtcEventDefinition { if (!status.ok()) return status; - ArrayView output_batch = + std::span output_batch = ExtendLoggedBatch(output, parser.NumEventsInBatch()); constexpr FieldParameters timestamp_params{ @@ -128,7 +128,7 @@ class RtcEventDefinition { .field_id = FieldParameters::kTimestampField, .field_type = FieldType::kVarInt, .value_width = 64}; - RtcEventLogParseStatusOr> result = + RtcEventLogParseStatusOr> result = parser.ParseNumericField(timestamp_params); if (!result.ok()) return result.status(); diff --git a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h index a2ffbcf62f9..aa821e1934b 100644 --- a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h +++ b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/dtls_transport_interface.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" @@ -50,7 +50,7 @@ class RtcEventDtlsTransportState : public RtcEvent { return dtls_transport_state_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h b/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h index f1da19f8dbb..4ebf693a9c2 100644 --- a/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h +++ b/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -50,7 +50,7 @@ class RtcEventDtlsWritableState : public RtcEvent { bool writable() const { return writable_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_end_log.cc b/logging/rtc_event_log/events/rtc_event_end_log.cc index e5455daff95..e912e800aa8 100644 --- a/logging/rtc_event_log/events/rtc_event_end_log.cc +++ b/logging/rtc_event_log/events/rtc_event_end_log.cc @@ -11,11 +11,11 @@ #include "logging/rtc_event_log/events/rtc_event_end_log.h" #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding.h" @@ -29,7 +29,7 @@ RtcEventEndLog::RtcEventEndLog(Timestamp timestamp) RtcEventEndLog::~RtcEventEndLog() = default; -std::string RtcEventEndLog::Encode(ArrayView batch) { +std::string RtcEventEndLog::Encode(std::span batch) { EventEncoder encoder(event_params_, batch); return encoder.AsString(); } @@ -43,7 +43,7 @@ RtcEventLogParseStatus RtcEventEndLog::Parse( if (!status.ok()) return status; - ArrayView output_batch = + std::span output_batch = ExtendLoggedBatch(output, parser.NumEventsInBatch()); constexpr FieldParameters timestamp_params{ @@ -51,7 +51,7 @@ RtcEventLogParseStatus RtcEventEndLog::Parse( .field_id = FieldParameters::kTimestampField, .field_type = FieldType::kVarInt, .value_width = 64}; - RtcEventLogParseStatusOr> result = + RtcEventLogParseStatusOr> result = parser.ParseNumericField(timestamp_params); if (!result.ok()) return result.status(); diff --git a/logging/rtc_event_log/events/rtc_event_end_log.h b/logging/rtc_event_log/events/rtc_event_end_log.h index bb7f7f4bb71..aba1043eb42 100644 --- a/logging/rtc_event_log/events/rtc_event_end_log.h +++ b/logging/rtc_event_log/events/rtc_event_end_log.h @@ -12,11 +12,11 @@ #define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_END_LOG_H_ #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding.h" @@ -46,7 +46,7 @@ class RtcEventEndLog final : public RtcEvent { Type GetType() const override { return kType; } bool IsConfigEvent() const override { return false; } - static std::string Encode(ArrayView batch); + static std::string Encode(std::span batch); static RtcEventLogParseStatus Parse(absl::string_view encoded_bytes, bool batched, diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding.cc b/logging/rtc_event_log/events/rtc_event_field_encoding.cc index e5b931d7e1e..b0f6290f8d3 100644 --- a/logging/rtc_event_log/events/rtc_event_field_encoding.cc +++ b/logging/rtc_event_log/events/rtc_event_field_encoding.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/encoder/bit_writer.h" #include "logging/rtc_event_log/encoder/var_int.h" @@ -26,6 +26,8 @@ #include "logging/rtc_event_log/events/rtc_event_field_extraction.h" #include "rtc_base/checks.h" +namespace webrtc { + using webrtc_event_logging::UnsignedDelta; namespace { @@ -60,8 +62,6 @@ std::string SerializeLittleEndian(uint64_t value, uint8_t bytes) { } // namespace -namespace webrtc { - std::string EncodeOptionalValuePositions(std::vector positions) { BitWriter writer((positions.size() + 7) / 8); for (bool position : positions) { @@ -107,7 +107,7 @@ std::optional ConvertFieldType(uint64_t value) { std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, uint64_t base, - ArrayView values) { + std::span values) { size_t outputbound = (values.size() * params.delta_bit_width() + 7) / 8; BitWriter writer(outputbound); @@ -142,7 +142,7 @@ std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, } EventEncoder::EventEncoder(EventParameters params, - ArrayView batch) { + std::span batch) { batch_size_ = batch.size(); if (!batch.empty()) { // Encode event type. @@ -212,9 +212,9 @@ void EventEncoder::EncodeField(const FieldParameters& params, const bool values_optional = values.size() != batch_size_; // Compute delta parameters - ArrayView all_values(values); + std::span all_values(values); uint64_t base = values[0]; - ArrayView remaining_values(all_values.subview(1)); + std::span remaining_values = all_values.subspan(1); FixedLengthEncodingParametersV3 delta_params = FixedLengthEncodingParametersV3::CalculateParameters( diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding.h b/logging/rtc_event_log/events/rtc_event_field_encoding.h index baa50b2e6d6..ba8427aeb0a 100644 --- a/logging/rtc_event_log/events/rtc_event_field_encoding.h +++ b/logging/rtc_event_log/events/rtc_event_field_encoding.h @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h" #include "logging/rtc_event_log/events/rtc_event_field_extraction.h" @@ -71,7 +71,7 @@ struct FieldParameters { // The EventEncoder is used to encode a batch of events. class EventEncoder { public: - EventEncoder(EventParameters params, ArrayView batch); + EventEncoder(EventParameters params, std::span batch); void EncodeField(const FieldParameters& params, const std::vector& values, @@ -94,7 +94,7 @@ class EventEncoder { std::string EncodeSingleValue(uint64_t value, FieldType field_type); std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, uint64_t base, - ArrayView values); + std::span values); // Given a batch of RtcEvents and a member pointer, extract that // member from each event in the batch. Signed integer members are @@ -107,7 +107,7 @@ std::string EncodeDeltasV3(FixedLengthEncodingParametersV3 params, template ::value, bool> = true> -std::vector ExtractRtcEventMember(ArrayView batch, +std::vector ExtractRtcEventMember(std::span batch, const T E::* member) { std::vector values; values.reserve(batch.size()); @@ -128,7 +128,7 @@ std::vector ExtractRtcEventMember(ArrayView batch, template ::value, bool> = true> -ValuesWithPositions ExtractRtcEventMember(ArrayView batch, +ValuesWithPositions ExtractRtcEventMember(std::span batch, const std::optional E::* member) { ValuesWithPositions result; result.position_mask.reserve(batch.size()); @@ -149,7 +149,7 @@ ValuesWithPositions ExtractRtcEventMember(ArrayView batch, template ::value, bool> = true> -std::vector ExtractRtcEventMember(ArrayView batch, +std::vector ExtractRtcEventMember(std::span batch, const T E::* member) { std::vector values; values.reserve(batch.size()); @@ -164,7 +164,7 @@ std::vector ExtractRtcEventMember(ArrayView batch, // Extract a string field from a batch of RtcEvents. template std::vector ExtractRtcEventMember( - ArrayView batch, + std::span batch, const std::string E::* member) { std::vector values; values.reserve(batch.size()); diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc index 006f5f1e657..ef97c46b611 100644 --- a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc +++ b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.cc @@ -1,4 +1,3 @@ - /* * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. * @@ -15,10 +14,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "logging/rtc_event_log/encoder/var_int.h" #include "logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding.h" @@ -27,27 +26,27 @@ #include "rtc_base/bitstream_reader.h" #include "rtc_base/checks.h" +namespace webrtc { + namespace { -std::optional ConvertFieldType(uint64_t value) { +std::optional ConvertFieldType(uint64_t value) { switch (value) { - case static_cast(webrtc::FieldType::kFixed8): - return webrtc::FieldType::kFixed8; - case static_cast(webrtc::FieldType::kFixed32): - return webrtc::FieldType::kFixed32; - case static_cast(webrtc::FieldType::kFixed64): - return webrtc::FieldType::kFixed64; - case static_cast(webrtc::FieldType::kVarInt): - return webrtc::FieldType::kVarInt; - case static_cast(webrtc::FieldType::kString): - return webrtc::FieldType::kString; + case static_cast(FieldType::kFixed8): + return FieldType::kFixed8; + case static_cast(FieldType::kFixed32): + return FieldType::kFixed32; + case static_cast(FieldType::kFixed64): + return FieldType::kFixed64; + case static_cast(FieldType::kVarInt): + return FieldType::kVarInt; + case static_cast(FieldType::kString): + return FieldType::kString; default: return std::nullopt; } } } // namespace -namespace webrtc { - uint64_t EventParser::ReadLittleEndian(uint8_t bytes) { RTC_DCHECK_LE(bytes, sizeof(uint64_t)); RTC_DCHECK_GE(bytes, 1); @@ -356,15 +355,15 @@ RtcEventLogParseStatus EventParser::ParseField(const FieldParameters& params) { return RtcEventLogParseStatus::Success(); } -RtcEventLogParseStatusOr> +RtcEventLogParseStatusOr> EventParser::ParseStringField(const FieldParameters& params, bool required_field) { - using StatusOr = RtcEventLogParseStatusOr>; + using StatusOr = RtcEventLogParseStatusOr>; RTC_DCHECK_EQ(params.field_type, FieldType::kString); auto status = ParseField(params); if (!status.ok()) return StatusOr(status); - ArrayView strings = GetStrings(); + std::span strings = GetStrings(); if (required_field && strings.size() != NumEventsInBatch()) { return StatusOr::Error("Required string field not found", __FILE__, __LINE__); @@ -372,15 +371,15 @@ EventParser::ParseStringField(const FieldParameters& params, return StatusOr(strings); } -RtcEventLogParseStatusOr> EventParser::ParseNumericField( +RtcEventLogParseStatusOr> EventParser::ParseNumericField( const FieldParameters& params, bool required_field) { - using StatusOr = RtcEventLogParseStatusOr>; + using StatusOr = RtcEventLogParseStatusOr>; RTC_DCHECK_NE(params.field_type, FieldType::kString); auto status = ParseField(params); if (!status.ok()) return StatusOr(status); - ArrayView values = GetValues(); + std::span values = GetValues(); if (required_field && values.size() != NumEventsInBatch()) { return StatusOr::Error("Required numerical field not found", __FILE__, __LINE__); diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h index d46c6ac5ac4..93b68cf392b 100644 --- a/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h +++ b/logging/rtc_event_log/events/rtc_event_field_encoding_parser.h @@ -14,13 +14,13 @@ #include #include #include +#include #include #include #include #include "absl/base/attributes.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/fixed_length_encoding_parameters_v3.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding.h" @@ -33,8 +33,8 @@ namespace webrtc { class EventParser { public: struct ValueAndPostionView { - ArrayView values; - ArrayView positions; + std::span values; + std::span positions; }; EventParser() = default; @@ -48,10 +48,10 @@ class EventParser { // other fields that may occur before it. If 'required_field == true', // then failing to find the field is an error, otherwise the functions // return success, but with an empty view of values. - RtcEventLogParseStatusOr> ParseStringField( + RtcEventLogParseStatusOr> ParseStringField( const FieldParameters& params, bool required_field = true); - RtcEventLogParseStatusOr> ParseNumericField( + RtcEventLogParseStatusOr> ParseNumericField( const FieldParameters& params, bool required_field = true); RtcEventLogParseStatusOr ParseOptionalNumericField( @@ -90,9 +90,9 @@ class EventParser { void SetError() { error_ = true; } bool Ok() const { return !error_; } - ArrayView GetValues() { return values_; } - ArrayView GetPositions() { return positions_; } - ArrayView GetStrings() { return strings_; } + std::span GetValues() { return values_; } + std::span GetPositions() { return positions_; } + std::span GetStrings() { return strings_; } void ClearTemporaries() { positions_.clear(); @@ -122,9 +122,9 @@ template ::value, bool> = true> ABSL_MUST_USE_RESULT RtcEventLogParseStatus -PopulateRtcEventMember(const ArrayView values, +PopulateRtcEventMember(const std::span values, T E::* member, - ArrayView output) { + std::span output) { size_t batch_size = values.size(); RTC_CHECK_EQ(output.size(), batch_size); for (size_t i = 0; i < batch_size; ++i) { @@ -138,10 +138,10 @@ template ::value, bool> = true> ABSL_MUST_USE_RESULT RtcEventLogParseStatus -PopulateRtcEventMember(const ArrayView positions, - const ArrayView values, +PopulateRtcEventMember(const std::span positions, + const std::span values, std::optional E::* member, - ArrayView output) { + std::span output) { size_t batch_size = positions.size(); RTC_CHECK_EQ(output.size(), batch_size); RTC_CHECK_LE(values.size(), batch_size); @@ -164,9 +164,9 @@ template ::value, bool> = true> ABSL_MUST_USE_RESULT RtcEventLogParseStatus -PopulateRtcEventMember(const ArrayView values, +PopulateRtcEventMember(const std::span values, T E::* member, - ArrayView output) { + std::span output) { size_t batch_size = values.size(); RTC_CHECK_EQ(output.size(), batch_size); for (size_t i = 0; i < batch_size; ++i) { @@ -182,9 +182,9 @@ PopulateRtcEventMember(const ArrayView values, // Same as above, but for string fields. template ABSL_MUST_USE_RESULT RtcEventLogParseStatus -PopulateRtcEventMember(const ArrayView values, +PopulateRtcEventMember(const std::span values, std::string E::* member, - ArrayView output) { + std::span output) { size_t batch_size = values.size(); RTC_CHECK_EQ(output.size(), batch_size); for (size_t i = 0; i < batch_size; ++i) { @@ -197,9 +197,9 @@ PopulateRtcEventMember(const ArrayView values, // N.B. Assumes that the encoded value uses millisecond precision. template ABSL_MUST_USE_RESULT RtcEventLogParseStatus -PopulateRtcEventTimestamp(const ArrayView& values, +PopulateRtcEventTimestamp(const std::span& values, Timestamp E::* timestamp, - ArrayView output) { + std::span output) { size_t batch_size = values.size(); RTC_CHECK_EQ(batch_size, output.size()); for (size_t i = 0; i < batch_size; ++i) { @@ -210,11 +210,11 @@ PopulateRtcEventTimestamp(const ArrayView& values, } template -ArrayView ExtendLoggedBatch(std::vector& output, size_t new_elements) { +std::span ExtendLoggedBatch(std::vector& output, size_t new_elements) { size_t old_size = output.size(); - output.insert(output.end(), old_size + new_elements, E()); - ArrayView output_batch = output; - output_batch.subview(old_size); + output.resize(old_size + new_elements); + std::span output_batch = output; + output_batch = output_batch.subspan(old_size); RTC_DCHECK_EQ(output_batch.size(), new_elements); return output_batch; } diff --git a/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc b/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc index 218197ace47..95a7a58c5c3 100644 --- a/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc +++ b/logging/rtc_event_log/events/rtc_event_field_encoding_unittest.cc @@ -14,13 +14,13 @@ #include #include #include +#include #include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/encoder/var_int.h" #include "logging/rtc_event_log/events/rtc_event_field_encoding_parser.h" @@ -366,8 +366,8 @@ class RtcEventFieldTest : public ::testing::Test { size_t size_before = parser_.RemainingBytes(); auto result = parser_.ParseOptionalNumericField(params); ASSERT_TRUE(result.ok()) << result.message().c_str(); - ArrayView values = result.value().values; - ArrayView positions = result.value().positions; + std::span values = result.value().values; + std::span positions = result.value().positions; ASSERT_EQ(positions.size(), expected_values.size()); auto value_it = values.begin(); for (size_t i = 0; i < expected_values.size(); i++) { @@ -396,8 +396,8 @@ class RtcEventFieldTest : public ::testing::Test { auto result = parser_.ParseOptionalNumericField(params, /*required_field=*/false); ASSERT_TRUE(result.ok()) << result.message().c_str(); - ArrayView values = result.value().values; - ArrayView positions = result.value().positions; + std::span values = result.value().values; + std::span positions = result.value().positions; EXPECT_EQ(positions.size(), 0u); EXPECT_EQ(values.size(), 0u); } diff --git a/logging/rtc_event_log/events/rtc_event_frame_decoded.h b/logging/rtc_event_log/events/rtc_event_frame_decoded.h index e7265458f28..c11664b959d 100644 --- a/logging/rtc_event_log/events/rtc_event_frame_decoded.h +++ b/logging/rtc_event_log/events/rtc_event_frame_decoded.h @@ -15,11 +15,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "api/video/video_codec_type.h" @@ -65,7 +65,7 @@ class RtcEventFrameDecoded final : public RtcEvent { VideoCodecType codec() const { return codec_; } uint8_t qp() const { return qp_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h index 3029c322d11..a935a3ae671 100644 --- a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h +++ b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -73,7 +73,7 @@ class RtcEventIceCandidatePair final : public RtcEvent { uint32_t candidate_pair_id() const { return candidate_pair_id_; } uint32_t transaction_id() const { return transaction_id_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h index 76bd1f7d354..097f789a56e 100644 --- a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h +++ b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" @@ -118,7 +118,7 @@ class RtcEventIceCandidatePairConfig final : public RtcEvent { return candidate_pair_desc_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h b/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h index c789257fc6c..f777f30e8a1 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h +++ b/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -69,7 +69,7 @@ class RtcEventProbeClusterCreated final : public RtcEvent { uint32_t min_probes() const { return min_probes_; } uint32_t min_bytes() const { return min_bytes_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_probe_result_failure.h b/logging/rtc_event_log/events/rtc_event_probe_result_failure.h index 5d90b774983..985757b9170 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_result_failure.h +++ b/logging/rtc_event_log/events/rtc_event_probe_result_failure.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -63,7 +63,7 @@ class RtcEventProbeResultFailure final : public RtcEvent { int32_t id() const { return id_; } ProbeFailureReason failure_reason() const { return failure_reason_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_probe_result_success.h b/logging/rtc_event_log/events/rtc_event_probe_result_success.h index 198c6066e1f..488cabe940d 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_result_success.h +++ b/logging/rtc_event_log/events/rtc_event_probe_result_success.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -56,7 +56,7 @@ class RtcEventProbeResultSuccess final : public RtcEvent { int32_t id() const { return id_; } int32_t bitrate_bps() const { return bitrate_bps_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_remote_estimate.h b/logging/rtc_event_log/events/rtc_event_remote_estimate.h index c1de1c6fa2d..fe4a49e18df 100644 --- a/logging/rtc_event_log/events/rtc_event_remote_estimate.h +++ b/logging/rtc_event_log/events/rtc_event_remote_estimate.h @@ -12,11 +12,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/data_rate.h" #include "api/units/timestamp.h" @@ -48,7 +48,7 @@ class RtcEventRemoteEstimate final : public RtcEvent { Type GetType() const override { return kType; } bool IsConfigEvent() const override { return false; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_route_change.h b/logging/rtc_event_log/events/rtc_event_route_change.h index 6eb8a9c3a64..4827ed47434 100644 --- a/logging/rtc_event_log/events/rtc_event_route_change.h +++ b/logging/rtc_event_log/events/rtc_event_route_change.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -53,7 +53,7 @@ class RtcEventRouteChange final : public RtcEvent { bool connected() const { return connected_; } uint32_t overhead() const { return overhead_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc b/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc index e5c834a1c60..8d0426a26ea 100644 --- a/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc +++ b/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.cc @@ -12,15 +12,15 @@ #include #include +#include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" namespace webrtc { RtcEventRtcpPacketIncoming::RtcEventRtcpPacketIncoming( - ArrayView packet) + std::span packet) : packet_(packet.data(), packet.size()) {} RtcEventRtcpPacketIncoming::RtcEventRtcpPacketIncoming( diff --git a/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h b/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h index 69c2e6b8522..c23ae218008 100644 --- a/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h +++ b/logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -30,7 +30,7 @@ class RtcEventRtcpPacketIncoming final : public RtcEvent { public: static constexpr Type kType = Type::RtcpPacketIncoming; - explicit RtcEventRtcpPacketIncoming(ArrayView packet); + explicit RtcEventRtcpPacketIncoming(std::span packet); ~RtcEventRtcpPacketIncoming() override; Type GetType() const override { return kType; } @@ -40,7 +40,7 @@ class RtcEventRtcpPacketIncoming final : public RtcEvent { const Buffer& packet() const { return packet_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc b/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc index 5926e5c5057..1b8472797c9 100644 --- a/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc +++ b/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.cc @@ -12,15 +12,15 @@ #include #include +#include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" namespace webrtc { RtcEventRtcpPacketOutgoing::RtcEventRtcpPacketOutgoing( - ArrayView packet) + std::span packet) : packet_(packet.data(), packet.size()) {} RtcEventRtcpPacketOutgoing::RtcEventRtcpPacketOutgoing( diff --git a/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h b/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h index 9916c94211b..4704fea037c 100644 --- a/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h +++ b/logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h @@ -14,11 +14,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -30,7 +30,7 @@ class RtcEventRtcpPacketOutgoing final : public RtcEvent { public: static constexpr Type kType = Type::RtcpPacketOutgoing; - explicit RtcEventRtcpPacketOutgoing(ArrayView packet); + explicit RtcEventRtcpPacketOutgoing(std::span packet); ~RtcEventRtcpPacketOutgoing() override; Type GetType() const override { return kType; } @@ -40,7 +40,7 @@ class RtcEventRtcpPacketOutgoing final : public RtcEvent { const Buffer& packet() const { return packet_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h b/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h index 03bb5ef626c..23cd3ae52a6 100644 --- a/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h +++ b/logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -47,8 +47,8 @@ class RtcEventRtpPacketIncoming final : public RtcEvent { size_t packet_length() const { return packet_.size(); } - ArrayView RawHeader() const { - return MakeArrayView(packet_.data(), header_length()); + std::span RawHeader() const { + return std::span(packet_.data(), header_length()); } uint32_t Ssrc() const { return packet_.Ssrc(); } uint32_t Timestamp() const { return packet_.Timestamp(); } @@ -60,7 +60,7 @@ class RtcEventRtpPacketIncoming final : public RtcEvent { return packet_.GetExtension(std::forward(args)...); } template - ArrayView GetRawExtension() const { + std::span GetRawExtension() const { return packet_.GetRawExtension(); } template @@ -75,7 +75,7 @@ class RtcEventRtpPacketIncoming final : public RtcEvent { return rtx_original_sequence_number_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h b/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h index bfe55fd3691..ce3931e3fa5 100644 --- a/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h +++ b/logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -48,8 +48,8 @@ class RtcEventRtpPacketOutgoing final : public RtcEvent { size_t packet_length() const { return packet_.size(); } - ArrayView RawHeader() const { - return MakeArrayView(packet_.data(), header_length()); + std::span RawHeader() const { + return std::span(packet_.data(), header_length()); } uint32_t Ssrc() const { return packet_.Ssrc(); } uint32_t Timestamp() const { return packet_.Timestamp(); } @@ -61,7 +61,7 @@ class RtcEventRtpPacketOutgoing final : public RtcEvent { return packet_.GetExtension(std::forward(args)...); } template - ArrayView GetRawExtension() const { + std::span GetRawExtension() const { return packet_.GetRawExtension(); } template @@ -77,7 +77,7 @@ class RtcEventRtpPacketOutgoing final : public RtcEvent { return rtx_original_sequence_number_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h b/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h index f8c222f88c8..c1c5103d2b2 100644 --- a/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -53,7 +53,7 @@ class RtcEventVideoReceiveStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h b/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h index 2d5b85c56f4..eeb08100692 100644 --- a/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h @@ -13,11 +13,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/units/timestamp.h" #include "logging/rtc_event_log/events/rtc_event_log_parse_status.h" @@ -53,7 +53,7 @@ class RtcEventVideoSendStreamConfig final : public RtcEvent { const rtclog::StreamConfig& config() const { return *config_; } - static std::string Encode(ArrayView /* batch */) { + static std::string Encode(std::span /* batch */) { // TODO(terelius): Implement return ""; } diff --git a/logging/rtc_event_log/rtc_event_log2rtp_dump.cc b/logging/rtc_event_log/rtc_event_log2rtp_dump.cc index 67b136e4c87..abe5a44d4ce 100644 --- a/logging/rtc_event_log/rtc_event_log2rtp_dump.cc +++ b/logging/rtc_event_log/rtc_event_log2rtp_dump.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,7 +21,6 @@ #include "absl/flags/parse.h" #include "absl/flags/usage.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" @@ -108,7 +108,7 @@ void ConvertRtpPacket( reconstructed_packet.SetTimestamp(incoming.rtp.header.timestamp); reconstructed_packet.SetSsrc(incoming.rtp.header.ssrc); if (incoming.rtp.header.numCSRCs > 0) { - reconstructed_packet.SetCsrcs(webrtc::ArrayView( + reconstructed_packet.SetCsrcs(std::span( incoming.rtp.header.arrOfCSRCs, incoming.rtp.header.numCSRCs)); } diff --git a/logging/rtc_event_log/rtc_event_log_impl.cc b/logging/rtc_event_log/rtc_event_log_impl.cc index 1cb0be9f191..2f3f1634cbb 100644 --- a/logging/rtc_event_log/rtc_event_log_impl.cc +++ b/logging/rtc_event_log/rtc_event_log_impl.cc @@ -67,7 +67,7 @@ RtcEventLogImpl::RtcEventLogImpl(const Environment& env, last_output_ms_(env_.clock().TimeInMilliseconds()), task_queue_(env_.task_queue_factory().CreateTaskQueue( "rtc_event_log", - TaskQueueFactory::Priority::NORMAL)) {} + TaskQueueFactory::Priority::kNormal)) {} RtcEventLogImpl::~RtcEventLogImpl() { // If we're logging to the output, this will stop that. Blocking function. diff --git a/logging/rtc_event_log/rtc_event_log_parser.cc b/logging/rtc_event_log/rtc_event_log_parser.cc index 79720d36bf3..2c4439b9656 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.cc +++ b/logging/rtc_event_log/rtc_event_log_parser.cc @@ -2658,6 +2658,25 @@ std::vector ParsedRtcEventLog::GetPacketInfos( PacketDirection::kOutgoingPacket); } process.ProcessEventsInOrder(); + + if (!ccfb_indices.empty()) { + // The log stores RTP packets by stream (with millisecond timestamps), but + // doesn't guarantee the order between packets sent or received at the same + // time on different SSRCs. We don't have transport sequence numbers when + // CCFB is used, so the order can't be reconstructed using those. + // process.ProcessEventsInOrder() will set log feedback time per packet + // based on when feedback was originally received/sent for a packet, and we + // can use that to ensure packets are at least ordered as originally seen in + // feedback. + std::stable_sort(packets.begin(), packets.end(), + [](const LoggedPacketInfo& a, const LoggedPacketInfo& b) { + if (a.log_packet_time == b.log_packet_time) { + return a.log_feedback_time < b.log_feedback_time; + } + return a.log_packet_time < b.log_packet_time; + }); + } + return packets; } diff --git a/logging/rtc_event_log/rtc_event_log_parser.h b/logging/rtc_event_log/rtc_event_log_parser.h index 4e97d910573..1b81e3c80fd 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.h +++ b/logging/rtc_event_log/rtc_event_log_parser.h @@ -388,7 +388,7 @@ class ParsedRtcEventLog { ~ParsedRtcEventLog(); - // Clears previously parsed events and resets the ParsedRtcEventLogNew to an + // Clears previously parsed events and resets the ParsedRtcEventLog to an // empty state. void Clear(); diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc index 719ec53d757..b711b7212e0 100644 --- a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc +++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/dtls_transport_interface.h" #include "api/rtc_event_log/rtc_event_log.h" @@ -87,7 +87,6 @@ #include "rtc_base/buffer.h" #include "rtc_base/checks.h" #include "rtc_base/random.h" -#include "rtc_base/time_utils.h" #include "system_wrappers/include/ntp_time.h" #include "test/gmock.h" #include "test/gtest.h" @@ -128,7 +127,7 @@ constexpr ExtensionPair kExtensions[kMaxNumExtensions] = { .name = RtpExtension::kDependencyDescriptorUri}}; template -void ShuffleInPlace(Random* prng, ArrayView array) { +void ShuffleInPlace(Random* prng, std::span array) { RTC_DCHECK_LE(array.size(), std::numeric_limits::max()); for (uint32_t i = 0; i + 1 < array.size(); i++) { uint32_t other = prng->Rand(i, static_cast(array.size() - 1)); @@ -238,13 +237,14 @@ std::unique_ptr EventGenerator::NewFrameDecodedEvent( constexpr VideoCodecType kCodecList[kNumCodecTypes] = { kVideoCodecGeneric, kVideoCodecVP8, kVideoCodecVP9, kVideoCodecAV1, kVideoCodecH264, kVideoCodecH265}; - const int64_t render_time_ms = - TimeMillis() + prng_.Rand(kMinRenderDelayMs, kMaxRenderDelayMs); + const Timestamp render_time = + clock_.CurrentTime() + + TimeDelta::Millis(prng_.Rand(kMinRenderDelayMs, kMaxRenderDelayMs)); const int width = prng_.Rand(kMinWidth, kMaxWidth); const int height = prng_.Rand(kMinHeight, kMaxHeight); const VideoCodecType codec = kCodecList[prng_.Rand(0, kNumCodecTypes - 1)]; const uint8_t qp = prng_.Rand(); - return Create(render_time_ms, ssrc, width, height, + return Create(render_time.ms(), ssrc, width, height, codec, qp); } @@ -657,7 +657,7 @@ RtpHeaderExtensionMap EventGenerator::NewRtpHeaderExtensionMap( std::vector id(RtpExtension::kOneByteHeaderExtensionMaxId - RtpExtension::kMinId + 1); std::iota(id.begin(), id.end(), RtpExtension::kMinId); - ShuffleInPlace(&prng_, ArrayView(id)); + ShuffleInPlace(&prng_, std::span(id)); auto not_excluded = [&](RTPExtensionType type) -> bool { return !absl::c_linear_search(excluded_extensions, type); @@ -1031,7 +1031,7 @@ void EventVerifier::VerifyLoggedDependencyDescriptor( const Event& packet, const std::vector& logged_dd) const { if (expect_dependency_descriptor_rtp_header_extension_is_set_) { - ArrayView original = + std::span original = packet.template GetRawExtension(); EXPECT_THAT(logged_dd, ElementsAreArray(original)); } else { diff --git a/logging/rtc_event_log/rtc_event_processor.h b/logging/rtc_event_log/rtc_event_processor.h index 846fbb842c4..66fbd7ae61f 100644 --- a/logging/rtc_event_log/rtc_event_processor.h +++ b/logging/rtc_event_log/rtc_event_processor.h @@ -104,7 +104,7 @@ class ProcessableEventList : public ProcessableEventListInterface { // the common base class. // // Usage example: -// ParsedRtcEventLogNew log; +// ParsedRtcEventLog log; // auto incoming_handler = [] (LoggedRtcpPacketIncoming elem) { ... }; // auto outgoing_handler = [] (LoggedRtcpPacketOutgoing elem) { ... }; // diff --git a/media/BUILD.gn b/media/BUILD.gn index c0c248c1ba6..dcdb269995b 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -69,7 +69,6 @@ rtc_library("rtc_media_base") { ":video_broadcaster", ":video_common", ":video_source_base", - "../api:array_view", "../api:audio_options_api", "../api:call_api", "../api:field_trials_view", @@ -218,7 +217,6 @@ rtc_library("video_common") { "base/video_common.h", ] deps = [ - "../api:array_view", "../rtc_base:stringutils", "../rtc_base:timeutils", "../rtc_base/system:rtc_export", @@ -238,7 +236,6 @@ rtc_library("media_engine") { ":rtc_media_config", ":stream_params", ":video_common", - "../api:array_view", "../api:audio_options_api", "../api:field_trials_view", "../api:rtc_error", @@ -257,6 +254,8 @@ rtc_library("media_engine") { "../api/video_codecs:scalability_mode_helper", "../call:call_interfaces", "../rtc_base:checks", + "../rtc_base:safe_compare", + "../rtc_base:safe_conversions", "../rtc_base:stringutils", "../rtc_base/system:file_wrapper", "//third_party/abseil-cpp/absl/algorithm:container", @@ -273,7 +272,6 @@ rtc_library("media_channel_impl") { ":media_channel", ":rtp_utils", ":stream_params", - "../api:array_view", "../api:audio_options_api", "../api:call_api", "../api:frame_transformer_interface", @@ -358,6 +356,7 @@ rtc_source_set("media_channel") { "../rtc_base:stringutils", "../rtc_base/network:sent_packet", "../video/config:encoder_config", + "//third_party/abseil-cpp/absl/container:flat_hash_map", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", @@ -376,6 +375,7 @@ rtc_library("codec") { ] deps = [ ":media_constants", + "../api:payload_type", "../api:rtp_parameters", "../api/audio_codecs:audio_codecs_api", "../api/video_codecs:scalability_mode", @@ -402,6 +402,7 @@ rtc_library("codec_list") { deps = [ ":codec", ":media_constants", + "../api:payload_type", "../api:rtc_error", "../rtc_base:checks", "../rtc_base:logging", @@ -416,7 +417,6 @@ rtc_library("rtp_utils") { ] deps = [ ":turn_utils", - "../api:array_view", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:async_packet_socket", "../rtc_base:byte_order", @@ -434,7 +434,6 @@ rtc_library("stream_params") { ] deps = [ ":rid_description", - "../api:array_view", "../rtc_base:checks", "../rtc_base:stringutils", "../rtc_base:unique_id_generator", @@ -485,7 +484,6 @@ rtc_library("rtc_simulcast_encoder_adapter") { deps = [ ":rtc_sdp_video_format_utils", ":video_common", - "../api:array_view", "../api:fec_controller_api", "../api:field_trials_view", "../api:scoped_refptr", @@ -505,13 +503,17 @@ rtc_library("rtc_simulcast_encoder_adapter") { "../api/video_codecs:video_codecs_api", "../common_video", "../media:media_constants", + "../modules:module_api_public", "../modules/video_coding:video_codec_interface", "../modules/video_coding:video_coding_utility", "../rtc_base:checks", "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:safe_conversions", "../rtc_base:stringutils", "../rtc_base/experiments:encoder_info_settings", "../rtc_base/experiments:rate_control_settings", + "../rtc_base/synchronization:mutex", "../rtc_base/system:no_unique_address", "../rtc_base/system:rtc_export", "../system_wrappers", @@ -590,13 +592,13 @@ rtc_library("rtc_audio_video") { ":rtc_media_config", ":rtp_utils", ":stream_params", - "../api:array_view", "../api:audio_options_api", "../api:call_api", "../api:field_trials_view", "../api:frame_transformer_interface", "../api:make_ref_counted", "../api:media_stream_interface", + "../api:payload_type", "../api:priority", "../api:rtc_error", "../api:rtp_headers", @@ -637,12 +639,14 @@ rtc_library("rtc_audio_video") { "../call:rtp_interfaces", "../call:video_receive_stream_api", "../call:video_send_stream_api", + "../common_video", "../common_video:frame_counts", "../modules/async_audio_processing", "../modules/audio_mixer:audio_mixer_impl", "../modules/rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", "../modules/video_coding/svc:scalability_mode_util", + "../rtc_base:callback_list", "../rtc_base:checks", "../rtc_base:dscp", "../rtc_base:event_tracer", @@ -724,7 +728,6 @@ if (rtc_build_dcsctp) { deps = [ ":media_channel", ":rtc_data_sctp_transport_internal", - "../api:array_view", "../api:data_channel_interface", "../api:dtls_transport_interface", "../api:field_trials_view", @@ -815,7 +818,6 @@ if (rtc_include_tests) { ":rtp_utils", ":stream_params", ":video_common", - "../api:array_view", "../api:audio_options_api", "../api:call_api", "../api:fec_controller_api", @@ -850,6 +852,7 @@ if (rtc_include_tests) { "../api/video:video_frame", "../api/video:video_frame_type", "../api/video:video_rtp_headers", + "../api/video:video_stream_encoder", "../api/video_codecs:scalability_mode", "../api/video_codecs:video_codecs_api", "../call:call_interfaces", @@ -870,10 +873,10 @@ if (rtc_include_tests) { "../rtc_base:async_packet_socket", "../rtc_base:buffer", "../rtc_base:byte_order", + "../rtc_base:callback_list", "../rtc_base:checks", "../rtc_base:copy_on_write_buffer", "../rtc_base:dscp", - "../rtc_base:gunit_helpers", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:network_route", @@ -957,7 +960,6 @@ if (rtc_include_tests) { ":stream_params", ":turn_utils", ":video_common", - "../api:array_view", "../api:audio_options_api", "../api:call_api", "../api:create_simulcast_test_fixture_api", @@ -970,6 +972,8 @@ if (rtc_include_tests) { "../api:mock_video_bitrate_allocator_factory", "../api:mock_video_codec_factory", "../api:mock_video_decoder", + "../api:mock_video_encoder", + "../api:payload_type", "../api:priority", "../api:ref_count", "../api:rtc_error", @@ -989,6 +993,7 @@ if (rtc_include_tests) { "../api/crypto:options", "../api/environment", "../api/environment:environment_factory", + "../api/task_queue", "../api/test/video:function_video_factory", "../api/transport:bitrate_settings", "../api/transport:datagram_transport_interface", @@ -1040,13 +1045,17 @@ if (rtc_include_tests) { "../rtc_base:byte_order", "../rtc_base:checks", "../rtc_base:copy_on_write_buffer", + "../rtc_base:cpu_info", "../rtc_base:dscp", + "../rtc_base:macromagic", + "../rtc_base:rtc_event", "../rtc_base:safe_conversions", "../rtc_base:socket", "../rtc_base:threading", "../rtc_base:timeutils", "../rtc_base:unique_id_generator", "../rtc_base/experiments:min_video_bitrate_experiment", + "../rtc_base/synchronization:mutex", "../rtc_base/system:file_wrapper", "../system_wrappers", "../test:audio_codec_mocks", @@ -1054,6 +1063,7 @@ if (rtc_include_tests) { "../test:create_test_field_trials", "../test:fake_video_codecs", "../test:rtp_test_utils", + "../test:run_loop", "../test:test_support", "../test:video_test_common", "../test/time_controller", @@ -1118,6 +1128,7 @@ if (rtc_include_tests) { "../net/dcsctp/public:factory", "../net/dcsctp/public:mocks", "../net/dcsctp/public:socket", + "../test:run_loop", ] } } diff --git a/media/DEPS b/media/DEPS index 10636cc5afe..60c0fb7472f 100644 --- a/media/DEPS +++ b/media/DEPS @@ -19,28 +19,28 @@ include_rules = [ ] specific_include_rules = { - "win32devicemanager\.cc": [ + "win32devicemanager\\.cc": [ "+third_party/logitech/files/logitechquickcam.h", ], - ".*webrtc_video_engine\.h": [ + ".*webrtc_video_engine\\.h": [ "+video/config", ], - ".*webrtc_video_engine\.cc": [ + ".*webrtc_video_engine\\.cc": [ "+video/config", ], - ".*media_channel\.h": [ + ".*media_channel\\.h": [ "+video/config", ], - ".*webrtc_video_engine_unittest\.cc": [ + ".*webrtc_video_engine_unittest\\.cc": [ "+video/config", ], - ".*fake_webrtc_call\.cc": [ + ".*fake_webrtc_call\\.cc": [ "+video/config", ], - ".*fake_webrtc_call\.h": [ + ".*fake_webrtc_call\\.h": [ "+video/config", ], - ".*codec\.h": [ + ".*codec\\.h": [ "+absl/strings/str_format.h", ], } diff --git a/media/OWNERS b/media/OWNERS index cf40b1c25d6..4003d48c49f 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -1,8 +1,6 @@ brandtr@webrtc.org ilnik@webrtc.org sprang@webrtc.org -magjed@webrtc.org -mflodman@webrtc.org perkj@webrtc.org # Audio-related changes: diff --git a/media/base/codec.cc b/media/base/codec.cc index 8379f9996b3..535fcd80a93 100644 --- a/media/base/codec.cc +++ b/media/base/codec.cc @@ -22,6 +22,7 @@ #include "absl/strings/str_cat.h" #include "api/audio_codecs/audio_format.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/rtp_parameters.h" #include "api/video_codecs/h264_profile_level_id.h" #include "api/video_codecs/sdp_video_format.h" @@ -97,10 +98,10 @@ bool FeedbackParams::HasDuplicateEntries() const { return false; } -Codec::Codec(Type type, int id, const std::string& name, int clockrate) +Codec::Codec(Type type, PayloadType id, const std::string& name, int clockrate) : Codec(type, id, name, clockrate, 0) {} Codec::Codec(Type type, - int id, + PayloadType id, const std::string& name, int clockrate, size_t channels) @@ -115,18 +116,22 @@ Codec::Codec(Type type, Codec::Codec(Type type) : Codec(type, - kIdNotSet, + PayloadType::NotSet(), "", type == Type::kVideo ? kDefaultVideoClockRateHz : kDefaultAudioClockRateHz) {} Codec::Codec(const SdpAudioFormat& c) - : Codec(Type::kAudio, kIdNotSet, c.name, c.clockrate_hz, c.num_channels) { + : Codec(Type::kAudio, + PayloadType::NotSet(), + c.name, + c.clockrate_hz, + c.num_channels) { params = c.parameters; } Codec::Codec(const SdpVideoFormat& c) - : Codec(Type::kVideo, kIdNotSet, c.name, kVideoCodecClockrate) { + : Codec(Type::kVideo, PayloadType::NotSet(), c.name, kVideoCodecClockrate) { params = c.parameters; scalability_modes = c.scalability_modes; } @@ -290,20 +295,25 @@ std::string Codec::ToString() const { return sb.str(); } -Codec CreateAudioRtxCodec(int rtx_payload_type, int associated_payload_type) { +Codec CreateAudioRtxCodec(PayloadType rtx_payload_type, + PayloadType associated_payload_type) { Codec rtx_codec = CreateAudioCodec(rtx_payload_type, kRtxCodecName, kDefaultAudioClockRateHz, 1); - rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type); + rtx_codec.SetParam(kCodecParamAssociatedPayloadType, + associated_payload_type.value()); return rtx_codec; } -Codec CreateVideoRtxCodec(int rtx_payload_type, int associated_payload_type) { +Codec CreateVideoRtxCodec(PayloadType rtx_payload_type, + PayloadType associated_payload_type) { Codec rtx_codec = CreateVideoCodec(rtx_payload_type, kRtxCodecName); - rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type); + rtx_codec.SetParam(kCodecParamAssociatedPayloadType, + associated_payload_type.value()); return rtx_codec; } -const Codec* FindCodecById(const std::vector& codecs, int payload_type) { +const Codec* FindCodecById(const std::vector& codecs, + PayloadType payload_type) { for (const auto& codec : codecs) { if (codec.id == payload_type) return &codec; @@ -395,7 +405,7 @@ void AddH264ConstrainedBaselineProfileToSupportedFormats( } } -Codec CreateAudioCodec(int id, +Codec CreateAudioCodec(PayloadType id, const std::string& name, int clockrate, size_t channels) { @@ -407,10 +417,10 @@ Codec CreateAudioCodec(const SdpAudioFormat& c) { } Codec CreateVideoCodec(const std::string& name) { - return CreateVideoCodec(Codec::kIdNotSet, name); + return CreateVideoCodec(PayloadType::NotSet(), name); } -Codec CreateVideoCodec(int id, const std::string& name) { +Codec CreateVideoCodec(PayloadType id, const std::string& name) { Codec c(Codec::Type::kVideo, id, name, kVideoCodecClockrate); if (absl::EqualsIgnoreCase(kH264CodecName, name)) { // This default is set for all H.264 codecs created because @@ -426,7 +436,7 @@ Codec CreateVideoCodec(const SdpVideoFormat& c) { return Codec(c); } -Codec CreateVideoCodec(int id, const SdpVideoFormat& sdp) { +Codec CreateVideoCodec(PayloadType id, const SdpVideoFormat& sdp) { Codec c = CreateVideoCodec(sdp); c.id = id; return c; diff --git a/media/base/codec.h b/media/base/codec.h index bc970c5a6b5..5f197441a9b 100644 --- a/media/base/codec.h +++ b/media/base/codec.h @@ -20,6 +20,7 @@ #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "api/audio_codecs/audio_format.h" +#include "api/payload_type.h" #include "api/rtp_parameters.h" #include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/sdp_video_format.h" @@ -87,7 +88,7 @@ struct RTC_EXPORT Codec { static const int kIdNotSet = -1; Type type; - int id; + PayloadType id; std::string name; int clockrate; @@ -173,7 +174,11 @@ struct RTC_EXPORT Codec { std::string ToString() const; // Default constructor, for initialization. - Codec() : Codec(Type::kAudio, kIdNotSet, "", kDefaultAudioClockRateHz) {} + Codec() + : Codec(Type::kAudio, + PayloadType::NotSet(), + "", + kDefaultAudioClockRateHz) {} Codec& operator=(const Codec& c); Codec& operator=(Codec&& c); @@ -183,7 +188,7 @@ struct RTC_EXPORT Codec { template friend void AbslStringify(Sink& sink, const Codec& c) { - absl::Format(&sink, "[%d:", c.id); + absl::Format(&sink, "[%v:", c.id); switch (c.type) { case Codec::Type::kAudio: sink.Append("audio/"); @@ -208,9 +213,9 @@ struct RTC_EXPORT Codec { // Creates an empty codec. explicit Codec(Type type); // Creates a codec with the given parameters. - Codec(Type type, int id, const std::string& name, int clockrate); + Codec(Type type, PayloadType id, const std::string& name, int clockrate); Codec(Type type, - int id, + PayloadType id, const std::string& name, int clockrate, size_t channels); @@ -218,36 +223,38 @@ struct RTC_EXPORT Codec { explicit Codec(const SdpAudioFormat& c); explicit Codec(const SdpVideoFormat& c); - friend Codec CreateAudioCodec(int id, + friend Codec CreateAudioCodec(PayloadType id, const std::string& name, int clockrate, size_t channels); friend Codec CreateAudioCodec(const SdpAudioFormat& c); - friend Codec CreateAudioRtxCodec(int rtx_payload_type, - int associated_payload_type); - friend Codec CreateVideoCodec(int id, const std::string& name); + friend Codec CreateAudioRtxCodec(PayloadType rtx_payload_type, + PayloadType associated_payload_type); + friend Codec CreateVideoCodec(PayloadType id, const std::string& name); friend Codec CreateVideoCodec(const SdpVideoFormat& c); - friend Codec CreateVideoRtxCodec(int rtx_payload_type, - int associated_payload_type); + friend Codec CreateVideoCodec(PayloadType id, const SdpVideoFormat& sdp); }; using Codecs = std::vector; -Codec CreateAudioCodec(int id, +Codec CreateAudioCodec(PayloadType id, const std::string& name, int clockrate, size_t channels); Codec CreateAudioCodec(const SdpAudioFormat& c); -Codec CreateAudioRtxCodec(int rtx_payload_type, int associated_payload_type); +Codec CreateAudioRtxCodec(PayloadType rtx_payload_type, + PayloadType associated_payload_type); Codec CreateVideoCodec(const std::string& name); -Codec CreateVideoCodec(int id, const std::string& name); +Codec CreateVideoCodec(PayloadType id, const std::string& name); Codec CreateVideoCodec(const SdpVideoFormat& c); -Codec CreateVideoCodec(int id, const SdpVideoFormat& sdp); -Codec CreateVideoRtxCodec(int rtx_payload_type, int associated_payload_type); +Codec CreateVideoCodec(PayloadType id, const SdpVideoFormat& sdp); +Codec CreateVideoRtxCodec(PayloadType rtx_payload_type, + PayloadType associated_payload_type); // Get the codec setting associated with `payload_type`. If there // is no codec associated with that payload type it returns nullptr. -const Codec* FindCodecById(const std::vector& codecs, int payload_type); +const Codec* FindCodecById(const std::vector& codecs, + PayloadType payload_type); bool HasLntf(const Codec& codec); bool HasNack(const Codec& codec); diff --git a/media/base/codec_comparators.cc b/media/base/codec_comparators.cc index 9da9d4aca54..3b4b198ad0b 100644 --- a/media/base/codec_comparators.cc +++ b/media/base/codec_comparators.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/rtp_parameters.h" #include "api/video_codecs/av1_profile.h" #include "api/video_codecs/h264_profile_level_id.h" @@ -59,12 +61,6 @@ std::string H264GetPacketizationModeOrDefault(const CodecParameterMap& params) { return GetFmtpParameterOrDefault(params, kH264FmtpPacketizationMode, "0"); } -bool H264IsSamePacketizationMode(const CodecParameterMap& left, - const CodecParameterMap& right) { - return H264GetPacketizationModeOrDefault(left) == - H264GetPacketizationModeOrDefault(right); -} - std::string AV1GetTierOrDefault(const CodecParameterMap& params) { // If the parameter is not present, the tier MUST be inferred to be 0. // https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters @@ -104,7 +100,7 @@ bool IsSameCodecSpecific(const std::string& name1, }; if (either_name_matches(kH264CodecName)) return H264IsSameProfile(params1, params2) && - H264IsSamePacketizationMode(params1, params2); + IsSameH264PacketizationMode(params1, params2); if (either_name_matches(kVp9CodecName)) return VP9IsSameProfile(params1, params2); // https://aomediacodec.github.io/av1-rtp-spec/#723-usage-with-the-sdp-offeranswer-model @@ -125,9 +121,9 @@ bool IsSameCodecSpecific(const std::string& name1, } bool ReferencedCodecsMatch(const std::vector& codecs1, - const int codec1_id, + const PayloadType codec1_id, const std::vector& codecs2, - const int codec2_id) { + const PayloadType codec2_id) { const Codec* codec1 = FindCodecById(codecs1, codec1_id); const Codec* codec2 = FindCodecById(codecs2, codec2_id); return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2); @@ -136,21 +132,25 @@ bool ReferencedCodecsMatch(const std::vector& codecs1, bool MatchesWithReferenceAttributesAndComparator( const Codec& codec_to_match, const Codec& potential_match, - absl::AnyInvocable reference_comparator) { + absl::AnyInvocable reference_comparator) { if (!MatchesWithCodecRules(codec_to_match, potential_match)) { return false; } Codec::ResiliencyType resiliency_type = codec_to_match.GetResiliencyType(); if (resiliency_type == Codec::ResiliencyType::kRtx) { - int apt_value_1 = 0; - int apt_value_2 = 0; + int apt_value_1_int = 0; + int apt_value_2_int = 0; if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType, - &apt_value_1) || + &apt_value_1_int) || !potential_match.GetParam(kCodecParamAssociatedPayloadType, - &apt_value_2)) { + &apt_value_2_int)) { RTC_LOG(LS_WARNING) << "RTX missing associated payload type."; return false; } + PayloadType apt_value_1 = + PayloadType(static_cast(apt_value_1_int)); + PayloadType apt_value_2 = + PayloadType(static_cast(apt_value_2_int)); if (reference_comparator(apt_value_1, apt_value_2)) { return true; } @@ -163,15 +163,7 @@ bool MatchesWithReferenceAttributesAndComparator( potential_match.params.find(kCodecParamNotInNameValueFormat); bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end(); bool has_parameters_2 = red_parameters_2 != potential_match.params.end(); - // If codec_to_match has unassigned PT and no parameter, - // we assume that it'll be assigned later and return a match. - // Note - this should be deleted. It's untidy. - if (potential_match.id == Codec::kIdNotSet && !has_parameters_2) { - return true; - } - if (codec_to_match.id == Codec::kIdNotSet && !has_parameters_1) { - return true; - } + if (has_parameters_1 && has_parameters_2) { // Different levels of redundancy between offer and answer are OK // since RED is considered to be declarative. @@ -203,7 +195,16 @@ bool MatchesWithReferenceAttributesAndComparator( return true; } if (!has_parameters_1 && !has_parameters_2) { - // Both parameters are missing. Happens for video RED. + return true; + } + // Exactly one lacks parameters. + // Allow match if it is an audio RED codec and at least one of the + // codecs has an unassigned payload type or they have the same ID. + if (codec_to_match.type == Codec::Type::kAudio && + codec_to_match.name == kRedCodecName && + (codec_to_match.id == PayloadType::NotSet() || + potential_match.id == PayloadType::NotSet() || + codec_to_match.id == potential_match.id)) { return true; } return false; @@ -295,9 +296,15 @@ bool MatchesWithCodecRules(const Codec& left_codec, const Codec& right_codec) { right_codec.id <= kLowerDynamicRangeMax) || (right_codec.id >= kUpperDynamicRangeMin && right_codec.id <= kUpperDynamicRangeMax); + + if (left_codec.type != right_codec.type) { + return false; + } + bool matches_id; if ((is_id_in_dynamic_range && is_codec_id_in_dynamic_range) || - left_codec.id == Codec::kIdNotSet || right_codec.id == Codec::kIdNotSet) { + left_codec.id == PayloadType::NotSet() || + right_codec.id == PayloadType::NotSet()) { matches_id = absl::EqualsIgnoreCase(left_codec.name, right_codec.name); } else { matches_id = (left_codec.id == right_codec.id); @@ -330,7 +337,7 @@ bool MatchesWithCodecRules(const Codec& left_codec, const Codec& right_codec) { bool MatchesWithReferenceAttributes(const Codec& codec1, const Codec& codec2) { return MatchesWithReferenceAttributesAndComparator( - codec1, codec2, [](int a, int b) { return a == b; }); + codec1, codec2, [](PayloadType a, PayloadType b) { return a == b; }); } // Finds a codec in `codecs2` that matches `codec_to_match`, which is @@ -348,7 +355,7 @@ std::optional FindMatchingCodec(const std::vector& codecs1, for (const Codec& potential_match : codecs2) { if (MatchesWithReferenceAttributesAndComparator( codec_to_match, potential_match, - [&codecs1, &codecs2](int a, int b) { + [&codecs1, &codecs2](PayloadType a, PayloadType b) { return ReferencedCodecsMatch(codecs1, a, codecs2, b); })) { return potential_match; @@ -360,12 +367,22 @@ std::optional FindMatchingCodec(const std::vector& codecs1, bool IsSameRtpCodec(const Codec& codec, const RtpCodec& rtp_codec) { RtpCodecParameters rtp_codec2 = codec.ToCodecParameters(); - return absl::EqualsIgnoreCase(rtp_codec.name, rtp_codec2.name) && - rtp_codec.kind == rtp_codec2.kind && - rtp_codec.num_channels == rtp_codec2.num_channels && - rtp_codec.clock_rate == rtp_codec2.clock_rate && - InsertDefaultParams(rtp_codec.name, rtp_codec.parameters) == - InsertDefaultParams(rtp_codec2.name, rtp_codec2.parameters); + if (!absl::EqualsIgnoreCase(rtp_codec.name, rtp_codec2.name) || + rtp_codec.kind != rtp_codec2.kind || + rtp_codec.num_channels != rtp_codec2.num_channels || + rtp_codec.clock_rate != rtp_codec2.clock_rate) { + return false; + } + + // audio/RED should ignore the parameters which specify payload types so + // can not be compared. + if (rtp_codec.kind == MediaType::AUDIO && + absl::EqualsIgnoreCase(rtp_codec.name, kRedCodecName)) { + return true; + } + + return InsertDefaultParams(rtp_codec.name, rtp_codec.parameters) == + InsertDefaultParams(rtp_codec2.name, rtp_codec2.parameters); } bool IsSameRtpCodecIgnoringLevel(const Codec& codec, @@ -393,11 +410,18 @@ bool IsSameRtpCodecIgnoringLevel(const Codec& codec, } // audio/RED should ignore the parameters which specify payload types so // can not be compared. - if (rtp_codec.kind == MediaType::AUDIO && rtp_codec.name == kRedCodecName) { + if (rtp_codec.kind == MediaType::AUDIO && + absl::EqualsIgnoreCase(rtp_codec.name, kRedCodecName)) { return true; } return params1 == params2; } +bool IsSameH264PacketizationMode(const CodecParameterMap& left, + const CodecParameterMap& right) { + return H264GetPacketizationModeOrDefault(left) == + H264GetPacketizationModeOrDefault(right); +} + } // namespace webrtc diff --git a/media/base/codec_comparators.h b/media/base/codec_comparators.h index 1d242b119d1..bf143596e37 100644 --- a/media/base/codec_comparators.h +++ b/media/base/codec_comparators.h @@ -44,6 +44,11 @@ bool IsSameRtpCodec(const Codec& codec, const RtpCodec& rtp_codec); // Similar to `IsSameRtpCodec` but ignoring the level related parameter. bool IsSameRtpCodecIgnoringLevel(const Codec& codec, const RtpCodec& rtp_codec); + +// Returns true if the two codecs have the same H.264 packetization-mode. +bool IsSameH264PacketizationMode(const CodecParameterMap& left, + const CodecParameterMap& right); + } // namespace webrtc #endif // MEDIA_BASE_CODEC_COMPARATORS_H_ diff --git a/media/base/codec_comparators_unittest.cc b/media/base/codec_comparators_unittest.cc index 2f78042c16a..048765670ed 100644 --- a/media/base/codec_comparators_unittest.cc +++ b/media/base/codec_comparators_unittest.cc @@ -105,6 +105,63 @@ TEST(CodecComparatorsTest, MatchesWithReferenceAttributesRed) { EXPECT_FALSE(MatchesWithReferenceAttributes(codec_1, codec_5)); } +TEST(CodecComparatorsTest, RedParametersMismatch) { + Codec with_params = CreateAudioCodec(101, kRedCodecName, 48000, 2); + with_params.SetParam(kCodecParamNotInNameValueFormat, "111/111"); + + Codec no_params = CreateAudioCodec(102, kRedCodecName, 48000, 2); + + // Case 1: Exactly one lacks parameters, both have IDs -> SHOULD NOT match for + // audio RED. + EXPECT_FALSE(MatchesWithReferenceAttributes(with_params, no_params)); + EXPECT_FALSE(MatchesWithReferenceAttributes(no_params, with_params)); + + // Case 2: Exactly one lacks parameters, the one WITHOUT parameters is + // unassigned -> SHOULD match. + // This is the case with late assignment. + Codec no_params_unassigned = no_params; + no_params_unassigned.id = Codec::kIdNotSet; + EXPECT_TRUE( + MatchesWithReferenceAttributes(with_params, no_params_unassigned)); + EXPECT_TRUE( + MatchesWithReferenceAttributes(no_params_unassigned, with_params)); + + // Case 3: Exactly one lacks parameters, the one WITH parameters is + // unassigned -> SHOULD match for audio RED because one side is unassigned. + Codec with_params_unassigned = with_params; + with_params_unassigned.id = Codec::kIdNotSet; + EXPECT_TRUE( + MatchesWithReferenceAttributes(with_params_unassigned, no_params)); + EXPECT_TRUE( + MatchesWithReferenceAttributes(no_params, with_params_unassigned)); + + // Case 4: Exactly one lacks parameters, both are unassigned -> SHOULD match. + EXPECT_TRUE(MatchesWithReferenceAttributes(with_params_unassigned, + no_params_unassigned)); + EXPECT_TRUE(MatchesWithReferenceAttributes(no_params_unassigned, + with_params_unassigned)); + + // Case 5: Both lack parameters -> SHOULD match. + Codec no_params_2 = CreateAudioCodec(103, kRedCodecName, 48000, 2); + EXPECT_TRUE(MatchesWithReferenceAttributes(no_params, no_params_2)); + + // A video RED codec should not match any audio RED codec, + // independent of parameters. + Codec video_red_codec = CreateVideoCodec(107, kRedCodecName); + EXPECT_FALSE(MatchesWithReferenceAttributes(no_params, video_red_codec)); + EXPECT_FALSE(MatchesWithReferenceAttributes(with_params, video_red_codec)); + + // Two video RED codecs with different IDs and one lacking parameters + // should NOT match. + Codec video_red_with_params = CreateVideoCodec(108, kRedCodecName); + video_red_with_params.SetParam(kCodecParamNotInNameValueFormat, "120/120"); + Codec video_red_no_params = CreateVideoCodec(109, kRedCodecName); + EXPECT_FALSE(MatchesWithReferenceAttributes(video_red_with_params, + video_red_no_params)); + EXPECT_FALSE(MatchesWithReferenceAttributes(video_red_no_params, + video_red_with_params)); +} + struct TestParams { std::string name; SdpVideoFormat codec1; diff --git a/media/base/codec_list.cc b/media/base/codec_list.cc index c82b7209ef2..a293244b11b 100644 --- a/media/base/codec_list.cc +++ b/media/base/codec_list.cc @@ -14,6 +14,7 @@ #include #include +#include "api/payload_type.h" #include "api/rtc_error.h" #include "media/base/codec.h" #include "media/base/media_constants.h" @@ -32,14 +33,14 @@ RTCError CheckInputConsistency(const std::vector& codecs) { // that there are no duplicates. for (size_t i = 0; i < codecs.size(); i++) { const Codec& codec = codecs[i]; - if (codec.id != Codec::kIdNotSet) { - auto [it, success] = pt_to_index.insert({codec.id, i}); + if (codec.id != PayloadType::NotSet()) { + auto [it, success] = pt_to_index.insert({codec.id, static_cast(i)}); if (!success) { RTC_LOG(LS_ERROR) << "Duplicate payload type in codec list, " << codec << " and " << codecs[it->second] << " have the same ID"; - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Duplicate payload type in codec list"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Duplicate payload type in codec list"); } } } @@ -56,7 +57,7 @@ RTCError CheckInputConsistency(const std::vector& codecs) { // TODO: https://issues.webrtc.org/384756622 - reject codec earlier and // enable check. RTC_DCHECK(apt_it != codec.params.end()); Until that is // fixed: - if (codec.id == Codec::kIdNotSet) { + if (codec.id == PayloadType::NotSet()) { // Should not have an apt parameter. if (apt_it != codec.params.end()) { RTC_LOG(LS_WARNING) << "Surprising condition: RTX codec without " @@ -74,18 +75,18 @@ RTCError CheckInputConsistency(const std::vector& codecs) { if (!(FromString(apt_it->second, &associated_pt))) { RTC_LOG(LS_ERROR) << "Non-numeric argument to rtx apt: " << codec << " apt=" << apt_it->second; - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Non-numeric argument to rtx apt parameter"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Non-numeric argument to rtx apt parameter"); } - if (codec.id != Codec::kIdNotSet && + if (codec.id != PayloadType::NotSet() && pt_to_index.count(associated_pt) != 1) { RTC_LOG(LS_WARNING) << "Surprising condition: RTX codec APT not found: " << codec << " points to a PT that occurs " << pt_to_index.count(associated_pt) << " times"; - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "PT pointed to by rtx apt parameter does not exist"); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "PT pointed to by rtx apt parameter does not exist"); } // const Codec& referred_codec = codecs[pt_to_index[associated_pt]]; // Not true: diff --git a/media/base/fake_media_engine.cc b/media/base/fake_media_engine.cc index a406b2a36cf..2603c7b4bc5 100644 --- a/media/base/fake_media_engine.cc +++ b/media/base/fake_media_engine.cc @@ -20,6 +20,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/strings/match.h" #include "api/audio/audio_device.h" #include "api/audio_options.h" @@ -173,6 +174,16 @@ bool FakeVoiceMediaReceiveChannel::GetStats( bool /* get_and_clear_legacy_stats */) { return false; } +absl::AnyInvocable()> +FakeVoiceMediaReceiveChannel::GetStatsCallback(bool reset_legacy) { + return [this, reset_legacy]() -> std::optional { + VoiceMediaReceiveInfo info; + if (GetStats(&info, reset_legacy)) { + return info; + } + return std::nullopt; + }; +} void FakeVoiceMediaReceiveChannel::SetRawAudioSink( uint32_t /* ssrc */, std::unique_ptr sink) { @@ -318,6 +329,16 @@ bool FakeVoiceMediaSendChannel::GetOutputVolume(uint32_t ssrc, double* volume) { bool FakeVoiceMediaSendChannel::GetStats(VoiceMediaSendInfo* /* info */) { return false; } +absl::AnyInvocable()> +FakeVoiceMediaSendChannel::GetStatsCallback() { + return [this]() -> std::optional { + VoiceMediaSendInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; +} bool FakeVoiceMediaSendChannel::SetSendCodecs( const std::vector& codecs) { if (fail_set_send_codecs()) { @@ -419,6 +440,16 @@ void FakeVideoMediaSendChannel::FillBitrateInfo( bool FakeVideoMediaSendChannel::GetStats(VideoMediaSendInfo* /* info */) { return false; } +absl::AnyInvocable()> +FakeVideoMediaSendChannel::GetStatsCallback() { + return [this]() -> std::optional { + VideoMediaSendInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; +} bool FakeVideoMediaSendChannel::SetSendCodecs( const std::vector& codecs) { if (fail_set_send_codecs()) { @@ -559,6 +590,16 @@ void FakeVideoMediaReceiveChannel::RequestRecvKeyFrame(uint32_t /* ssrc */) {} bool FakeVideoMediaReceiveChannel::GetStats(VideoMediaReceiveInfo* /* info */) { return false; } +absl::AnyInvocable()> +FakeVideoMediaReceiveChannel::GetStatsCallback() { + return [this]() -> std::optional { + VideoMediaReceiveInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; +} FakeVoiceEngine::FakeVoiceEngine() : encoder_factory_(make_ref_counted(this)), @@ -654,7 +695,9 @@ FakeVideoEngine::CreateSendChannel( const MediaConfig& /* config */, const VideoOptions& options, const CryptoOptions& /* crypto_options */, - VideoBitrateAllocatorFactory* /* video_bitrate_allocator_factory */) { + VideoBitrateAllocatorFactory* /* video_bitrate_allocator_factory */, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + /* video_encoder_switch_request_callback */) { std::unique_ptr ch = std::make_unique(options, call->network_thread()); diff --git a/media/base/fake_media_engine.h b/media/base/fake_media_engine.h index b72d5edbb85..acff856d740 100644 --- a/media/base/fake_media_engine.h +++ b/media/base/fake_media_engine.h @@ -128,8 +128,6 @@ class RtpReceiveChannelHelper : public Base, public MediaChannelUtil { std::optional GetUnsignaledSsrc() const override { return std::nullopt; } - void ChooseReceiverReportSsrc( - const std::set& /* choices */) override {} virtual bool SetLocalSsrc(const StreamParams& /* sp */) { return true; } void OnDemuxerCriteriaUpdatePending() override {} @@ -213,7 +211,7 @@ class RtpReceiveChannelHelper : public Base, public MediaChannelUtil { const MediaChannelParameters::RtcpParameters& params) { recv_rtcp_parameters_ = params; } - void OnPacketReceived(const RtpPacketReceived& packet) override { + void OnPacketReceived(RtpPacketReceived packet) override { rtp_packets_.push_back( std::string(packet.Buffer().cdata(), packet.size())); } @@ -339,6 +337,11 @@ class RtpSendChannelHelper : public Base, public MediaChannelUtil { } return RtpParameters(); } + + absl::AnyInvocable GetRtpSendParametersCallback() + const override { + return [this](uint32_t ssrc) { return GetRtpSendParameters(ssrc); }; + } RTCError SetRtpSendParameters(uint32_t ssrc, const RtpParameters& parameters, SetParametersCallback callback) override { @@ -349,14 +352,8 @@ class RtpSendChannelHelper : public Base, public MediaChannelUtil { if (!result.ok()) { return InvokeSetParametersCallback(callback, result); } - bool changed = (parameters_iterator->second != parameters); parameters_iterator->second = parameters; - // Invoke the callback if set. - if (changed && on_rtp_send_parameters_changed_callback_) { - on_rtp_send_parameters_changed_callback_(ssrc, parameters); - } - InvokeSetParametersCallback(callback, RTCError::OK()); - return RTCError::OK(); + return InvokeSetParametersCallback(callback, RTCError::OK()); } // Replicate the behavior of the real media channel: return false // when setting parameters for unknown SSRCs. @@ -408,14 +405,6 @@ class RtpSendChannelHelper : public Base, public MediaChannelUtil { rtcp_packets_.push_back(std::string(packet->cdata(), packet->size())); } - // Stuff that deals with encryptors, transformers and the like - void SetOnRtpSendParametersChanged( - absl::AnyInvocable, const RtpParameters&)> - callback) override { - RTC_DCHECK(!on_rtp_send_parameters_changed_callback_); - on_rtp_send_parameters_changed_callback_ = std::move(callback); - } - void SetFrameEncryptor(uint32_t /* ssrc */, scoped_refptr /* frame_encryptor */) override {} @@ -488,8 +477,6 @@ class RtpSendChannelHelper : public Base, public MediaChannelUtil { MediaChannelNetworkInterface* network_interface_ = nullptr; absl::AnyInvocable&)> ssrc_list_changed_callback_ = nullptr; - absl::AnyInvocable, const RtpParameters&)> - on_rtp_send_parameters_changed_callback_; }; class FakeVoiceMediaReceiveChannel @@ -537,14 +524,16 @@ class FakeVoiceMediaReceiveChannel bool GetStats(VoiceMediaReceiveInfo* info, bool get_and_clear_legacy_stats) override; + absl::AnyInvocable()> GetStatsCallback( + bool reset_legacy) override; void SetRawAudioSink(uint32_t ssrc, std::unique_ptr sink) override; void SetDefaultRawAudioSink( std::unique_ptr sink) override; - ::webrtc::RtcpMode RtcpMode() const override { return recv_rtcp_mode_; } - void SetRtcpMode(::webrtc::RtcpMode mode) override { recv_rtcp_mode_ = mode; } + webrtc::RtcpMode RtcpMode() const override { return recv_rtcp_mode_; } + void SetRtcpMode(webrtc::RtcpMode mode) override { recv_rtcp_mode_ = mode; } std::vector GetSources(uint32_t ssrc) const override; void SetReceiveNackEnabled(bool /* enabled */) override {} void SetReceiveNonSenderRttEnabled(bool /* enabled */) override {} @@ -580,7 +569,7 @@ class FakeVoiceMediaReceiveChannel std::map> local_sinks_; std::unique_ptr sink_; int max_bps_; - ::webrtc::RtcpMode recv_rtcp_mode_ = RtcpMode::kCompound; + webrtc::RtcpMode recv_rtcp_mode_ = RtcpMode::kCompound; }; class FakeVoiceMediaSendChannel @@ -629,6 +618,8 @@ class FakeVoiceMediaSendChannel std::optional GetSendCodec() const override; bool GetStats(VoiceMediaSendInfo* stats) override; + absl::AnyInvocable()> GetStatsCallback() + override; private: class VoiceChannelAudioSink : public AudioSource::Sink { @@ -713,6 +704,8 @@ class FakeVideoMediaReceiveChannel void ClearRecordableEncodedFrameCallback(uint32_t ssrc) override; void RequestRecvKeyFrame(uint32_t ssrc) override; bool GetStats(VideoMediaReceiveInfo* info) override; + absl::AnyInvocable()> GetStatsCallback() + override; bool AddDefaultRecvStreamForTesting(const StreamParams& /* sp */) override { RTC_CHECK_NOTREACHED(); @@ -767,15 +760,14 @@ class FakeVideoMediaSendChannel void GenerateSendKeyFrame(uint32_t ssrc, const std::vector& rids) override; - RtcpMode SendCodecRtcpMode() const override { return RtcpMode::kCompound; } void SetSsrcListChangedCallback( absl::AnyInvocable&)> /* callback */) override {} - bool SendCodecHasLntf() const override { return false; } bool SendCodecHasNack() const override { return false; } - std::optional SendCodecRtxTime() const override { return std::nullopt; } bool GetStats(VideoMediaSendInfo* info) override; + absl::AnyInvocable()> GetStatsCallback() + override; bool SetOptions(const VideoOptions& options) override; private: @@ -907,7 +899,9 @@ class FakeVideoEngine : public VideoEngineInterface { const MediaConfig& config, const VideoOptions& options, const CryptoOptions& crypto_options, - VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) override; + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + video_encoder_switch_request_callback = nullptr) override; std::unique_ptr CreateReceiveChannel( const Environment& env, Call* call, diff --git a/media/base/fake_network_interface.h b/media/base/fake_network_interface.h index 1967ff627cf..4573c43ca85 100644 --- a/media/base/fake_network_interface.h +++ b/media/base/fake_network_interface.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -190,7 +191,9 @@ class FakeNetworkInterface : public MediaChannelNetworkInterface { private: void SetRtpSsrc(uint32_t ssrc, CopyOnWriteBuffer& buffer) { RTC_CHECK_GE(buffer.size(), 12); - SetBE32(buffer.MutableData() + 8, ssrc); + SetBE32( + std::span(buffer.MutableData(), buffer.size()).subspan(8, 4), + ssrc); } void GetNumRtpBytesAndPackets(uint32_t ssrc, int* bytes, int* packets) { diff --git a/media/base/media_channel.h b/media/base/media_channel.h index 49b85e87ae0..8b362573acf 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -22,6 +22,7 @@ #include #include +#include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -68,8 +69,8 @@ namespace webrtc { class VideoFrame; struct VideoFormat; -webrtc::RTCError InvokeSetParametersCallback(SetParametersCallback& callback, - RTCError error); +RTCError InvokeSetParametersCallback(SetParametersCallback& callback, + RTCError error); class VideoMediaSendChannelInterface; class VideoMediaReceiveChannelInterface; @@ -227,22 +228,6 @@ class MediaSendChannelInterface { const RtpParameters& parameters, SetParametersCallback callback = nullptr) = 0; - // Sets a callback that will be invoked whenever the RTP parameters are - // changed. The callback is invoked on the worker thread. - // - // This callback is intended to inform the RtpSender that parameters of - // its associated media channel have changed including due to functions - // invoked on the channel object outside of the RtpSender. Once the Channel - // object has been merged with the RtpTransceiver, and MediaSendChannel is - // consistently accessed through the RtpSender, we should be able to remove - // this callback. - // - // Note for implementations: It's important that the callback is not invoked - // if the parameters haven't actually changed. - virtual void SetOnRtpSendParametersChanged( - absl::AnyInvocable, const RtpParameters&)> - callback) = 0; - virtual void SetEncoderToPacketizerFrameTransformer( uint32_t ssrc, scoped_refptr frame_transformer) = 0; @@ -253,10 +238,26 @@ class MediaSendChannelInterface { uint32_t /* ssrc */, VideoEncoderFactory::EncoderSelectorInterface* /* encoder_selector */) {} virtual RtpParameters GetRtpSendParameters(uint32_t ssrc) const = 0; + // Returns a callback that is to be used to retrieve RTP send parameters + // in the same way as `GetRtpSendParameters` does. + // The difference between GetRtpSendParametersCallback and + // `GetRtpSendParameters` is that GetRtpSendParametersCallback is callable + // from the signaling thread and allows binding the `alive` state of the + // channel with the returned callback object. The callback object itself + // however must be called on the worker thread, but does not require the + // caller to have a pointer to the channel. This avoids running into a race + // conditions during asynchronous teardown where signaling thread and worker + // thread state may be torn down asynchronously. + virtual absl::AnyInvocable + GetRtpSendParametersCallback() const = 0; virtual bool SendCodecHasNack() const = 0; // Called whenever the list of sending SSRCs changes. virtual void SetSsrcListChangedCallback( absl::AnyInvocable&)> callback) = 0; + // Called whenever the parameters change autonomously on the worker thread. + // This is used for cache invalidation on the signaling thread. + virtual void SetParametersChangedCallback( + absl::AnyInvocable callback) {} }; class MediaReceiveChannelInterface { @@ -281,11 +282,9 @@ class MediaReceiveChannelInterface { // Sets the abstract interface class for sending RTP/RTCP data. virtual void SetInterface(MediaChannelNetworkInterface* iface) = 0; // Called on the network when an RTP packet is received. - virtual void OnPacketReceived(const RtpPacketReceived& packet) = 0; + virtual void OnPacketReceived(RtpPacketReceived packet) = 0; // Gets the current unsignaled receive stream's SSRC, if there is one. virtual std::optional GetUnsignaledSsrc() const = 0; - // Sets the local SSRC for listening to incoming RTCP reports. - virtual void ChooseReceiverReportSsrc(const std::set& choices) = 0; // This is currently a workaround because of the demuxer state being managed // across two separate threads. Once the state is consistently managed on // the same thread (network), this workaround can be removed. @@ -614,7 +613,7 @@ struct VideoSenderInfo : public MediaSenderInfo { std::optional qp_sum; VideoContentType content_type = VideoContentType::UNSPECIFIED; // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-psnrsum - webrtc::EncodedImage::Psnr psnr_sum; + EncodedImage::Psnr psnr_sum; uint32_t psnr_measurements = 0; uint32_t frames_sent = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent @@ -719,7 +718,8 @@ struct BandwidthEstimationInfo { }; // Maps from payload type to `RtpCodecParameters`. -typedef std::map RtpCodecParametersMap; +typedef absl::flat_hash_map + RtpCodecParametersMap; // Stats returned from VoiceMediaSendChannel.GetStats() struct VoiceMediaSendInfo { @@ -923,6 +923,13 @@ class VoiceMediaSendChannelInterface : public MediaSendChannelInterface { // DTMF event 0-9, *, #, A-D. virtual bool InsertDtmf(uint32_t ssrc, int event, int duration) = 0; virtual bool GetStats(VoiceMediaSendInfo* stats) = 0; + // Returns a callback that can be used to retrieve stats. + // The purpose is to allow binding state as it exists on the signaling thread + // to a callback that is run on the worker thread. This avoids race conditions + // during asynchronous teardown where signaling thread and worker thread state + // may be torn down asynchronously. + virtual absl::AnyInvocable()> + GetStatsCallback() = 0; virtual bool SenderNackEnabled() const = 0; virtual bool SenderNonSenderRttEnabled() const = 0; virtual bool SetOptions(const AudioOptions& options) = 0; @@ -948,6 +955,13 @@ class VoiceMediaReceiveChannelInterface : public MediaReceiveChannelInterface { virtual void SetDefaultRawAudioSink( std::unique_ptr sink) = 0; virtual bool GetStats(VoiceMediaReceiveInfo* stats, bool reset_legacy) = 0; + // Returns a callback that can be used to retrieve stats. + // The purpose is to allow binding state as it exists on the signaling thread + // to a callback that is run on the worker thread. This avoids race conditions + // during asynchronous teardown where signaling thread and worker thread state + // may be torn down asynchronously. + virtual absl::AnyInvocable()> + GetStatsCallback(bool reset_legacy) = 0; virtual enum RtcpMode RtcpMode() const = 0; virtual void SetRtcpMode(enum RtcpMode mode) = 0; virtual void SetReceiveNackEnabled(bool enabled) = 0; @@ -974,6 +988,10 @@ struct VideoReceiverParameters : MediaChannelParameters {}; class VideoMediaSendChannelInterface : public MediaSendChannelInterface { public: + using EncoderSwitchRequestAction = absl::AnyInvocable; + using EncoderSwitchRequestCallback = + absl::AnyInvocable; + virtual bool SetSenderParameters(const VideoSenderParameters& params) = 0; // Starts or stops transmission (and potentially capture) of local video. virtual bool SetSend(bool send) = 0; @@ -987,6 +1005,13 @@ class VideoMediaSendChannelInterface : public MediaSendChannelInterface { virtual void GenerateSendKeyFrame(uint32_t ssrc, const std::vector& rids) = 0; virtual bool GetStats(VideoMediaSendInfo* stats) = 0; + // Returns a callback that can be used to retrieve stats. + // The purpose is to allow binding state as it exists on the signaling thread + // to a callback that is run on the worker thread. This avoids race conditions + // during asynchronous teardown where signaling thread and worker thread state + // may be torn down asynchronously. + virtual absl::AnyInvocable()> + GetStatsCallback() = 0; // This fills the "bitrate parts" (rtx, video bitrate) of the // BandwidthEstimationInfo, since that part that isn't possible to get // through Call::GetStats, as they are statistics of the send @@ -996,10 +1021,6 @@ class VideoMediaSendChannelInterface : public MediaSendChannelInterface { // so that it's getting the send stream stats separately by calling // GetStats(), and merges with BandwidthEstimationInfo by itself. virtual void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) = 0; - // Information queries to support SetReceiverFeedbackParameters - virtual RtcpMode SendCodecRtcpMode() const = 0; - virtual bool SendCodecHasLntf() const = 0; - virtual std::optional SendCodecRtxTime() const = 0; }; class VideoMediaReceiveChannelInterface : public MediaReceiveChannelInterface { @@ -1028,6 +1049,13 @@ class VideoMediaReceiveChannelInterface : public MediaReceiveChannelInterface { // Clear recordable encoded frame callback for `ssrc` virtual void ClearRecordableEncodedFrameCallback(uint32_t ssrc) = 0; virtual bool GetStats(VideoMediaReceiveInfo* stats) = 0; + // Returns a callback that can be used to retrieve stats. + // The purpose is to allow binding state as it exists on the signaling thread + // to a callback that is run on the worker thread. This avoids race conditions + // during asynchronous teardown where signaling thread and worker thread state + // may be torn down asynchronously. + virtual absl::AnyInvocable()> + GetStatsCallback() = 0; virtual bool AddDefaultRecvStreamForTesting(const StreamParams& sp) = 0; virtual void StartReceive(uint32_t ssrc) {} virtual void StopReceive(uint32_t ssrc) {} diff --git a/media/base/media_channel_impl.cc b/media/base/media_channel_impl.cc index d8f3b41988f..cb74a2710e4 100644 --- a/media/base/media_channel_impl.cc +++ b/media/base/media_channel_impl.cc @@ -12,11 +12,11 @@ #include #include +#include #include #include #include "absl/functional/any_invocable.h" -#include "api/array_view.h" #include "api/audio_options.h" #include "api/call/transport.h" #include "api/media_stream_interface.h" @@ -190,7 +190,7 @@ MediaChannelUtil::TransportForMediaChannels::TranslatePacketOptions( } bool MediaChannelUtil::TransportForMediaChannels::SendRtcp( - ArrayView packet, + std::span packet, const PacketOptions& options) { auto send = [this, packet = CopyOnWriteBuffer(packet, kMaxRtpPacketLen), options]() mutable { @@ -206,7 +206,7 @@ bool MediaChannelUtil::TransportForMediaChannels::SendRtcp( } bool MediaChannelUtil::TransportForMediaChannels::SendRtp( - ArrayView packet, + std::span packet, const PacketOptions& options) { auto send = [this, packet = CopyOnWriteBuffer(packet, kMaxRtpPacketLen), options]() mutable { diff --git a/media/base/media_channel_impl.h b/media/base/media_channel_impl.h index 958018beb8b..9826cdf25e1 100644 --- a/media/base/media_channel_impl.h +++ b/media/base/media_channel_impl.h @@ -14,7 +14,8 @@ #include #include -#include "api/array_view.h" +#include + #include "api/call/transport.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -89,9 +90,9 @@ class MediaChannelUtil { ~TransportForMediaChannels() override; // Implementation of Transport - bool SendRtp(ArrayView packet, + bool SendRtp(std::span packet, const PacketOptions& options) override; - bool SendRtcp(ArrayView packet, + bool SendRtcp(std::span packet, const PacketOptions& options) override; // Not implementation of Transport diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc index 1382b07aa10..27fbdbc55fd 100644 --- a/media/base/media_engine.cc +++ b/media/base/media_engine.cc @@ -10,29 +10,30 @@ #include "media/base/media_engine.h" -#include - +#include #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/rtc_error.h" +#include "api/rtp_headers.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/video/video_codec_constants.h" #include "api/video_codecs/scalability_mode.h" +#include "api/video_codecs/scalability_mode_helper.h" #include "media/base/codec.h" #include "media/base/codec_comparators.h" #include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "rtc_base/checks.h" -#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_compare.h" namespace webrtc { namespace { @@ -41,10 +42,9 @@ bool SupportsMode(const Codec& codec, if (!scalability_mode.has_value()) { return true; } - return absl::c_any_of( - codec.scalability_modes, [&](webrtc::ScalabilityMode mode) { - return ScalabilityModeToString(mode) == *scalability_mode; - }); + return absl::c_any_of(codec.scalability_modes, [&](ScalabilityMode mode) { + return ScalabilityModeToString(mode) == *scalability_mode; + }); } } // namespace @@ -67,7 +67,7 @@ RtpParameters CreateRtpParametersWithEncodings(StreamParams sp) { } const std::vector& rids = sp.rids(); - RTC_DCHECK(rids.size() == 0 || rids.size() == encoding_count); + RTC_DCHECK(rids.empty() || rids.size() == encoding_count); for (size_t i = 0; i < rids.size(); ++i) { encodings[i].rid = rids[i].rid; } @@ -80,7 +80,7 @@ RtpParameters CreateRtpParametersWithEncodings(StreamParams sp) { std::vector GetDefaultEnabledRtpHeaderExtensions( const RtpHeaderExtensionQueryInterface& query_interface, - const webrtc::FieldTrialsView* field_trials) { + const FieldTrialsView* field_trials) { std::vector extensions; for (const auto& entry : query_interface.GetRtpHeaderExtensions(field_trials)) { @@ -91,27 +91,8 @@ std::vector GetDefaultEnabledRtpHeaderExtensions( } RTCError CheckScalabilityModeValues(const RtpParameters& rtp_parameters, - ArrayView send_codecs, + std::span send_codecs, std::optional send_codec) { - using webrtc::RTCErrorType; - - RTC_LOG(LS_ERROR) << "CheckScalabilityModeValues: Starting check with " - << send_codecs.size() << " send_codecs"; - - // Log all available codecs and their scalability modes for debugging - for (size_t idx = 0; idx < send_codecs.size(); ++idx) { - const webrtc::Codec& codec = send_codecs[idx]; - RTC_LOG(LS_ERROR) << "CheckScalabilityModeValues: Available codec[" << idx - << "] name=" << codec.name - << " payload_type=" << codec.id - << " scalability_modes_count=" << codec.scalability_modes.size(); - for (size_t mode_idx = 0; mode_idx < codec.scalability_modes.size(); ++mode_idx) { - std::string mode_string = std::string(ScalabilityModeToString(codec.scalability_modes[mode_idx])); - RTC_LOG(LS_ERROR) << "CheckScalabilityModeValues: available_mode[" << mode_idx - << "] = '" << mode_string << "'"; - } - } - if (send_codecs.empty()) { // This is an audio sender or an extra check in the stack where the codec // list is not available and we can't check the scalability_mode values. @@ -121,7 +102,7 @@ RTCError CheckScalabilityModeValues(const RtpParameters& rtp_parameters, for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) { if (rtp_parameters.encodings[i].codec) { bool codecFound = false; - for (const webrtc::Codec& codec : send_codecs) { + for (const Codec& codec : send_codecs) { if (IsSameRtpCodecIgnoringLevel(codec, *rtp_parameters.encodings[i].codec) && SupportsMode(codec, rtp_parameters.encodings[i].scalability_mode)) { @@ -131,35 +112,19 @@ RTCError CheckScalabilityModeValues(const RtpParameters& rtp_parameters, } } if (!codecFound) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Attempted to use an unsupported codec for layer " + - std::to_string(i)); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to use an unsupported codec for layer " + << i); } } if (rtp_parameters.encodings[i].scalability_mode) { if (!send_codec) { bool scalabilityModeFound = false; - RTC_LOG(LS_INFO) << "CheckScalabilityModeValues: Checking scalability mode '" - << *rtp_parameters.encodings[i].scalability_mode - << "' for encoding " << i << " against " << send_codecs.size() << " send_codecs"; - - for (size_t codec_idx = 0; codec_idx < send_codecs.size(); ++codec_idx) { - const webrtc::Codec& codec = send_codecs[codec_idx]; - RTC_LOG(LS_INFO) << "CheckScalabilityModeValues: Checking codec[" << codec_idx - << "] name=" << codec.name - << " payload_type=" << codec.id - << " with " << codec.scalability_modes.size() << " scalability modes"; - - for (size_t mode_idx = 0; mode_idx < codec.scalability_modes.size(); ++mode_idx) { - const auto& scalability_mode = codec.scalability_modes[mode_idx]; - std::string mode_string = std::string(ScalabilityModeToString(scalability_mode)); - RTC_LOG(LS_INFO) << "CheckScalabilityModeValues: mode[" << mode_idx - << "] = '" << mode_string << "'"; - - if (mode_string == *rtp_parameters.encodings[i].scalability_mode) { + for (const Codec& codec : send_codecs) { + for (const auto& scalability_mode : codec.scalability_modes) { + if (ScalabilityModeToString(scalability_mode) == + *rtp_parameters.encodings[i].scalability_mode) { scalabilityModeFound = true; - RTC_LOG(LS_INFO) << "CheckScalabilityModeValues: Found matching scalability mode!"; break; } } @@ -168,44 +133,25 @@ RTCError CheckScalabilityModeValues(const RtpParameters& rtp_parameters, } if (!scalabilityModeFound) { - RTC_LOG(LS_ERROR) << "CheckScalabilityModeValues: scalabilityModeFound is false. " - << "Requested scalability mode '" << *rtp_parameters.encodings[i].scalability_mode - << "' not found in any of the " << send_codecs.size() << " send_codecs"; - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Attempted to set RtpParameters scalabilityMode " - "to an unsupported value for the current codecs."); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to set RtpParameters scalabilityMode " + "to an unsupported value for the current codecs."); } } else { bool scalabilityModeFound = false; - RTC_LOG(LS_INFO) << "CheckScalabilityModeValues: Checking scalability mode '" - << *rtp_parameters.encodings[i].scalability_mode - << "' for encoding " << i << " against send_codec name=" - << send_codec->name << " payload_type=" << send_codec->id - << " with " << send_codec->scalability_modes.size() << " scalability modes"; - - for (size_t mode_idx = 0; mode_idx < send_codec->scalability_modes.size(); ++mode_idx) { - const auto& scalability_mode = send_codec->scalability_modes[mode_idx]; - std::string mode_string = std::string(ScalabilityModeToString(scalability_mode)); - RTC_LOG(LS_INFO) << "CheckScalabilityModeValues: send_codec mode[" << mode_idx - << "] = '" << mode_string << "'"; - - if (mode_string == *rtp_parameters.encodings[i].scalability_mode) { + for (const auto& scalability_mode : send_codec->scalability_modes) { + if (ScalabilityModeToString(scalability_mode) == + *rtp_parameters.encodings[i].scalability_mode) { scalabilityModeFound = true; - RTC_LOG(LS_INFO) << "CheckScalabilityModeValues: Found matching scalability mode in send_codec!"; break; } } - if (!scalabilityModeFound) { - RTC_LOG(LS_ERROR) << "CheckScalabilityModeValues: scalabilityModeFound is false. " - << "Requested scalability mode '" << *rtp_parameters.encodings[i].scalability_mode - << "' not found in send_codec name=" << send_codec->name - << " payload_type=" << send_codec->id; - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Attempted to set RtpParameters scalabilityMode " - "to an unsupported value for the current codecs."); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to set RtpParameters scalabilityMode " + "to an unsupported value for the current codecs."); } } } @@ -215,47 +161,45 @@ RTCError CheckScalabilityModeValues(const RtpParameters& rtp_parameters, } RTCError CheckRtpParametersValues(const RtpParameters& rtp_parameters, - ArrayView send_codecs, + std::span send_codecs, std::optional send_codec, const FieldTrialsView& field_trials) { - using webrtc::RTCErrorType; - bool has_scale_resolution_down_to = false; for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) { if (rtp_parameters.encodings[i].bitrate_priority <= 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, - "Attempted to set RtpParameters bitrate_priority to " - "an invalid number. bitrate_priority must be > 0."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_RANGE) + << "Attempted to set RtpParameters bitrate_priority to " + "an invalid number. bitrate_priority must be > 0."); } if (rtp_parameters.encodings[i].scale_resolution_down_by && *rtp_parameters.encodings[i].scale_resolution_down_by < 1.0) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_RANGE, - "Attempted to set RtpParameters scale_resolution_down_by to an " - "invalid value. scale_resolution_down_by must be >= 1.0"); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_RANGE) + << "Attempted to set RtpParameters scale_resolution_down_by to an " + "invalid value. scale_resolution_down_by must be >= 1.0"); } if (rtp_parameters.encodings[i].max_framerate && *rtp_parameters.encodings[i].max_framerate < 0.0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, - "Attempted to set RtpParameters max_framerate to an " - "invalid value. max_framerate must be >= 0.0"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_RANGE) + << "Attempted to set RtpParameters max_framerate to an " + "invalid value. max_framerate must be >= 0.0"); } if (rtp_parameters.encodings[i].min_bitrate_bps && rtp_parameters.encodings[i].max_bitrate_bps) { if (*rtp_parameters.encodings[i].max_bitrate_bps < *rtp_parameters.encodings[i].min_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, - "Attempted to set RtpParameters min bitrate " - "larger than max bitrate."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_RANGE) + << "Attempted to set RtpParameters min bitrate " + "larger than max bitrate."); } } if (rtp_parameters.encodings[i].num_temporal_layers) { if (*rtp_parameters.encodings[i].num_temporal_layers < 1 || - *rtp_parameters.encodings[i].num_temporal_layers > - webrtc::kMaxTemporalStreams) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, - "Attempted to set RtpParameters " - "num_temporal_layers to an invalid number."); + SafeGt(*rtp_parameters.encodings[i].num_temporal_layers, + kMaxTemporalStreams)) { + return LOG_ERROR(RTCError(RTCErrorType::INVALID_RANGE) + << "Attempted to set RtpParameters " + "num_temporal_layers to an invalid number."); } } @@ -263,30 +207,63 @@ RTCError CheckRtpParametersValues(const RtpParameters& rtp_parameters, has_scale_resolution_down_to = true; if (rtp_parameters.encodings[i].scale_resolution_down_to->width <= 0 || rtp_parameters.encodings[i].scale_resolution_down_to->height <= 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "The resolution dimensions must be positive."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "The resolution dimensions must be positive."); } } if (!field_trials.IsEnabled("WebRTC-MixedCodecSimulcast")) { if (i > 0 && rtp_parameters.encodings[i - 1].codec != rtp_parameters.encodings[i].codec) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "Attempted to use different codec values for " - "different encodings."); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Attempted to use different codec values for " + "different encodings."); } } + + if (rtp_parameters.encodings[i].csrcs.has_value() && + rtp_parameters.encodings[i].csrcs.value().size() > kRtpCsrcSize) { + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_RANGE) + << "Attempted to set more than the maximum allowed number of CSRCs."); + } + + if (i > 0 && rtp_parameters.encodings[i - 1].csrcs != + rtp_parameters.encodings[i].csrcs) { + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to set different CSRCs for different " + "encodings."); + } } if (has_scale_resolution_down_to && absl::c_any_of(rtp_parameters.encodings, - [](const webrtc::RtpEncodingParameters& encoding) { + [](const RtpEncodingParameters& encoding) { return encoding.active && !encoding.scale_resolution_down_to.has_value(); })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "If a resolution is specified on any encoding then " - "it must be specified on all encodings."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "If a resolution is specified on any encoding then " + "it must be specified on all encodings."); + } + + // In a mixed codec scenario, we only support scalability modes without + // spatial layers. + if (rtp_parameters.IsMixedCodec()) { + for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) { + auto scalability_mode = rtp_parameters.encodings[i].scalability_mode; + if (!scalability_mode) { + continue; + } + auto num_spatial_layers = + ScalabilityModeStringToNumSpatialLayers(*scalability_mode); + if (num_spatial_layers && *num_spatial_layers > 1) { + return LOG_ERROR( + RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Attempted to use a scalabilityMode with spatial layers in " + "a mixed codec scenario."); + } + } } return CheckScalabilityModeValues(rtp_parameters, send_codecs, send_codec); @@ -303,41 +280,40 @@ RTCError CheckRtpParametersInvalidModificationAndValues( RTCError CheckRtpParametersInvalidModificationAndValues( const RtpParameters& old_rtp_parameters, const RtpParameters& rtp_parameters, - ArrayView send_codecs, + std::span send_codecs, std::optional send_codec, const FieldTrialsView& field_trials) { - using webrtc::RTCErrorType; if (rtp_parameters.encodings.size() != old_rtp_parameters.encodings.size()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Attempted to set RtpParameters with different encoding count"); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to set RtpParameters with different encoding count"); } if (rtp_parameters.rtcp != old_rtp_parameters.rtcp) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Attempted to set RtpParameters with modified RTCP parameters"); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to set RtpParameters with modified RTCP parameters"); } if (rtp_parameters.header_extensions != old_rtp_parameters.header_extensions) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Attempted to set RtpParameters with modified header extensions"); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to set RtpParameters with modified header extensions"); } if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings, - [](const webrtc::RtpEncodingParameters& encoding1, - const webrtc::RtpEncodingParameters& encoding2) { + [](const RtpEncodingParameters& encoding1, + const RtpEncodingParameters& encoding2) { return encoding1.rid == encoding2.rid; })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Attempted to change RID values in the encodings."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to change RID values in the encodings."); } if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings, - [](const webrtc::RtpEncodingParameters& encoding1, - const webrtc::RtpEncodingParameters& encoding2) { + [](const RtpEncodingParameters& encoding1, + const RtpEncodingParameters& encoding2) { return encoding1.ssrc == encoding2.ssrc; })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Attempted to set RtpParameters with modified SSRC"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Attempted to set RtpParameters with modified SSRC"); } return CheckRtpParametersValues(rtp_parameters, send_codecs, send_codec, @@ -370,19 +346,19 @@ void CompositeMediaEngine::Terminate() { } VoiceEngineInterface& CompositeMediaEngine::voice() { - return *voice_engine_.get(); + return *voice_engine_; } VideoEngineInterface& CompositeMediaEngine::video() { - return *video_engine_.get(); + return *video_engine_; } const VoiceEngineInterface& CompositeMediaEngine::voice() const { - return *voice_engine_.get(); + return *voice_engine_; } const VideoEngineInterface& CompositeMediaEngine::video() const { - return *video_engine_.get(); + return *video_engine_; } } // namespace webrtc diff --git a/media/base/media_engine.h b/media/base/media_engine.h index 6300605a620..c85db96653c 100644 --- a/media/base/media_engine.h +++ b/media/base/media_engine.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_encoder_factory.h" @@ -43,13 +43,13 @@ class Call; // Checks that the scalability_mode value of each encoding is supported by at // least one video codec of the list. If the list is empty, no check is done. RTCError CheckScalabilityModeValues(const RtpParameters& new_parameters, - ArrayView send_codecs, + std::span send_codecs, std::optional send_codec); // Checks the parameters have valid and supported values, and checks parameters // with CheckScalabilityModeValues(). RTCError CheckRtpParametersValues(const RtpParameters& new_parameters, - ArrayView send_codecs, + std::span send_codecs, std::optional send_codec, const FieldTrialsView& field_trials); @@ -58,7 +58,7 @@ RTCError CheckRtpParametersValues(const RtpParameters& new_parameters, RTCError CheckRtpParametersInvalidModificationAndValues( const RtpParameters& old_parameters, const RtpParameters& new_parameters, - ArrayView send_codecs, + std::span send_codecs, std::optional send_codec, const FieldTrialsView& field_trials); @@ -77,7 +77,7 @@ class RtpHeaderExtensionQueryInterface { // Returns a vector of RtpHeaderExtensionCapability, whose direction is // kStopped if the extension is stopped (not used) by default. virtual std::vector GetRtpHeaderExtensions( - const webrtc::FieldTrialsView* field_trials) const = 0; + const FieldTrialsView* field_trials) const = 0; }; class VoiceEngineInterface : public RtpHeaderExtensionQueryInterface { @@ -153,7 +153,9 @@ class VideoEngineInterface : public RtpHeaderExtensionQueryInterface { const MediaConfig& config, const VideoOptions& options, const CryptoOptions& crypto_options, - VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) = 0; + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + video_encoder_switch_request_callback = nullptr) = 0; virtual std::unique_ptr CreateReceiveChannel(const Environment& env, @@ -240,7 +242,7 @@ RtpParameters CreateRtpParametersWithEncodings(StreamParams sp); // GetRtpHeaderExtensions() that are not kStopped. std::vector GetDefaultEnabledRtpHeaderExtensions( const RtpHeaderExtensionQueryInterface& query_interface, - const webrtc::FieldTrialsView* field_trials); + const FieldTrialsView* field_trials); } // namespace webrtc diff --git a/media/base/rtp_utils.cc b/media/base/rtp_utils.cc index 1bb208ab31e..3a2002eafef 100644 --- a/media/base/rtp_utils.cc +++ b/media/base/rtp_utils.cc @@ -12,12 +12,13 @@ #include #include +#include #include // PacketTimeUpdateParams is defined in asyncpacketsocket.h. // TODO(sergeyu): Find more appropriate place for PacketTimeUpdateParams. + #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "media/base/turn_utils.h" #include "modules/rtp_rtcp/source/rtp_util.h" #include "rtc_base/async_packet_socket.h" @@ -73,7 +74,7 @@ void UpdateAbsSendTimeExtensionValue(uint8_t* extension_data, // Assumes `length` is actual packet length + tag length. Updates HMAC at end of // the RTP packet. -void UpdateRtpAuthTag(ArrayView rtp, +void UpdateRtpAuthTag(std::span rtp, const PacketTimeUpdateParams& packet_time_params) { // If there is no key, return. if (packet_time_params.srtp_auth_key.empty()) { @@ -92,6 +93,8 @@ void UpdateRtpAuthTag(ArrayView rtp, uint8_t* auth_tag = rtp.data() + (rtp.size() - tag_length); // We should have a fake HMAC value @ auth_tag. + // Tag length must be no bigger than kFakeAuthTag size (currently 10). + RTC_DCHECK_LE(tag_length, sizeof(kFakeAuthTag)); RTC_DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length)); // Copy ROC after end of rtp packet. @@ -145,7 +148,8 @@ bool GetRtcpSsrc(const void* data, size_t len, uint32_t* value) { // SDES packet parsing is not supported. if (pl_type == kRtcpTypeSDES) return false; - *value = GetBE32(static_cast(data) + 4); + *value = GetBE32( + std::span(static_cast(data) + 4, 4)); return true; } @@ -173,7 +177,7 @@ absl::string_view RtpPacketTypeToString(RtpPacketType packet_type) { RTC_CHECK_NOTREACHED(); } -RtpPacketType InferRtpPacketType(ArrayView packet) { +RtpPacketType InferRtpPacketType(std::span packet) { if (IsRtcpPacket(packet)) { return RtpPacketType::kRtcp; } @@ -183,7 +187,7 @@ RtpPacketType InferRtpPacketType(ArrayView packet) { return RtpPacketType::kUnknown; } -bool ValidateRtpHeader(ArrayView rtp, size_t* header_length) { +bool ValidateRtpHeader(std::span rtp, size_t* header_length) { size_t length = rtp.size(); if (header_length) { *header_length = 0; @@ -215,7 +219,7 @@ bool ValidateRtpHeader(ArrayView rtp, size_t* header_length) { // Getting extension profile length. // Length is in 32 bit words. uint16_t extension_length_in_32bits = - GetBE16(&rtp[header_length_without_extension + 2]); + GetBE16(rtp.subspan(header_length_without_extension + 2, 2)); size_t extension_length = extension_length_in_32bits * 4; size_t rtp_header_length = extension_length + @@ -235,7 +239,7 @@ bool ValidateRtpHeader(ArrayView rtp, size_t* header_length) { // ValidateRtpHeader() must be called before this method to make sure, we have // a sane rtp packet. -bool UpdateRtpAbsSendTimeExtension(ArrayView packet, +bool UpdateRtpAbsSendTimeExtension(std::span packet, int extension_id, uint64_t time_us) { // 0 1 2 3 @@ -262,10 +266,13 @@ bool UpdateRtpAbsSendTimeExtension(ArrayView packet, uint8_t* rtp = packet.data(); rtp += header_length_without_extension; + std::span extension_header = + packet.subspan(header_length_without_extension, kRtpExtensionHeaderLen); + // Getting extension profile ID and length. - uint16_t profile_id = GetBE16(rtp); + uint16_t profile_id = GetBE16(extension_header); // Length is in 32 bit words. - uint16_t extension_length_in_32bits = GetBE16(rtp + 2); + uint16_t extension_length_in_32bits = GetBE16(extension_header.subspan(2, 2)); size_t extension_length = extension_length_in_32bits * 4; rtp += kRtpExtensionHeaderLen; // Moving past extension header. @@ -350,7 +357,7 @@ bool UpdateRtpAbsSendTimeExtension(ArrayView packet, return found; } -bool ApplyPacketOptions(ArrayView data, +bool ApplyPacketOptions(std::span data, const PacketTimeUpdateParams& packet_time_params, uint64_t time_us) { RTC_DCHECK(!data.empty()); @@ -374,7 +381,7 @@ bool ApplyPacketOptions(ArrayView data, } // Making sure we have a valid RTP packet at the end. - auto packet = data.subview(rtp_start_pos, rtp_length); + auto packet = data.subspan(rtp_start_pos, rtp_length); if (!IsRtpPacket(packet) || !ValidateRtpHeader(packet, nullptr)) { RTC_DCHECK_NOTREACHED(); return false; diff --git a/media/base/rtp_utils.h b/media/base/rtp_utils.h index b9fae21a4a3..a8f1a128b98 100644 --- a/media/base/rtp_utils.h +++ b/media/base/rtp_utils.h @@ -13,9 +13,9 @@ #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/system/rtc_export.h" @@ -45,7 +45,7 @@ bool GetRtcpType(const void* data, size_t len, int* value); bool GetRtcpSsrc(const void* data, size_t len, uint32_t* value); // Checks the packet header to determine if it can be an RTP or RTCP packet. -RtpPacketType InferRtpPacketType(ArrayView packet); +RtpPacketType InferRtpPacketType(std::span packet); // True if |payload type| is 0-127. bool IsValidRtpPayloadType(int payload_type); @@ -56,18 +56,18 @@ bool IsValidRtpPacketSize(RtpPacketType packet_type, size_t size); absl::string_view RtpPacketTypeToString(RtpPacketType packet_type); // Verifies that a packet has a valid RTP header. -bool RTC_EXPORT ValidateRtpHeader(ArrayView rtp, +bool RTC_EXPORT ValidateRtpHeader(std::span rtp, size_t* header_length); // Helper method which updates the absolute send time extension if present. -bool UpdateRtpAbsSendTimeExtension(ArrayView rtp, +bool UpdateRtpAbsSendTimeExtension(std::span rtp, int extension_id, uint64_t time_us); // Applies specified `options` to the packet. It updates the absolute send time // extension header if it is present present then updates HMAC. bool RTC_EXPORT -ApplyPacketOptions(ArrayView data, +ApplyPacketOptions(std::span data, const PacketTimeUpdateParams& packet_time_params, uint64_t time_us); diff --git a/media/base/rtp_utils_unittest.cc b/media/base/rtp_utils_unittest.cc index af3263e7e27..269e4d6c9ae 100644 --- a/media/base/rtp_utils_unittest.cc +++ b/media/base/rtp_utils_unittest.cc @@ -14,7 +14,6 @@ #include #include -#include "api/array_view.h" #include "media/base/fake_rtp.h" #include "rtc_base/async_packet_socket.h" #include "test/gtest.h" @@ -72,13 +71,6 @@ static const int kAstIndexInOneByteRtpMsg = 21; // and in message `kRtpMsgWithTwoByteAbsSendTimeExtension`. static const int kAstIndexInTwoByteRtpMsg = 21; -static const ArrayView kPcmuFrameArrayView = - MakeArrayView(kPcmuFrame, sizeof(kPcmuFrame)); -static const ArrayView kRtcpReportArrayView = - MakeArrayView(kRtcpReport, sizeof(kRtcpReport)); -static const ArrayView kInvalidPacketArrayView = - MakeArrayView(kInvalidPacket, sizeof(kInvalidPacket)); - TEST(RtpUtilsTest, GetRtcp) { int pt; EXPECT_TRUE(GetRtcpType(kRtcpReport, sizeof(kRtcpReport), &pt)); @@ -280,10 +272,9 @@ TEST(RtpUtilsTest, ApplyPacketOptionsWithAuthParamsAndAbsSendTime) { } TEST(RtpUtilsTest, InferRtpPacketType) { - EXPECT_EQ(RtpPacketType::kRtp, InferRtpPacketType(kPcmuFrameArrayView)); - EXPECT_EQ(RtpPacketType::kRtcp, InferRtpPacketType(kRtcpReportArrayView)); - EXPECT_EQ(RtpPacketType::kUnknown, - InferRtpPacketType(kInvalidPacketArrayView)); + EXPECT_EQ(RtpPacketType::kRtp, InferRtpPacketType(kPcmuFrame)); + EXPECT_EQ(RtpPacketType::kRtcp, InferRtpPacketType(kRtcpReport)); + EXPECT_EQ(RtpPacketType::kUnknown, InferRtpPacketType(kInvalidPacket)); } } // namespace webrtc diff --git a/media/base/stream_params.cc b/media/base/stream_params.cc index 4cae898ef75..d330d9c60fe 100644 --- a/media/base/stream_params.cc +++ b/media/base/stream_params.cc @@ -11,12 +11,12 @@ #include "media/base/stream_params.h" #include +#include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "media/base/rid_description.h" #include "rtc_base/checks.h" #include "rtc_base/strings/string_builder.h" @@ -25,7 +25,7 @@ namespace webrtc { namespace { -void AppendSsrcs(ArrayView ssrcs, SimpleStringBuilder* sb) { +void AppendSsrcs(std::span ssrcs, SimpleStringBuilder* sb) { *sb << "ssrcs:["; const char* delimiter = ""; for (uint32_t ssrc : ssrcs) { @@ -35,7 +35,7 @@ void AppendSsrcs(ArrayView ssrcs, SimpleStringBuilder* sb) { *sb << "]"; } -void AppendSsrcGroups(ArrayView ssrc_groups, +void AppendSsrcGroups(std::span ssrc_groups, SimpleStringBuilder* sb) { *sb << "ssrc_groups:"; const char* delimiter = ""; @@ -45,7 +45,7 @@ void AppendSsrcGroups(ArrayView ssrc_groups, } } -void AppendStreamIds(ArrayView stream_ids, +void AppendStreamIds(std::span stream_ids, SimpleStringBuilder* sb) { *sb << "stream_ids:"; const char* delimiter = ""; @@ -55,7 +55,7 @@ void AppendStreamIds(ArrayView stream_ids, } } -void AppendRids(ArrayView rids, SimpleStringBuilder* sb) { +void AppendRids(std::span rids, SimpleStringBuilder* sb) { *sb << "rids:["; const char* delimiter = ""; for (const RidDescription& rid : rids) { diff --git a/media/base/stream_params_unittest.cc b/media/base/stream_params_unittest.cc index 49a8926c861..332022a5f88 100644 --- a/media/base/stream_params_unittest.cc +++ b/media/base/stream_params_unittest.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "media/base/test_utils.h" #include "rtc_base/unique_id_generator.h" #include "test/gmock.h" @@ -29,7 +29,7 @@ static const uint32_t kSsrcs2[] = {1, 2}; static webrtc::StreamParams CreateStreamParamsWithSsrcGroup( const std::string& semantics, - webrtc::ArrayView ssrcs_in) { + std::span ssrcs_in) { webrtc::StreamParams stream; std::vector ssrcs(ssrcs_in.begin(), ssrcs_in.end()); webrtc::SsrcGroup sg(semantics, ssrcs); diff --git a/media/base/turn_utils.cc b/media/base/turn_utils.cc index d006bb892d5..b3e14bd1a13 100644 --- a/media/base/turn_utils.cc +++ b/media/base/turn_utils.cc @@ -12,6 +12,7 @@ #include #include +#include #include "api/transport/stun.h" #include "rtc_base/byte_order.h" @@ -31,7 +32,7 @@ bool IsTurnSendIndicationPacket(const uint8_t* data, size_t length) { return false; } - uint16_t type = GetBE16(data); + uint16_t type = GetBE16(std::span(data, 2)); return (type == TURN_SEND_INDICATION); } @@ -41,6 +42,7 @@ bool UnwrapTurnPacket(const uint8_t* packet, size_t packet_size, size_t* content_position, size_t* content_size) { + std::span data_view(packet, packet_size); if (IsTurnChannelData(packet, packet_size)) { // Turn Channel Message header format. // 0 1 2 3 @@ -52,7 +54,7 @@ bool UnwrapTurnPacket(const uint8_t* packet, // / Application Data / // / / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - size_t length = GetBE16(&packet[2]); + size_t length = GetBE16(data_view.subspan(2, 2)); if (length + kTurnChannelHeaderLength > packet_size) { return false; } @@ -64,7 +66,7 @@ bool UnwrapTurnPacket(const uint8_t* packet, if (IsTurnSendIndicationPacket(packet, packet_size)) { // Validate STUN message length. - const size_t stun_message_length = GetBE16(&packet[2]); + const size_t stun_message_length = GetBE16(data_view.subspan(2, 2)); if (stun_message_length + kStunHeaderSize != packet_size) { return false; } @@ -94,8 +96,8 @@ bool UnwrapTurnPacket(const uint8_t* packet, } // Getting attribute type and length. - attr_type = GetBE16(&packet[pos]); - attr_length = GetBE16(&packet[pos + sizeof(attr_type)]); + attr_type = GetBE16(data_view.subspan(pos, 2)); + attr_length = GetBE16(data_view.subspan(pos + sizeof(attr_type), 2)); pos += kAttrHeaderLength; // Skip STUN_DATA_ATTR header. diff --git a/media/base/video_adapter.cc b/media/base/video_adapter.cc index 7300282bd03..533bb95cd48 100644 --- a/media/base/video_adapter.cc +++ b/media/base/video_adapter.cc @@ -29,6 +29,8 @@ #include "rtc_base/synchronization/mutex.h" #include "rtc_base/time_utils.h" +namespace webrtc { + namespace { struct Fraction { @@ -136,8 +138,6 @@ std::optional> Swap( } // namespace -namespace webrtc { - VideoAdapter::VideoAdapter(int source_resolution_alignment) : frames_in_(0), frames_out_(0), diff --git a/media/base/video_common.cc b/media/base/video_common.cc index 053a250cd84..38b55624bcc 100644 --- a/media/base/video_common.cc +++ b/media/base/video_common.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { diff --git a/media/engine/fake_video_codec_factory.cc b/media/engine/fake_video_codec_factory.cc index 1c623535445..c9c114b4eb5 100644 --- a/media/engine/fake_video_codec_factory.cc +++ b/media/engine/fake_video_codec_factory.cc @@ -22,13 +22,13 @@ #include "test/fake_decoder.h" #include "test/fake_encoder.h" +namespace webrtc { + namespace { const char kFakeCodecFactoryCodecName[] = "FakeCodec"; -} // anonymous namespace - -namespace webrtc { +} // namespace std::vector FakeVideoEncoderFactory::GetSupportedFormats() const { diff --git a/media/engine/fake_webrtc_call.cc b/media/engine/fake_webrtc_call.cc index 04eaa4fffac..e489601715e 100644 --- a/media/engine/fake_webrtc_call.cc +++ b/media/engine/fake_webrtc_call.cc @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include #include @@ -19,11 +21,11 @@ #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" #include "api/adaptation/resource.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/call/audio_sink.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/environment/environment.h" +#include "api/fec_controller.h" #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/media_types.h" @@ -35,6 +37,7 @@ #include "api/task_queue/task_queue_base.h" #include "api/units/timestamp.h" #include "api/video/video_source_interface.h" +#include "api/video/video_stream_encoder_settings.h" #include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_encoder.h" #include "call/audio_receive_stream.h" @@ -125,11 +128,11 @@ void FakeAudioReceiveStream::SetStats( } bool FakeAudioReceiveStream::VerifyLastPacket( - ArrayView data) const { + std::span data) const { return last_packet_ == Buffer(data.data(), data.size()); } -bool FakeAudioReceiveStream::DeliverRtp(ArrayView packet, +bool FakeAudioReceiveStream::DeliverRtp(std::span packet, int64_t /* packet_time_us */) { ++received_packets_; last_packet_.SetData(packet); @@ -296,7 +299,7 @@ VideoSendStream::Stats FakeVideoSendStream::GetStats() { return stats_; } -void FakeVideoSendStream::SetCsrcs(ArrayView csrcs) {} +void FakeVideoSendStream::SetCsrcs(std::span csrcs) {} void FakeVideoSendStream::ReconfigureVideoEncoder(VideoEncoderConfig config) { ReconfigureVideoEncoder(std::move(config), nullptr); @@ -590,7 +593,20 @@ void FakeCall::DestroyAudioReceiveStream( VideoSendStream* FakeCall::CreateVideoSendStream( VideoSendStream::Config config, - VideoEncoderConfig encoder_config) { + VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback) { + FakeVideoSendStream* fake_stream = new FakeVideoSendStream( + env_, std::move(config), std::move(encoder_config)); + video_send_streams_.push_back(fake_stream); + ++num_created_send_streams_; + return fake_stream; +} + +VideoSendStream* FakeCall::CreateVideoSendStream( + VideoSendStream::Config config, + VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback, + std::unique_ptr fec_controller) { FakeVideoSendStream* fake_stream = new FakeVideoSendStream( env_, std::move(config), std::move(encoder_config)); video_send_streams_.push_back(fake_stream); @@ -744,24 +760,6 @@ void FakeCall::SignalChannelNetworkState(MediaType media, NetworkState state) { void FakeCall::OnAudioTransportOverheadChanged( int /* transport_overhead_per_packet */) {} -void FakeCall::OnLocalSsrcUpdated(AudioReceiveStreamInterface& stream, - uint32_t local_ssrc) { - auto& fake_stream = static_cast(stream); - fake_stream.SetLocalSsrc(local_ssrc); -} - -void FakeCall::OnLocalSsrcUpdated(VideoReceiveStreamInterface& stream, - uint32_t local_ssrc) { - auto& fake_stream = static_cast(stream); - fake_stream.SetLocalSsrc(local_ssrc); -} - -void FakeCall::OnLocalSsrcUpdated(FlexfecReceiveStream& stream, - uint32_t local_ssrc) { - auto& fake_stream = static_cast(stream); - fake_stream.SetLocalSsrc(local_ssrc); -} - void FakeCall::OnUpdateSyncGroup(AudioReceiveStreamInterface& stream, absl::string_view sync_group) { auto& fake_stream = static_cast(stream); diff --git a/media/engine/fake_webrtc_call.h b/media/engine/fake_webrtc_call.h index 43850338f2f..a104e01c871 100644 --- a/media/engine/fake_webrtc_call.h +++ b/media/engine/fake_webrtc_call.h @@ -25,18 +25,19 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" #include "api/adaptation/resource.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_format.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/environment/environment.h" +#include "api/fec_controller.h" #include "api/frame_transformer_interface.h" #include "api/media_types.h" #include "api/rtp_headers.h" @@ -51,6 +52,7 @@ #include "api/video/video_frame.h" #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" +#include "api/video/video_stream_encoder_settings.h" #include "api/video_codecs/video_codec.h" #include "call/audio_receive_stream.h" #include "call/audio_send_stream.h" @@ -125,19 +127,15 @@ class FakeAudioReceiveStream final : public AudioReceiveStreamInterface { const AudioReceiveStreamInterface::Config& GetConfig() const; void SetStats(const AudioReceiveStreamInterface::Stats& stats); int received_packets() const { return received_packets_; } - bool VerifyLastPacket(ArrayView data) const; + bool VerifyLastPacket(std::span data) const; const AudioSinkInterface* sink() const { return sink_; } float gain() const { return gain_; } - bool DeliverRtp(ArrayView packet, int64_t packet_time_us); + bool DeliverRtp(std::span packet, int64_t packet_time_us); bool started() const { return started_; } int base_mininum_playout_delay_ms() const { return base_mininum_playout_delay_ms_; } - void SetLocalSsrc(uint32_t local_ssrc) { - config_.rtp.local_ssrc = local_ssrc; - } - void SetSyncGroup(absl::string_view sync_group) { config_.sync_group = std::string(sync_group); } @@ -210,7 +208,7 @@ class FakeVideoSendStream final : public VideoSendStream, int GetLastHeight() const; int64_t GetLastTimestamp() const; void SetStats(const VideoSendStream::Stats& stats) override; - void SetCsrcs(ArrayView csrcs) override; + void SetCsrcs(std::span csrcs) override; int num_encoder_reconfigurations() const { return num_encoder_reconfigurations_; } @@ -289,10 +287,6 @@ class FakeVideoReceiveStream final : public VideoReceiveStreamInterface { return base_mininum_playout_delay_ms_; } - void SetLocalSsrc(uint32_t local_ssrc) { - config_.rtp.local_ssrc = local_ssrc; - } - void UpdateRtxSsrc(uint32_t ssrc) override { config_.rtp.rtx_ssrc = ssrc; } void SetFrameDecryptor(scoped_refptr @@ -366,10 +360,6 @@ class FakeFlexfecReceiveStream final : public FlexfecReceiveStream { public: explicit FakeFlexfecReceiveStream(const FlexfecReceiveStream::Config config); - void SetLocalSsrc(uint32_t local_ssrc) { - config_.rtp.local_ssrc = local_ssrc; - } - void SetRtcpMode(RtcpMode mode) override { config_.rtcp_mode = mode; } int payload_type() const override { return config_.payload_type; } @@ -379,7 +369,7 @@ class FakeFlexfecReceiveStream final : public FlexfecReceiveStream { const FlexfecReceiveStream::Config& GetConfig() const; - uint32_t remote_ssrc() const { return config_.rtp.remote_ssrc; } + uint32_t remote_ssrc() const { return config_.remote_ssrc; } const ReceiveStatistics* GetStats() const override { return nullptr; } @@ -455,7 +445,15 @@ class FakeCall final : public Call, public PacketReceiver { VideoSendStream* CreateVideoSendStream( VideoSendStream::Config config, - VideoEncoderConfig encoder_config) override; + VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback = + nullptr) override; + + VideoSendStream* CreateVideoSendStream( + VideoSendStream::Config config, + VideoEncoderConfig encoder_config, + EncoderSwitchRequestCallback encoder_switch_request_callback, + std::unique_ptr fec_controller) override; void DestroyVideoSendStream(VideoSendStream* send_stream) override; VideoReceiveStreamInterface* CreateVideoReceiveStream( @@ -496,12 +494,6 @@ class FakeCall final : public Call, public PacketReceiver { void SignalChannelNetworkState(MediaType media, NetworkState state) override; void OnAudioTransportOverheadChanged( int transport_overhead_per_packet) override; - void OnLocalSsrcUpdated(AudioReceiveStreamInterface& stream, - uint32_t local_ssrc) override; - void OnLocalSsrcUpdated(VideoReceiveStreamInterface& stream, - uint32_t local_ssrc) override; - void OnLocalSsrcUpdated(FlexfecReceiveStream& stream, - uint32_t local_ssrc) override; void OnUpdateSyncGroup(AudioReceiveStreamInterface& stream, absl::string_view sync_group) override; void OnSentPacket(const SentPacketInfo& sent_packet) override; diff --git a/media/engine/internal_decoder_factory.cc b/media/engine/internal_decoder_factory.cc index f81c0f57bb0..ed05e715727 100644 --- a/media/engine/internal_decoder_factory.cc +++ b/media/engine/internal_decoder_factory.cc @@ -16,10 +16,12 @@ #include "absl/strings/match.h" #include "api/environment/environment.h" #include "api/video/video_codec_type.h" +#include "api/video_codecs/h264_profile_level_id.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_decoder.h" #include "api/video_codecs/video_decoder_factory.h" +#include "media/base/codec_comparators.h" #include "media/base/media_constants.h" #include "modules/video_coding/codecs/h264/include/h264.h" #include "modules/video_coding/codecs/vp8/include/vp8.h" @@ -75,14 +77,28 @@ VideoDecoderFactory::CodecSupport InternalDecoderFactory::QueryCodecSupport( } CodecSupport codec_support; - codec_support.is_supported = format.IsCodecInList(GetSupportedFormats()); + const std::vector& supported_formats = GetSupportedFormats(); + // For H.264, stream profile can be subset of supported profile. + if (absl::EqualsIgnoreCase(format.name, kH264CodecName)) { + for (const SdpVideoFormat& supported : supported_formats) { + if (absl::EqualsIgnoreCase(supported.name, kH264CodecName) && + IsSameH264PacketizationMode(format.parameters, + supported.parameters) && + H264IsProfileSubsetOf(format.parameters, supported.parameters)) { + codec_support.is_supported = true; + break; + } + } + } else { + codec_support.is_supported = format.IsCodecInList(supported_formats); + } return codec_support; } std::unique_ptr InternalDecoderFactory::Create( const Environment& env, const SdpVideoFormat& format) { - if (!format.IsCodecInList(GetSupportedFormats())) { + if (!QueryCodecSupport(format, false).is_supported) { RTC_LOG(LS_WARNING) << "Trying to create decoder for unsupported format. " << format.ToString(); return nullptr; diff --git a/media/engine/internal_decoder_factory_unittest.cc b/media/engine/internal_decoder_factory_unittest.cc index f72110cf28c..b10cc473570 100644 --- a/media/engine/internal_decoder_factory_unittest.cc +++ b/media/engine/internal_decoder_factory_unittest.cc @@ -149,6 +149,47 @@ TEST(InternalDecoderFactoryTest, QueryCodecSupportNoReferenceScaling) { #endif } +TEST(InternalDecoderFactoryTest, QueryCodecSupportH264Profiles) { + InternalDecoderFactory factory; + auto h264_support = kH264Enabled ? kSupported : kUnsupported; + + // The internal decoder factory explicitly lists Baseline, Constrained + // Baseline, Main, and High Predictive 4:4:4. Through profile subset matching, + // it should also support High and Constrained High profiles because they are + // subsets of High Predictive 4:4:4. + EXPECT_THAT( + factory.QueryCodecSupport( + SdpVideoFormat("H264", {{"profile-level-id", "42e01f"}}), false), + Support(h264_support)); + EXPECT_THAT( + factory.QueryCodecSupport( + SdpVideoFormat("H264", {{"profile-level-id", "42001f"}}), false), + Support(h264_support)); + EXPECT_THAT( + factory.QueryCodecSupport( + SdpVideoFormat("H264", {{"profile-level-id", "4d001f"}}), false), + Support(h264_support)); + EXPECT_THAT( + factory.QueryCodecSupport( + SdpVideoFormat("H264", {{"profile-level-id", "640c1f"}}), false), + Support(h264_support)); + EXPECT_THAT( + factory.QueryCodecSupport( + SdpVideoFormat("H264", {{"profile-level-id", "64001f"}}), false), + Support(h264_support)); + + EXPECT_THAT( + factory.QueryCodecSupport( + SdpVideoFormat("H264", {{"profile-level-id", "ff0000"}}), false), + Support(kUnsupported)); + + EXPECT_THAT(factory.QueryCodecSupport( + SdpVideoFormat("H264", {{"profile-level-id", "42e01f"}, + {"packetization-mode", "2"}}), + false), + Support(kUnsupported)); +} + TEST(InternalDecoderFactoryTest, QueryCodecSupportReferenceScaling) { InternalDecoderFactory factory; // VP9 and AV1 support for spatial layers. diff --git a/media/engine/simulcast_encoder_adapter.cc b/media/engine/simulcast_encoder_adapter.cc index 31c1864cbb5..b3f6ebbe97c 100644 --- a/media/engine/simulcast_encoder_adapter.cc +++ b/media/engine/simulcast_encoder_adapter.cc @@ -11,6 +11,7 @@ #include "media/engine/simulcast_encoder_adapter.h" #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +27,6 @@ #include "absl/algorithm/container.h" #include "absl/base/nullability.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/fec_controller_override.h" #include "api/field_trials_view.h" @@ -51,14 +52,17 @@ #include "api/video_codecs/video_encoder_software_fallback_wrapper.h" #include "common_video/framerate_controller.h" #include "media/base/sdp_video_format_utils.h" +#include "modules/include/module_common_types_public.h" #include "modules/video_coding/include/video_error_codes.h" #include "modules/video_coding/include/video_error_codes_utils.h" #include "modules/video_coding/utility/simulcast_rate_allocator.h" #include "rtc_base/checks.h" #include "rtc_base/experiments/rate_control_settings.h" #include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/strings/str_join.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/synchronization/mutex.h" namespace webrtc { namespace { @@ -128,7 +132,7 @@ bool StreamQualityCompare(const SimulcastStream& a, const SimulcastStream& b) { } void GetLowestAndHighestQualityStreamIndixes( - ArrayView streams, + std::span streams, int* lowest_quality_stream_idx, int* highest_quality_stream_idx) { const auto lowest_highest_quality_streams = @@ -141,13 +145,13 @@ void GetLowestAndHighestQualityStreamIndixes( std::vector GetStreamStartBitratesKbps(const Environment& env, const VideoCodec& codec) { - std::vector start_bitrates; VideoBitrateAllocation allocation = SimulcastRateAllocator(env, codec) .Allocate(VideoBitrateAllocationParameters(codec.startBitrate * 1000, codec.maxFramerate)); int total_streams_count = CountAllStreams(codec); + std::vector start_bitrates; for (int i = 0; i < total_streams_count; ++i) { uint32_t stream_bitrate = allocation.GetSpatialLayerSum(i) / 1000; start_bitrates.push_back(stream_bitrate); @@ -193,6 +197,8 @@ SimulcastEncoderAdapter::StreamContext::StreamContext( is_keyframe_needed_(false), is_paused_(is_paused) { if (parent_) { + // If we have a parent, this encoder is not running in bypass mode and we + // should funnnel the callbacks back via this stream context. encoder_context_->encoder().RegisterEncodeCompleteCallback(this); } } @@ -207,6 +213,8 @@ SimulcastEncoderAdapter::StreamContext::StreamContext(StreamContext&& rhs) is_keyframe_needed_(rhs.is_keyframe_needed_), is_paused_(rhs.is_paused_) { if (parent_) { + // If we have a parent, this encoder is not running in bypass mode and we + // should funnel the callbacks back via this stream context. encoder_context_->encoder().RegisterEncodeCompleteCallback(this); } } @@ -243,14 +251,65 @@ SimulcastEncoderAdapter::StreamContext::OnEncodedImage( const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info) { RTC_CHECK(parent_); // If null, this method should never be called. + // If encoder is simulcast capable, it should be used in bypass mode and never + // end up here - so we expect all frame to always be end of temporal unit in + // this context. + RTC_DCHECK(encoded_image.is_end_of_temporal_unit().value_or(true)); + MaybeCullOldTimestamps(encoded_image.RtpTimestamp()); return parent_->OnEncodedImage(stream_idx_, encoded_image, codec_specific_info); } -void SimulcastEncoderAdapter::StreamContext::OnDroppedFrame( - DropReason /*reason*/) { +void SimulcastEncoderAdapter::StreamContext::OnFrameDropped( + uint32_t rtp_timestamp, + int spatial_id, + bool is_end_of_temporal_unit) { RTC_CHECK(parent_); // If null, this method should never be called. - parent_->OnDroppedFrame(stream_idx_); + // If encoder is simulcast capable, it should be used in bypass mode and never + // end up here - so we expect all frame to always be end of temporal unit in + // this context. + RTC_DCHECK(is_end_of_temporal_unit); + MaybeCullOldTimestamps(rtp_timestamp); + parent_->OnFrameDropped(rtp_timestamp, stream_idx_ + spatial_id); +} + +int SimulcastEncoderAdapter::StreamContext::Encode( + const VideoFrame& frame, + const std::vector* frame_types) { + { + MutexLock lock(&queue_mutex_); + pending_rtp_timestamps_.push_back(frame.rtp_timestamp()); + } + int ret = encoder().Encode(frame, frame_types); + if (ret != WEBRTC_VIDEO_CODEC_OK) { + MutexLock lock(&queue_mutex_); + RTC_DCHECK_EQ(pending_rtp_timestamps_.back(), frame.rtp_timestamp()); + pending_rtp_timestamps_.pop_back(); + } + return ret; +} + +void SimulcastEncoderAdapter::StreamContext::MaybeCullOldTimestamps( + uint32_t new_rtp_timestamp) { + std::vector dropped_timestamps; + { + MutexLock lock(&queue_mutex_); + while ( + !pending_rtp_timestamps_.empty() && + pending_rtp_timestamps_.front() != new_rtp_timestamp && + IsNewerTimestamp(new_rtp_timestamp, pending_rtp_timestamps_.front())) { + dropped_timestamps.push_back(pending_rtp_timestamps_.front()); + pending_rtp_timestamps_.pop_front(); + } + if (!pending_rtp_timestamps_.empty() && + pending_rtp_timestamps_.front() == new_rtp_timestamp) { + pending_rtp_timestamps_.pop_front(); + } + } + // Make callback outside lock to avoid potential deadlocks. + for (uint32_t timestamp : dropped_timestamps) { + parent_->OnFrameDropped(timestamp, stream_idx_); + } } SimulcastEncoderAdapter::SimulcastEncoderAdapter( @@ -278,11 +337,11 @@ SimulcastEncoderAdapter::SimulcastEncoderAdapter( // The adapter is typically created on the worker thread, but operated on // the encoder task queue. - encoder_queue_.Detach(); + encoder_queue_checker_.Detach(); } SimulcastEncoderAdapter::~SimulcastEncoderAdapter() { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); RTC_DCHECK(!Initialized()); DestroyStoredEncoders(); } @@ -293,7 +352,7 @@ void SimulcastEncoderAdapter::SetFecControllerOverride( } int SimulcastEncoderAdapter::Release() { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); while (!stream_contexts_.empty()) { // Move the encoder instances and put it on the `cached_encoder_contexts_` @@ -305,8 +364,13 @@ int SimulcastEncoderAdapter::Release() { bypass_mode_ = false; + { + MutexLock lock(&pending_frames_mutex_); + pending_frames_.clear(); + } + // It's legal to move the encoder to another queue now. - encoder_queue_.Detach(); + encoder_queue_checker_.Detach(); inited_.store(0); @@ -316,7 +380,7 @@ int SimulcastEncoderAdapter::Release() { int SimulcastEncoderAdapter::InitEncode( const VideoCodec* codec_settings, const VideoEncoder::Settings& settings) { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); if (settings.number_of_cores < 1) { return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; @@ -337,7 +401,7 @@ int SimulcastEncoderAdapter::InitEncode( int highest_quality_stream_idx = 0; if (!is_legacy_singlecast) { GetLowestAndHighestQualityStreamIndixes( - ArrayView(codec_.simulcastStream, + std::span(codec_.simulcastStream, total_streams_count_), &lowest_quality_stream_idx, &highest_quality_stream_idx); } @@ -446,11 +510,7 @@ int SimulcastEncoderAdapter::InitEncode( return result; } - // Intercept frame encode complete callback only for upper streams, where - // we need to set a correct stream index. Set `parent` to nullptr for the - // lowest stream to bypass the callback. - SimulcastEncoderAdapter* parent = stream_idx > 0 ? this : nullptr; - + SimulcastEncoderAdapter* parent = this; bool is_paused = stream_start_bitrate_kbps[stream_idx] == 0; stream_contexts_.emplace_back( parent, std::move(encoder_context), @@ -458,7 +518,6 @@ int SimulcastEncoderAdapter::InitEncode( stream_idx, stream_codec.width, stream_codec.height, is_paused); encoder_context = nullptr; } - // To save memory, don't store encoders that we don't use. DestroyStoredEncoders(); @@ -469,7 +528,7 @@ int SimulcastEncoderAdapter::InitEncode( int SimulcastEncoderAdapter::Encode( const VideoFrame& input_image, const std::vector* frame_types) { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); if (!Initialized()) { return WEBRTC_VIDEO_CODEC_UNINITIALIZED; @@ -518,6 +577,51 @@ int SimulcastEncoderAdapter::Encode( int src_width = input_image.width(); int src_height = input_image.height(); + if (!bypass_mode_) { + // Limit to prevent this queue from growing if the underlying encoder is + // not reporting the completion of frames via either OnEncodedFrame or + // OnFrameDropped. + constexpr size_t kMaxPendingFrames = 15; + std::vector> frame_drops; + { + MutexLock lock(&pending_frames_mutex_); + if (pending_frames_.size() >= kMaxPendingFrames) { + // Drop the oldest frame. + PendingFrame& dropped_frame = pending_frames_.front(); + RTC_LOG(LS_WARNING) << "Pending frames queue full. Dropping frame with " + "rtp_timestamp=" + << dropped_frame.rtp_timestamp; + for (size_t stream_idx = 0; stream_idx < kMaxSimulcastStreams; + ++stream_idx) { + if (dropped_frame.expected_layer_index.test(stream_idx)) { + dropped_frame.expected_layer_index.reset(stream_idx); + bool is_last = dropped_frame.expected_layer_index.none(); + frame_drops.emplace_back(dropped_frame.rtp_timestamp, stream_idx, + is_last); + } + } + pending_frames_.pop_front(); + } + + std::bitset expected_layer_index; + for (const auto& layer : stream_contexts_) { + if (!layer.is_paused()) { + expected_layer_index.set(layer.stream_idx()); + } + } + pending_frames_.push_back({ + .rtp_timestamp = input_image.rtp_timestamp(), + .expected_layer_index = expected_layer_index, + }); + } + + // Make frame drop callbacks outside of the lock. + for (const auto& [rtp_timestamp, stream_idx, is_last] : frame_drops) { + encoded_complete_callback_->OnFrameDropped(rtp_timestamp, stream_idx, + is_last); + } + } + for (auto& layer : stream_contexts_) { // Don't encode frames in resolutions that we don't intend to send. if (layer.is_paused()) { @@ -563,6 +667,7 @@ int SimulcastEncoderAdapter::Encode( if (keyframe_requested) { layer.OnKeyframe(frame_timestamp); } else if (layer.ShouldDropFrame(frame_timestamp)) { + OnFrameDropped(input_image.rtp_timestamp(), layer.stream_idx()); continue; } @@ -580,8 +685,12 @@ int SimulcastEncoderAdapter::Encode( (input_image.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative && layer.encoder().GetEncoderInfo().supports_native_handle)) { - int ret = layer.encoder().Encode(input_image, &stream_frame_types); + int ret = layer.Encode(input_image, &stream_frame_types); if (ret != WEBRTC_VIDEO_CODEC_OK) { + if (!bypass_mode_) { + MutexLock lock(&pending_frames_mutex_); + pending_frames_.pop_back(); + } return ret; } } else { @@ -604,7 +713,8 @@ int SimulcastEncoderAdapter::Encode( .offset_y = 0, .width = frame.width(), .height = frame.height()}); - int ret = layer.encoder().Encode(frame, &stream_frame_types); + + int ret = layer.Encode(frame, &stream_frame_types); if (ret != WEBRTC_VIDEO_CODEC_OK) { return ret; } @@ -616,11 +726,9 @@ int SimulcastEncoderAdapter::Encode( int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback( EncodedImageCallback* callback) { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); encoded_complete_callback_ = callback; - if (!stream_contexts_.empty() && stream_contexts_.front().stream_idx() == 0) { - // Bypass frame encode complete callback for the lowest layer since there is - // no need to override frame's spatial index. + if (bypass_mode_ && !stream_contexts_.empty()) { stream_contexts_.front().encoder().RegisterEncodeCompleteCallback(callback); } return WEBRTC_VIDEO_CODEC_OK; @@ -628,7 +736,7 @@ int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback( void SimulcastEncoderAdapter::SetRates( const RateControlParameters& parameters) { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); if (!Initialized()) { RTC_LOG(LS_WARNING) << "SetRates while not initialized"; @@ -662,7 +770,7 @@ void SimulcastEncoderAdapter::SetRates( // the encoder handling the current simulcast stream. RateControlParameters stream_parameters = parameters; stream_parameters.bitrate = VideoBitrateAllocation(); - for (int i = 0; i < kMaxTemporalStreams; ++i) { + for (int i = 0; checked_cast(i) < kMaxTemporalStreams; ++i) { if (parameters.bitrate.HasBitrate(stream_idx, i)) { stream_parameters.bitrate.SetBitrate( 0, i, parameters.bitrate.GetBitrate(stream_idx, i)); @@ -711,27 +819,72 @@ void SimulcastEncoderAdapter::OnLossNotification( } } -// TODO(brandtr): Add task checker to this member function, when all encoder -// callbacks are coming in on the encoder queue. EncodedImageCallback::Result SimulcastEncoderAdapter::OnEncodedImage( size_t stream_idx, const EncodedImage& encodedImage, const CodecSpecificInfo* codecSpecificInfo) { EncodedImage stream_image(encodedImage); - CodecSpecificInfo stream_codec_specific = *codecSpecificInfo; - stream_image.SetSimulcastIndex(stream_idx); + if (!bypass_mode_) { + // In non-bypass mode, the adapter manages stream indices. + // Overwrite any index set by the underlying encoder. + stream_image.SetSimulcastIndex(stream_idx); + } if (codec_.IsMixedCodec()) { stream_image.SetSpatialIndex(std::nullopt); } + bool is_last_in_temporal_unit = false; + if (!bypass_mode_) { + MutexLock lock(&pending_frames_mutex_); + auto it = std::find_if(pending_frames_.begin(), pending_frames_.end(), + [&](const PendingFrame& frame) { + return frame.rtp_timestamp == + encodedImage.RtpTimestamp(); + }); + if (it != pending_frames_.end()) { + it->expected_layer_index.reset(stream_idx); + if (it->expected_layer_index.none()) { + pending_frames_.erase(it); + is_last_in_temporal_unit = true; + } + } else { + // The frame was removed from the queue (e.g. due to overflow). + // We should drop this encoded image and not propagate it. + return EncodedImageCallback::Result(EncodedImageCallback::Result::OK, + encodedImage.RtpTimestamp()); + } + stream_image.set_end_of_temporal_unit(is_last_in_temporal_unit); + } + return encoded_complete_callback_->OnEncodedImage(stream_image, - &stream_codec_specific); + codecSpecificInfo); } -void SimulcastEncoderAdapter::OnDroppedFrame(size_t /* stream_idx */) { - // Not yet implemented. +void SimulcastEncoderAdapter::OnFrameDropped(uint32_t rtp_timestamp, + int spatial_id) { + bool is_last_in_temporal_unit = false; + { + MutexLock lock(&pending_frames_mutex_); + auto it = std::find_if(pending_frames_.begin(), pending_frames_.end(), + [rtp_timestamp](const PendingFrame& frame) { + return frame.rtp_timestamp == rtp_timestamp; + }); + if (it != pending_frames_.end()) { + it->expected_layer_index.reset(spatial_id); + if (it->expected_layer_index.none()) { + pending_frames_.erase(it); + is_last_in_temporal_unit = true; + } + } else { + // Already removed, ignore. + return; + } + } + + encoded_complete_callback_->OnFrameDropped(rtp_timestamp, spatial_id, + is_last_in_temporal_unit); } bool SimulcastEncoderAdapter::Initialized() const { @@ -739,7 +892,7 @@ bool SimulcastEncoderAdapter::Initialized() const { } void SimulcastEncoderAdapter::DestroyStoredEncoders() { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); while (!cached_encoder_contexts_.empty()) { cached_encoder_contexts_.pop_back(); } @@ -749,7 +902,7 @@ std::unique_ptr SimulcastEncoderAdapter::FetchOrCreateEncoderContext( bool is_lowest_quality_stream, std::optional stream_idx) const { - RTC_DCHECK_RUN_ON(&encoder_queue_); + RTC_DCHECK_RUN_ON(&encoder_queue_checker_); if (stream_idx) { RTC_CHECK_LT(*stream_idx, codec_.numberOfSimulcastStreams); } @@ -1005,6 +1158,10 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { return encoder_info; } + // Default is to not enable CPU overuse detection. Aggregate across + // sub-encoders so we enable it if any encoder opts in. + encoder_info.enable_cpu_overuse_detection = false; + encoder_info.scaling_settings = VideoEncoder::ScalingSettings::kOff; std::vector encoder_names; // SEA can keep multiple stream contexts alive even when runtime bitrate @@ -1065,6 +1222,9 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const { encoder_info.is_qp_trusted.value_or(true) && encoder_impl_info.is_qp_trusted.value_or(true); } + // If any encoder wants CPU overuse detection, enable it for all of them + encoder_info.enable_cpu_overuse_detection |= + encoder_impl_info.enable_cpu_overuse_detection; encoder_info.fps_allocation[i] = encoder_impl_info.fps_allocation[0]; encoder_info.requested_resolution_alignment = std::lcm(encoder_info.requested_resolution_alignment, diff --git a/media/engine/simulcast_encoder_adapter.h b/media/engine/simulcast_encoder_adapter.h index a8809a2d089..99ca19d4313 100644 --- a/media/engine/simulcast_encoder_adapter.h +++ b/media/engine/simulcast_encoder_adapter.h @@ -13,8 +13,10 @@ #define MEDIA_ENGINE_SIMULCAST_ENCODER_ADAPTER_H_ #include +#include #include #include +#include #include #include #include @@ -26,6 +28,7 @@ #include "api/sequence_checker.h" #include "api/units/timestamp.h" #include "api/video/encoded_image.h" +#include "api/video/video_codec_constants.h" #include "api/video/video_frame.h" #include "api/video/video_frame_type.h" #include "api/video_codecs/sdp_video_format.h" @@ -35,8 +38,10 @@ #include "common_video/framerate_controller.h" #include "modules/video_coding/include/video_codec_interface.h" #include "rtc_base/experiments/encoder_info_settings.h" +#include "rtc_base/synchronization/mutex.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -119,10 +124,15 @@ class RTC_EXPORT SimulcastEncoderAdapter : public VideoEncoder { Result OnEncodedImage( const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info) override; - void OnDroppedFrame(DropReason reason) override; + void OnFrameDropped(uint32_t rtp_timestamp, + int spatial_id, + bool is_end_of_temporal_unit) override; + int Encode(const VideoFrame& frame, + const std::vector* frame_types); VideoEncoder& encoder() { return encoder_context_->encoder(); } const VideoEncoder& encoder() const { return encoder_context_->encoder(); } + int stream_idx() const { return stream_idx_; } uint16_t width() const { return width_; } uint16_t height() const { return height_; } @@ -144,9 +154,13 @@ class RTC_EXPORT SimulcastEncoderAdapter : public VideoEncoder { bool ShouldDropFrame(Timestamp timestamp); private: + void MaybeCullOldTimestamps(uint32_t new_rtp_timestamp); + SimulcastEncoderAdapter* const parent_; std::unique_ptr encoder_context_; std::unique_ptr framerate_controller_; + mutable Mutex queue_mutex_; + std::deque pending_rtp_timestamps_ RTC_GUARDED_BY(queue_mutex_); const int stream_idx_; const uint16_t width_; const uint16_t height_; @@ -154,6 +168,12 @@ class RTC_EXPORT SimulcastEncoderAdapter : public VideoEncoder { bool is_paused_; }; + struct PendingFrame { + uint32_t rtp_timestamp; + // Bitmask of expected stream indices (1 << stream_idx). + std::bitset expected_layer_index; + }; + bool Initialized() const; // This method creates encoder. May reuse previously created encoders from @@ -174,7 +194,7 @@ class RTC_EXPORT SimulcastEncoderAdapter : public VideoEncoder { const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info); - void OnDroppedFrame(size_t stream_idx); + void OnFrameDropped(uint32_t rtp_timestamp, int spatial_id); void OverrideFromFieldTrial(VideoEncoder::EncoderInfo* info) const; @@ -189,8 +209,18 @@ class RTC_EXPORT SimulcastEncoderAdapter : public VideoEncoder { std::vector stream_contexts_; EncodedImageCallback* encoded_complete_callback_; + // TODO: webrtc:467444018 - Remove this mutex and post any callback to + // OnEncodedImage/OnFrameDropped to the encoder queue instead if not already + // on it. + mutable Mutex pending_frames_mutex_; + // A queue of pending frames for which we have not yet received a callback + // (either an encoded frame or frame drop notification), sorted by their + // RTP timestamp. + std::deque pending_frames_ + RTC_GUARDED_BY(pending_frames_mutex_); + // Used for checking the single-threaded access of the encoder interface. - RTC_NO_UNIQUE_ADDRESS SequenceChecker encoder_queue_; + RTC_NO_UNIQUE_ADDRESS SequenceChecker encoder_queue_checker_; // Store previously created and released encoders , so they don't have to be // recreated. Remaining encoders are destroyed by the destructor. diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc index 956643aa6e7..fe23245a7ef 100644 --- a/media/engine/simulcast_encoder_adapter_unittest.cc +++ b/media/engine/simulcast_encoder_adapter_unittest.cc @@ -10,16 +10,20 @@ #include "media/engine/simulcast_encoder_adapter.h" +#include +#include + #include -#include -#include +#include #include #include #include +#include #include #include #include "absl/container/inlined_vector.h" +#include "absl/functional/any_invocable.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" #include "api/fec_controller_override.h" @@ -27,12 +31,17 @@ #include "api/make_ref_counted.h" #include "api/rtp_parameters.h" #include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_base.h" +#include "api/task_queue/task_queue_factory.h" #include "api/test/create_simulcast_test_fixture.h" +#include "api/test/mock_video_bitrate_allocator.h" #include "api/test/mock_video_decoder.h" +#include "api/test/mock_video_encoder.h" #include "api/test/simulcast_test_fixture.h" #include "api/test/video/function_video_decoder_factory.h" #include "api/test/video/function_video_encoder_factory.h" #include "api/units/data_rate.h" +#include "api/units/timestamp.h" #include "api/video/encoded_image.h" #include "api/video/i420_buffer.h" #include "api/video/video_bitrate_allocation.h" @@ -56,9 +65,14 @@ #include "modules/video_coding/utility/simulcast_rate_allocator.h" #include "modules/video_coding/utility/simulcast_test_fixture_impl.h" #include "rtc_base/checks.h" +#include "rtc_base/cpu_info.h" +#include "rtc_base/event.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" using ::testing::_; using ::testing::Return; @@ -195,6 +209,7 @@ class MockVideoEncoder; class MockVideoEncoderFactory : public VideoEncoderFactory { public: + explicit MockVideoEncoderFactory(const Environment& env) : env_(env) {} std::vector GetSupportedFormats() const override; std::unique_ptr Create(const Environment& env, @@ -220,10 +235,18 @@ class MockVideoEncoderFactory : public VideoEncoderFactory { void set_fallback_from_simulcast(std::optional return_value) { fallback_from_simulcast_ = return_value; } + TaskQueueBase* GetAsyncEncoderQueue() { + if (!async_encoder_queue_) { + async_encoder_queue_ = env_.task_queue_factory().CreateTaskQueue( + "AsyncEncoderQueue", TaskQueueFactory::Priority::NORMAL); + } + return async_encoder_queue_.get(); + } void DestroyVideoEncoder(VideoEncoder* encoder); private: + const Environment& env_; bool create_video_encoder_return_nullptr_ = false; int32_t init_encode_return_value_ = 0; std::optional fallback_from_simulcast_; @@ -233,6 +256,7 @@ class MockVideoEncoderFactory : public VideoEncoderFactory { std::vector requested_resolution_alignments_ = {1, 1, 1}; bool supports_simulcast_ = false; std::vector resolution_bitrate_limits_; + std::unique_ptr async_encoder_queue_; }; class MockVideoEncoder : public VideoEncoder { @@ -269,7 +293,11 @@ class MockVideoEncoder : public VideoEncoder { return 0; } - MOCK_METHOD(int32_t, Release, (), (override)); + MOCK_METHOD(int32_t, ReleaseMock, ()); + int32_t Release() override { + RunUntilIdle(); + return ReleaseMock(); + } void SetRates(const RateControlParameters& parameters) override { last_set_rates_ = parameters; @@ -285,6 +313,7 @@ class MockVideoEncoder : public VideoEncoder { apply_alignment_to_all_simulcast_layers_; info.has_trusted_rate_controller = has_trusted_rate_controller_; info.is_hardware_accelerated = is_hardware_accelerated_; + info.enable_cpu_overuse_detection = enable_cpu_overuse_detection_; info.fps_allocation[0] = fps_allocation_; info.supports_simulcast = supports_simulcast_; info.is_qp_trusted = is_qp_trusted_; @@ -299,16 +328,40 @@ class MockVideoEncoder : public VideoEncoder { EncodedImageCallback* callback() const { return callback_; } - void SendEncodedImage(int width, int height) { + void SendEncodedImage(int width, + int height, + uint32_t rtp_timestamp = 0, + std::optional simulcast_index = std::nullopt) { // Sends a fake image of the given width/height. EncodedImage image; image._encodedWidth = width; image._encodedHeight = height; + image.SetRtpTimestamp(rtp_timestamp); + if (simulcast_index.has_value()) { + image.SetSimulcastIndex(*simulcast_index); + } CodecSpecificInfo codec_specific_info; codec_specific_info.codecType = kVideoCodecVP8; callback_->OnEncodedImage(image, &codec_specific_info); } + void PostTask(absl::AnyInvocable task) { + if (is_async_) { + factory_->GetAsyncEncoderQueue()->PostTask(std::move(task)); + } else { + std::move(task)(); + } + } + + void set_is_async(bool is_async) { + if (is_async_ && !is_async) { + // Turning from asynchronous to synchronous. Wait for any pending tasks to + // complete. + RunUntilIdle(); + } + is_async_ = is_async; + } + void set_supports_native_handle(bool enabled) { supports_native_handle_ = enabled; } @@ -346,6 +399,10 @@ class MockVideoEncoder : public VideoEncoder { is_hardware_accelerated_ = is_hardware_accelerated; } + void set_enable_cpu_overuse_detection(bool enable) { + enable_cpu_overuse_detection_ = enable; + } + void set_fps_allocation(const FramerateFractions& fps_allocation) { fps_allocation_ = fps_allocation; } @@ -376,6 +433,13 @@ class MockVideoEncoder : public VideoEncoder { SdpVideoFormat video_format() const { return video_format_; } private: + void RunUntilIdle() { + // This is dangerous, only do it here for testing. + Event event; + factory_->GetAsyncEncoderQueue()->PostTask([&event] { event.Set(); }); + event.Wait(Event::kForever); + } + MockVideoEncoderFactory* const factory_; bool supports_native_handle_ = false; std::string implementation_name_ = "unknown"; @@ -384,6 +448,7 @@ class MockVideoEncoder : public VideoEncoder { bool apply_alignment_to_all_simulcast_layers_ = false; bool has_trusted_rate_controller_ = false; bool is_hardware_accelerated_ = false; + bool enable_cpu_overuse_detection_ = true; int32_t init_encode_return_value_ = 0; std::optional fallback_from_simulcast_; VideoEncoder::RateControlParameters last_set_rates_; @@ -393,6 +458,7 @@ class MockVideoEncoder : public VideoEncoder { std::optional min_qp_; SdpVideoFormat video_format_; std::vector resolution_bitrate_limits; + bool is_async_ = false; VideoCodec codec_; EncodedImageCallback* callback_; @@ -455,8 +521,9 @@ class TestSimulcastEncoderAdapterFakeHelper { bool use_fallback_factory, const SdpVideoFormat& video_format) : env_(env), + primary_factory_(env_), fallback_factory_(use_fallback_factory - ? std::make_unique() + ? std::make_unique(env_) : nullptr), video_format_(video_format) {} @@ -496,6 +563,7 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, env_, use_fallback_factory_, SdpVideoFormat("VP8", sdp_video_parameters_)); adapter_ = helper_->CreateMockEncoderAdapter(); + MutexLock lock(&mutex_); last_encoded_image_width_ = std::nullopt; last_encoded_image_height_ = std::nullopt; last_encoded_image_simulcast_index_ = std::nullopt; @@ -514,6 +582,7 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, Result OnEncodedImage( const EncodedImage& encoded_image, const CodecSpecificInfo* /* codec_specific_info */) override { + MutexLock lock(&mutex_); last_encoded_image_width_ = encoded_image._encodedWidth; last_encoded_image_height_ = encoded_image._encodedHeight; last_encoded_image_simulcast_index_ = encoded_image.SimulcastIndex(); @@ -521,9 +590,21 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, return Result(Result::OK, encoded_image.RtpTimestamp()); } + void OnFrameDropped(uint32_t rtp_timestamp, + int spatial_id, + bool is_end_of_temporal_unit) override { + dropped_frames_.emplace_back(rtp_timestamp, spatial_id, + is_end_of_temporal_unit); + } + + const std::vector>& GetDroppedFrames() const { + return dropped_frames_; + } + bool GetLastEncodedImageInfo(std::optional* out_width, std::optional* out_height, std::optional* out_simulcast_index) { + MutexLock lock(&mutex_); if (!last_encoded_image_width_.has_value()) { return false; } @@ -677,16 +758,19 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, protected: FieldTrials field_trials_ = CreateTestFieldTrials(); - Environment env_ = CreateEnvironment(field_trials_.CreateCopy()); + Environment env_ = EnvironmentFactory().Create(); std::unique_ptr helper_; std::unique_ptr adapter_; VideoCodec codec_; - std::optional last_encoded_image_width_; - std::optional last_encoded_image_height_; - std::optional last_encoded_image_simulcast_index_; - std::unique_ptr rate_allocator_; + std::optional last_encoded_image_width_ RTC_GUARDED_BY(mutex_); + std::optional last_encoded_image_height_ RTC_GUARDED_BY(mutex_); + std::optional last_encoded_image_simulcast_index_ RTC_GUARDED_BY(mutex_); + Mutex mutex_; + std::unique_ptr rate_allocator_; bool use_fallback_factory_; CodecParameterMap sdp_video_parameters_; + test::RunLoop run_loop_; + std::vector> dropped_frames_; }; TEST_F(TestSimulcastEncoderAdapterFake, InitEncode) { @@ -722,7 +806,7 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) { // Set bitrates so that we send all layers. adapter_->SetRates(VideoEncoder::RateControlParameters( - rate_allocator_->Allocate(VideoBitrateAllocationParameters(1200, 30)), + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), 30.0)); // At this point, the simulcast encoder adapter should have 3 streams: HD, @@ -732,6 +816,17 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) { // image. std::vector encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, encoders.size()); + + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .set_timestamp_ms(0) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + encoders[0]->SendEncodedImage(1152, 704); std::optional width; std::optional height; @@ -741,8 +836,9 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) { EXPECT_EQ(1152, width.value()); ASSERT_TRUE(height.has_value()); EXPECT_EQ(704, height.value()); - // SEA doesn't intercept frame encode complete callback for the lowest stream. - EXPECT_FALSE(simulcast_index.has_value()); + // SEA should intercept frame encode complete callback for all streams. + EXPECT_TRUE(simulcast_index.has_value()); + EXPECT_EQ(0, simulcast_index.value()); encoders[1]->SendEncodedImage(300, 620); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); @@ -763,10 +859,102 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) { EXPECT_EQ(2, simulcast_index.value()); } -// This test verifies that the underlying encoders are reused, when the adapter -// is reinited with different number of simulcast streams. It further checks -// that the allocated encoders are reused in the same order as before, starting -// with the lowest stream. +TEST_F(TestSimulcastEncoderAdapterFake, + EncodedCallbackForDifferentEncodersOverwritesIndex) { + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + std::vector names; + names.push_back("codec1"); + names.push_back("codec2"); + names.push_back("codec3"); + helper_->factory()->SetEncoderNames(names); + adapter_->RegisterEncodeCompleteCallback(this); + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + + // Initialize rate allocator. + rate_allocator_ = std::make_unique(env_, codec_); + + // Set bitrates so that we send all layers. + adapter_->SetRates(VideoEncoder::RateControlParameters( + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), + 30.0)); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(3u, encoders.size()); + + // Trigger an Encode call to populate pending_frames for non-bypass mode. + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(1000) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + + // Have the 2nd encoder (stream 1) send a frame with simulcast_index = 0. + // This simulates an encoder that doesn't know its own index. + // We expect SEA to overwrite it to 1. + encoders[1]->SendEncodedImage(640, 360, 100, /*simulcast_index=*/0); + + std::optional last_simulcast_index; + std::optional width; + std::optional height; + ASSERT_TRUE(GetLastEncodedImageInfo(&width, &height, &last_simulcast_index)); + EXPECT_EQ(1, last_simulcast_index); +} + +TEST_F(TestSimulcastEncoderAdapterFake, + EncodedCallbackInBypassModeDoesNotOverwriteIndex) { + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + std::vector names; + names.push_back("codec1"); + // Supports simulcast, so SEA enters bypass mode for single stream. + helper_->factory()->set_supports_simulcast(true); + helper_->factory()->SetEncoderNames(names); + adapter_->RegisterEncodeCompleteCallback(this); + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + + // Trigger an Encode call to populate pending_frames. + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(1000) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + + // Get the single encoder. + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(1u, encoders.size()); + + // 1. Send frame with missing simulcast index. + // SEA should NOT add an index (bypass mode). + encoders[0]->SendEncodedImage(1280, 720, 100, + /*simulcast_index=*/std::nullopt); + std::optional last_simulcast_index; + std::optional width; + std::optional height; + ASSERT_TRUE(GetLastEncodedImageInfo(&width, &height, &last_simulcast_index)); + EXPECT_FALSE(last_simulcast_index.has_value()); + + // 2. Send frame with existing simulcast index. + // SEA should preserve it. + encoders[0]->SendEncodedImage(1280, 720, 100, /*simulcast_index=*/0); + ASSERT_TRUE(GetLastEncodedImageInfo(&width, &height, &last_simulcast_index)); + EXPECT_EQ(0, last_simulcast_index); +} + +// This test verifies that the underlying encoders are reused, when the +// adapter is reinited with different number of simulcast streams. It further +// checks that the allocated encoders are reused in the same order as before, +// starting with the lowest stream. TEST_F(TestSimulcastEncoderAdapterFake, ReusesEncodersInOrder) { // Set up common settings for three streams. SimulcastTestFixtureImpl::DefaultSettings( @@ -808,11 +996,11 @@ TEST_F(TestSimulcastEncoderAdapterFake, ReusesEncodersInOrder) { .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); - EXPECT_CALL(*original_encoders[0], Release()) + EXPECT_CALL(*original_encoders[0], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); - EXPECT_CALL(*original_encoders[1], Release()) + EXPECT_CALL(*original_encoders[1], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); - EXPECT_CALL(*original_encoders[2], Release()) + EXPECT_CALL(*original_encoders[2], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); @@ -835,9 +1023,9 @@ TEST_F(TestSimulcastEncoderAdapterFake, ReusesEncodersInOrder) { .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(2, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); - EXPECT_CALL(*original_encoders[0], Release()) + EXPECT_CALL(*original_encoders[0], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); - EXPECT_CALL(*original_encoders[1], Release()) + EXPECT_CALL(*original_encoders[1], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); @@ -857,7 +1045,7 @@ TEST_F(TestSimulcastEncoderAdapterFake, ReusesEncodersInOrder) { .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(1, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); - EXPECT_CALL(*original_encoders[0], Release()) + EXPECT_CALL(*original_encoders[0], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); @@ -883,11 +1071,11 @@ TEST_F(TestSimulcastEncoderAdapterFake, ReusesEncodersInOrder) { .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); - EXPECT_CALL(*original_encoders[0], Release()) + EXPECT_CALL(*original_encoders[0], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); - EXPECT_CALL(*new_encoders[1], Release()) + EXPECT_CALL(*new_encoders[1], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); - EXPECT_CALL(*new_encoders[2], Release()) + EXPECT_CALL(*new_encoders[2], ReleaseMock()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); } @@ -943,29 +1131,310 @@ TEST_F(TestSimulcastEncoderAdapterFake, ReinitDoesNotReorderEncoderSettings) { } } -// This test is similar to the one above, except that it tests the simulcastIdx -// from the CodecSpecificInfo that is connected to an encoded frame. The -// PayloadRouter demuxes the incoming encoded frames on different RTP modules -// using the simulcastIdx, so it's important that there is no corresponding -// encoder reordering in between adapter reinits as this would lead to PictureID -// discontinuities. +TEST_F(TestSimulcastEncoderAdapterFake, FrameDroppingAllLayers) { + SetupCodec(); + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 100000); + allocation.SetBitrate(1, 0, 500000); + allocation.SetBitrate(2, 0, 1500000); + auto mock_allocator = std::make_unique(); + EXPECT_CALL(*mock_allocator, Allocate(_)).WillRepeatedly(Return(allocation)); + rate_allocator_ = std::move(mock_allocator); + adapter_->SetRates(VideoEncoder::RateControlParameters(allocation, 30.0)); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(3u, encoders.size()); + + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(1000) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_CALL(*encoders[0], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[1], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[2], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + + // Simulate all 3 internal encoders dropping the frame. + encoders[0]->callback()->OnFrameDropped(100, 0, true); + encoders[1]->callback()->OnFrameDropped(100, 0, true); + encoders[2]->callback()->OnFrameDropped(100, 0, true); + + // We should receive 3 drops. + // The first two should NOT match the end of temporal unit logic, + // but the LAST one (which empties the pending list for this timestamp) + // SHOULD. Note: OnFrameDropped in adapter calls callback with (timestamp, + // stream_idx). The spatial indices are 0, 1, 2. + auto dropped = GetDroppedFrames(); + ASSERT_EQ(3u, dropped.size()); + EXPECT_EQ(dropped[0], std::make_tuple(100u, 0, false)); + EXPECT_EQ(dropped[1], std::make_tuple(100u, 1, false)); + EXPECT_EQ(dropped[2], std::make_tuple(100u, 2, true)); +} + +TEST_F(TestSimulcastEncoderAdapterFake, FrameDroppingMixed) { + SetupCodec(); + adapter_->SetRates(VideoEncoder::RateControlParameters( + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), + 30.0)); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(3u, encoders.size()); + + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(1000) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_CALL(*encoders[0], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[1], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[2], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + + // Layer 0 sends encoded image. + encoders[0]->SendEncodedImage(1152, 704, 100); + // Layer 1 drops frame. + encoders[1]->callback()->OnFrameDropped(100, 0, true); + // Layer 2 sends encoded image. + encoders[2]->SendEncodedImage(120, 240, 100); + + // Verify encoded images. + std::optional width; + std::optional height; + std::optional simulcast_index; + EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); + // The last encoded image was from layer 2. + ASSERT_TRUE(width.has_value()); + EXPECT_EQ(120, width.value()); + + // Verify dropped frame. + auto dropped = GetDroppedFrames(); + ASSERT_EQ(1u, dropped.size()); + // Stream index 1. + EXPECT_EQ(dropped[0], std::make_tuple(100u, 1, false)); +} + +TEST_F(TestSimulcastEncoderAdapterFake, FrameDroppingOutOfOrder) { + SetupCodec(); + adapter_->SetRates(VideoEncoder::RateControlParameters( + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), + 30.0)); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(3u, encoders.size()); + + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(1000) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_CALL(*encoders[0], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[1], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[2], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + + // Simulate out of order completion. + // Layer 2 finishes FIRST (dropped). + encoders[2]->callback()->OnFrameDropped(100, 0, true); + auto dropped = GetDroppedFrames(); + ASSERT_EQ(1u, dropped.size()); + EXPECT_EQ(dropped[0], std::make_tuple(100u, 2, false)); + + // Layer 0 finishes SECOND (encoded). + encoders[0]->SendEncodedImage(1152, 704, 100); + // No new dropped frame. + EXPECT_EQ(1u, GetDroppedFrames().size()); + + // Layer 1 finishes LAST (dropped). + encoders[1]->callback()->OnFrameDropped(100, 0, true); + dropped = GetDroppedFrames(); + ASSERT_EQ(2u, dropped.size()); + // This last drop should have is_end_of_temporal_unit = true. + EXPECT_EQ(dropped[1], std::make_tuple(100u, 1, true)); +} + +TEST_F(TestSimulcastEncoderAdapterFake, InterleavedFrames) { + SetupCodec(); + adapter_->SetRates(VideoEncoder::RateControlParameters( + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), + 30.0)); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(3u, encoders.size()); + + // Frame 1 (ts=100) + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame1 = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(1000) + .set_rotation(kVideoRotation_0) + .build(); + // Frame 2 (ts=200) + VideoFrame input_frame2 = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(200) + .set_timestamp_ms(2000) + .set_rotation(kVideoRotation_0) + .build(); + + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + + // Expect Encode calls. + EXPECT_CALL(*encoders[0], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[1], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[2], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + + EXPECT_EQ(0, adapter_->Encode(input_frame1, &frame_types)); + EXPECT_EQ(0, adapter_->Encode(input_frame2, &frame_types)); + + // Interleaved results. + // Frame 1 Layer 0 (Dropped) + encoders[0]->callback()->OnFrameDropped(100, 0, true); + auto dropped = GetDroppedFrames(); + ASSERT_EQ(1u, dropped.size()); + EXPECT_EQ(dropped[0], std::make_tuple(100u, 0, false)); + + // Frame 2 Layer 0 (Dropped) + encoders[0]->callback()->OnFrameDropped(200, 0, true); + dropped = GetDroppedFrames(); + ASSERT_EQ(2u, dropped.size()); + EXPECT_EQ(dropped[1], std::make_tuple(200u, 0, false)); + + // Frame 2 Layer 1 (Dropped) + encoders[1]->callback()->OnFrameDropped(200, 0, true); + dropped = GetDroppedFrames(); + ASSERT_EQ(4u, dropped.size()); + EXPECT_EQ(dropped[2], std::make_tuple(100u, 1, false)); + EXPECT_EQ(dropped[3], std::make_tuple(200u, 1, false)); + + // We still have Frame 1 Layer 2 and Frame 2 Layer 2 pending. + // Finish Frame 1 Layer 2 (Dropped) -> This completes Frame 1! + encoders[2]->callback()->OnFrameDropped(100, 0, true); + dropped = GetDroppedFrames(); + ASSERT_EQ(5u, dropped.size()); + EXPECT_EQ(dropped[4], + std::make_tuple(100u, 2, true)); // End of TU for Frame 1 + + // Finish Frame 2 Layer 2 (Dropped) -> This completes Frame 2! + encoders[2]->callback()->OnFrameDropped(200, 0, true); + dropped = GetDroppedFrames(); + ASSERT_EQ(6u, dropped.size()); + EXPECT_EQ(dropped[5], + std::make_tuple(200u, 2, true)); // End of TU for Frame 2 +} + +TEST_F(TestSimulcastEncoderAdapterFake, OldFramesAreCulled) { + SetupCodec(); + adapter_->SetRates(VideoEncoder::RateControlParameters( + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), + 30.0)); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(3u, encoders.size()); + + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame1 = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(1000) + .set_rotation(kVideoRotation_0) + .build(); + VideoFrame input_frame2 = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(200) + .set_timestamp_ms(2000) + .set_rotation(kVideoRotation_0) + .build(); + + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + + EXPECT_CALL(*encoders[0], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[1], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[2], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + + EXPECT_EQ(0, adapter_->Encode(input_frame1, &frame_types)); + EXPECT_EQ(0, adapter_->Encode(input_frame2, &frame_types)); + + encoders[0]->callback()->OnFrameDropped(200, 0, true); + + auto dropped = GetDroppedFrames(); + ASSERT_GE(dropped.size(), 2u); + EXPECT_EQ(dropped[0], std::make_tuple(100u, 0, false)); + EXPECT_EQ(dropped[1], std::make_tuple(200u, 0, false)); + + // What about Stream 1? It hasn't received anything yet. + // Send Frame 2 for Stream 1. + encoders[1]->callback()->OnFrameDropped(200, 0, true); + dropped = GetDroppedFrames(); + ASSERT_GE(dropped.size(), 4u); + EXPECT_EQ( + dropped[2], + std::make_tuple(100u, 1, false)); // Implicit drop of 100 on Stream 1 + EXPECT_EQ( + dropped[3], + std::make_tuple(200u, 1, false)); // Explicit drop of 200 on Stream 1 +} + +// This test is similar to the one above, except that it tests the +// simulcastIdx from the CodecSpecificInfo that is connected to an encoded +// frame. The PayloadRouter demuxes the incoming encoded frames on different +// RTP modules using the simulcastIdx, so it's important that there is no +// corresponding encoder reordering in between adapter reinits as this would +// lead to PictureID discontinuities. TEST_F(TestSimulcastEncoderAdapterFake, ReinitDoesNotReorderFrameSimulcastIdx) { SetupCodec(); adapter_->SetRates(VideoEncoder::RateControlParameters( - rate_allocator_->Allocate(VideoBitrateAllocationParameters(1200, 30)), + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), 30.0)); VerifyCodecSettings(); // Send frames on all streams. std::vector encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, encoders.size()); + + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .set_timestamp_ms(0) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + encoders[0]->SendEncodedImage(1152, 704); std::optional width; std::optional height; std::optional simulcast_index; EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); - // SEA doesn't intercept frame encode complete callback for the lowest stream. - EXPECT_FALSE(simulcast_index.has_value()); + // SEA should intercept frame encode complete callback for all streams. + ASSERT_TRUE(simulcast_index.has_value()); + EXPECT_EQ(0, simulcast_index.value()); encoders[1]->SendEncodedImage(300, 620); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); @@ -981,13 +1450,16 @@ TEST_F(TestSimulcastEncoderAdapterFake, ReinitDoesNotReorderFrameSimulcastIdx) { EXPECT_EQ(0, adapter_->Release()); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(VideoEncoder::RateControlParameters( - rate_allocator_->Allocate(VideoBitrateAllocationParameters(1200, 30)), + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), 30.0)); - // Verify that the same encoder sends out frames on the same simulcast index. + // Verify that the same encoder sends out frames on the same simulcast + // index. + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); encoders[0]->SendEncodedImage(1152, 704); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); - EXPECT_FALSE(simulcast_index.has_value()); + ASSERT_TRUE(simulcast_index.has_value()); + EXPECT_EQ(0, simulcast_index.value()); encoders[1]->SendEncodedImage(300, 620); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); @@ -1371,6 +1843,68 @@ TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) { adapter_->Encode(input_frame, &frame_types)); } +TEST_F(TestSimulcastEncoderAdapterFake, TestPendingFramesQueueLimit) { + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + std::vector names; + names.push_back("codec1"); + names.push_back("codec2"); + names.push_back("codec3"); + helper_->factory()->SetEncoderNames(names); + adapter_->RegisterEncodeCompleteCallback(this); + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + rate_allocator_ = std::make_unique(env_, codec_); + adapter_->SetRates(VideoEncoder::RateControlParameters( + rate_allocator_->Allocate(VideoBitrateAllocationParameters(5000000, 30)), + 30.0)); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(3u, encoders.size()); + + // Encoders will accept the frame but NOT call OnEncodedImage, duplicating a + // stuck encoder scenario. + EXPECT_CALL(*encoders[0], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[1], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(*encoders[2], Encode(_, _)) + .WillRepeatedly(Return(WEBRTC_VIDEO_CODEC_OK)); + + // Fill the queue up to the limit (15). + for (int i = 0; i < 15; ++i) { + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100 + i * 10) + .set_timestamp_ms(1000 + i * 100) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + } + + // The 16th frame should cause the 1st frame (timestamp 100) to be dropped. + // Expect OnFrameDropped for timestamp 100, spatial_idx 0, 1, 2. + // 3 streams active. + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + VideoFrame input_frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(9000) + .set_timestamp_ms(9000) + .set_rotation(kVideoRotation_0) + .build(); + std::vector frame_types(3, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); + + const std::vector>& dropped_frames = + GetDroppedFrames(); + ASSERT_EQ(3u, dropped_frames.size()); + EXPECT_EQ(std::make_tuple(100u, 0, false), dropped_frames[0]); + EXPECT_EQ(std::make_tuple(100u, 1, false), dropped_frames[1]); + EXPECT_EQ(std::make_tuple(100u, 2, true), dropped_frames[2]); +} + TEST_F(TestSimulcastEncoderAdapterFake, TestInitFailureCleansUpEncoders) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), @@ -1557,6 +2091,28 @@ TEST_F(TestSimulcastEncoderAdapterFake, ReportsHardwareAccelerated) { EXPECT_TRUE(adapter_->GetEncoderInfo().is_hardware_accelerated); } +TEST_F(TestSimulcastEncoderAdapterFake, ReportsEnableCpuOveruseDetection) { + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + codec_.numberOfSimulcastStreams = 3; + adapter_->RegisterEncodeCompleteCallback(this); + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + ASSERT_EQ(3u, helper_->factory()->encoders().size()); + + // All encoders opt out, so simulcast adapter should report false. + for (MockVideoEncoder* encoder : helper_->factory()->encoders()) { + encoder->set_enable_cpu_overuse_detection(false); + } + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + EXPECT_FALSE(adapter_->GetEncoderInfo().enable_cpu_overuse_detection); + + // One encoder opts in, so simulcast adapter should report true. + helper_->factory()->encoders()[1]->set_enable_cpu_overuse_detection(true); + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + EXPECT_TRUE(adapter_->GetEncoderInfo().enable_cpu_overuse_detection); +} + TEST_F(TestSimulcastEncoderAdapterFake, ReportsLeastCommonMultipleOfRequestedResolutionAlignments) { SimulcastTestFixtureImpl::DefaultSettings( @@ -2217,14 +2773,14 @@ TEST_F(TestSimulcastEncoderAdapterFake, EXPECT_EQ(helper_->factory()->encoders()[0], prev_encoder); // Singlecast, an upper stream is active. Encoder should be recreated. - EXPECT_CALL(*prev_encoder, Release()).Times(1); + EXPECT_CALL(*prev_encoder, ReleaseMock()).Times(1); SetupCodec(/*active_streams=*/{false, true}); ASSERT_EQ(1u, helper_->factory()->encoders().size()); EXPECT_NE(helper_->factory()->encoders()[0], prev_encoder); // Singlecast, the lowest stream is active. Encoder should be recreated. prev_encoder = helper_->factory()->encoders()[0]; - EXPECT_CALL(*prev_encoder, Release()).Times(1); + EXPECT_CALL(*prev_encoder, ReleaseMock()).Times(1); SetupCodec(/*active_streams=*/{true, false}); ASSERT_EQ(1u, helper_->factory()->encoders().size()); EXPECT_NE(helper_->factory()->encoders()[0], prev_encoder); @@ -2425,5 +2981,117 @@ TEST_F(TestSimulcastEncoderAdapterFake, adapter_->Encode(input_frame, &frame_types)); } +// Only run this test under TSAN, otherwise it doesn't make much sense and +// mostly wastes CPU resources. +#if defined(THREAD_SANITIZER) +#define MAYBE_ConcurrentEncodeAndOnEncodedImage \ + ConcurrentEncodeAndOnEncodedImage +#else +#define MAYBE_ConcurrentEncodeAndOnEncodedImage \ + DISABLED_ConcurrentEncodeAndOnEncodedImage +#endif + +TEST_F(TestSimulcastEncoderAdapterFake, + MAYBE_ConcurrentEncodeAndOnEncodedImage) { + // The test setup here is quite complex, but that is needed in order to make + // sure tools like TSAN is able to reliable detect issue in the wrapper + // encoder instances used for each stream. The code below makes sure that + // there is concurrent usage of both an `Encode()` to request encoding of + // a new frame at the same time that an encoder is calling `OnEncodedImage()` + // on a separate thread. + + if (cpu_info::DetectNumberOfCores() <= 2) { + GTEST_SKIP() + << "Skipping test on low core count machine due to risk of flakiness."; + } + + SetUp(); + helper_->factory()->set_supports_simulcast(false); + SetupCodec({true, true}); + + std::vector encoders = helper_->factory()->encoders(); + ASSERT_EQ(2u, encoders.size()); + encoders[0]->set_is_async(true); + encoders[1]->set_is_async(true); + + std::atomic wait_for_second_frame{false}; + + EXPECT_CALL(*encoders[0], ReleaseMock()).WillRepeatedly(testing::Return(0)); + EXPECT_CALL(*encoders[1], ReleaseMock()).WillRepeatedly(testing::Return(0)); + + MockEncodedImageCallback mock_encoded_image_callback; + EXPECT_CALL(mock_encoded_image_callback, OnEncodedImage) + .WillRepeatedly(Return( + EncodedImageCallback::Result(EncodedImageCallback::Result::OK))); + adapter_->RegisterEncodeCompleteCallback(&mock_encoded_image_callback); + EXPECT_CALL(*encoders[0], Encode) + .WillRepeatedly([&wait_for_second_frame, encoders]( + const VideoFrame& frame, + const std::vector* types) { + if (frame.rtp_timestamp() == 100) { + encoders[0]->PostTask([width = frame.width(), height = frame.height(), + rtp_timestamp = frame.rtp_timestamp(), + &wait_for_second_frame, + encoder = encoders[0]]() { + // Spin until the main thread is just about to encode the second + // frame. Using relaxed memory order prevents TSAN from seeing a + // happens-before edge but abort after 1000000 iterations. + constexpr int kMaxLoops = 1000000; + int loops = 0; + while (!wait_for_second_frame.load(std::memory_order_relaxed) && + ++loops <= kMaxLoops) { + } + EXPECT_LT(loops, kMaxLoops); + encoder->SendEncodedImage(width, height, rtp_timestamp, + /*simulcast_index=*/1); + }); + } + return WEBRTC_VIDEO_CODEC_OK; + }); + + EXPECT_CALL(*encoders[1], Encode) + .WillRepeatedly([&](const VideoFrame& frame, + const std::vector* types) { + encoders[1]->SendEncodedImage(frame.width() / 2, frame.height() / 2, + frame.rtp_timestamp(), + /*simulcast_index=*/0); + return WEBRTC_VIDEO_CODEC_OK; + }); + + scoped_refptr buffer(I420Buffer::Create(1280, 720)); + + std::vector frame_types0{VideoFrameType::kVideoFrameKey, + VideoFrameType::kVideoFrameKey}; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + adapter_->Encode(VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(100) + .set_timestamp_ms(0) + .build(), + &frame_types0)); + + std::vector frame_types1{VideoFrameType::kVideoFrameDelta, + VideoFrameType::kVideoFrameDelta}; + + // Signal the async thread to proceed. To maximize the chance of a collision, + // we use a relaxed atomic flag which TSAN won't treat as a happens-before + // edge. + wait_for_second_frame.store(true, std::memory_order_relaxed); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + adapter_->Encode(VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(90100) + .set_timestamp_ms(1000) + .build(), + &frame_types1)); + + // The callbacks should have executed via the task queue, but we don't have + // strict ordering guarantees on the main thread until we call adapter + // Release, which will indirectly call Release on the encoders, flushing + // their queues. + EXPECT_EQ(0, adapter_->Release()); +} + } // namespace test } // namespace webrtc diff --git a/media/engine/webrtc_media_engine.cc b/media/engine/webrtc_media_engine.cc index 17008920272..4f586d5efcc 100644 --- a/media/engine/webrtc_media_engine.cc +++ b/media/engine/webrtc_media_engine.cc @@ -11,13 +11,12 @@ #include "media/engine/webrtc_media_engine.h" #include -#include +#include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/rtp_parameters.h" #include "api/transport/bitrate_settings.h" @@ -31,7 +30,7 @@ namespace { // Remove mutually exclusive extensions with lower priority. void DiscardRedundantExtensions( std::vector* extensions, - ArrayView extensions_decreasing_prio) { + std::span extensions_decreasing_prio) { RTC_DCHECK(extensions); bool found = false; for (const char* uri : extensions_decreasing_prio) { @@ -47,63 +46,6 @@ void DiscardRedundantExtensions( } } // namespace -bool ValidateRtpExtensions(ArrayView extensions, - ArrayView old_extensions) { - bool id_used[1 + RtpExtension::kMaxId] = {false}; - for (const auto& extension : extensions) { - if (extension.id < RtpExtension::kMinId || - extension.id > RtpExtension::kMaxId) { - RTC_LOG(LS_ERROR) << "Bad RTP extension ID: " << extension.ToString(); - return false; - } - if (id_used[extension.id]) { - RTC_LOG(LS_ERROR) << "Duplicate RTP extension ID: " - << extension.ToString(); - return false; - } - id_used[extension.id] = true; - } - // Validate the extension list against the already negotiated extensions. - // Re-registering is OK, re-mapping (either same URL at new ID or same - // ID used with new URL) is an illegal remap. - - // This is required in order to avoid a crash when registering an - // extension. A better structure would use the registered extensions - // in the RTPSender. This requires spinning through: - // - // WebRtcVoiceMediaChannel::::WebRtcAudioSendStream::stream_ (pointer) - // AudioSendStream::rtp_rtcp_module_ (pointer) - // ModuleRtpRtcpImpl2::rtp_sender_ (pointer) - // RtpSenderContext::packet_generator (struct member) - // RTPSender::rtp_header_extension_map_ (class member) - // - // Getting at this seems like a hard slog. - if (!old_extensions.empty()) { - absl::string_view urimap[1 + RtpExtension::kMaxId]; - std::map idmap; - for (const auto& old_extension : old_extensions) { - urimap[old_extension.id] = old_extension.uri; - idmap[old_extension.uri] = old_extension.id; - } - for (const auto& extension : extensions) { - if (!urimap[extension.id].empty() && - urimap[extension.id] != extension.uri) { - RTC_LOG(LS_ERROR) << "Extension negotiation failure: " << extension.id - << " was mapped to " << urimap[extension.id] - << " but is proposed changed to " << extension.uri; - return false; - } - const auto& it = idmap.find(extension.uri); - if (it != idmap.end() && it->second != extension.id) { - RTC_LOG(LS_ERROR) << "Extension negotation failure: " << extension.uri - << " was identified by " << it->second - << " but is proposed changed to " << extension.id; - return false; - } - } - } - return true; -} std::vector FilterRtpExtensions( const std::vector& extensions, @@ -111,7 +53,6 @@ std::vector FilterRtpExtensions( bool filter_redundant_extensions, const FieldTrialsView& trials) { // Don't check against old parameters; this should have been done earlier. - RTC_DCHECK(ValidateRtpExtensions(extensions, {})); RTC_DCHECK(supported); std::vector result; diff --git a/media/engine/webrtc_media_engine.h b/media/engine/webrtc_media_engine.h index 488fe3f3e8d..493548e6455 100644 --- a/media/engine/webrtc_media_engine.h +++ b/media/engine/webrtc_media_engine.h @@ -14,7 +14,6 @@ #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/rtp_parameters.h" #include "api/transport/bitrate_settings.h" @@ -22,11 +21,6 @@ namespace webrtc { -// Verify that extension IDs are within 1-byte extension range and are not -// overlapping, and that they form a legal change from previously registerd -// extensions (if any). -bool ValidateRtpExtensions(ArrayView extennsions, - ArrayView old_extensions); // Discard any extensions not validated by the 'supported' predicate. Duplicate // extensions are removed if 'filter_redundant_extensions' is set, and also any diff --git a/media/engine/webrtc_media_engine_unittest.cc b/media/engine/webrtc_media_engine_unittest.cc index 69f5f1b0159..5c125b2b82d 100644 --- a/media/engine/webrtc_media_engine_unittest.cc +++ b/media/engine/webrtc_media_engine_unittest.cc @@ -11,7 +11,6 @@ #include "media/engine/webrtc_media_engine.h" #include -#include #include #include "absl/strings/string_view.h" @@ -66,67 +65,6 @@ bool IsSorted(const std::vector& extensions) { } } // namespace -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsEmptyList) { - std::vector extensions; - EXPECT_TRUE(ValidateRtpExtensions(extensions, {})); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsAllGood) { - std::vector extensions = MakeUniqueExtensions(); - EXPECT_TRUE(ValidateRtpExtensions(extensions, {})); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOutOfRangeId_Low) { - std::vector extensions = MakeUniqueExtensions(); - extensions.push_back(RtpExtension("foo", 0)); - EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOutOfRangeIdHigh) { - std::vector extensions = MakeUniqueExtensions(); - extensions.push_back(RtpExtension("foo", 256)); - EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOverlappingIdsStartOfSet) { - std::vector extensions = MakeUniqueExtensions(); - extensions.push_back(RtpExtension("foo", 1)); - EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsOverlappingIdsEndOfSet) { - std::vector extensions = MakeUniqueExtensions(); - extensions.push_back(RtpExtension("foo", 255)); - EXPECT_FALSE(ValidateRtpExtensions(extensions, {})); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsEmptyToEmpty) { - std::vector extensions; - EXPECT_TRUE(ValidateRtpExtensions(extensions, extensions)); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsNoChange) { - std::vector extensions = MakeUniqueExtensions(); - EXPECT_TRUE(ValidateRtpExtensions(extensions, extensions)); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsChangeIdNotUrl) { - std::vector old_extensions = MakeUniqueExtensions(); - std::vector new_extensions = old_extensions; - std::swap(new_extensions[0].id, new_extensions[1].id); - - EXPECT_FALSE(ValidateRtpExtensions(new_extensions, old_extensions)); -} - -TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsChangeIdForUrl) { - std::vector old_extensions = MakeUniqueExtensions(); - std::vector new_extensions = old_extensions; - // Change first extension to something not generated by MakeUniqueExtensions - new_extensions[0].id = 123; - - EXPECT_FALSE(ValidateRtpExtensions(new_extensions, old_extensions)); -} - TEST(WebRtcMediaEngineTest, FilterRtpExtensionsEmptyList) { std::vector extensions; FieldTrials trials = CreateTestFieldTrials(); diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index d0c1eb2a687..2f6114ff2ac 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include "absl/functional/bind_front.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/crypto/crypto_options.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/environment/environment.h" @@ -38,6 +38,7 @@ #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/priority.h" #include "api/rtc_error.h" #include "api/rtp_headers.h" @@ -67,12 +68,12 @@ #include "call/packet_receiver.h" #include "call/payload_type.h" #include "call/payload_type_picker.h" -#include "call/receive_stream.h" #include "call/rtp_config.h" #include "call/rtp_transport_controller_send_interface.h" #include "call/video_receive_stream.h" #include "call/video_send_stream.h" #include "common_video/frame_counts.h" +#include "common_video/include/quality_limitation_reason.h" #include "media/base/codec.h" #include "media/base/codec_comparators.h" #include "media/base/media_channel.h" @@ -86,7 +87,6 @@ #include "media/engine/webrtc_media_engine.h" #include "modules/rtp_rtcp/include/receive_statistics.h" #include "modules/rtp_rtcp/include/rtcp_statistics.h" -#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "modules/video_coding/svc/scalability_mode_util.h" @@ -112,8 +112,6 @@ constexpr int64_t kUnsignaledSsrcCooldownMs = kNumMillisecsPerSec / 2; // duration hasn't been implemented. const int kNackHistoryMs = 1000; -const int kDefaultRtcpReceiverReportSsrc = 1; - // Minimum time interval for logging stats. const int64_t kStatsLogIntervalMs = 10000; @@ -736,7 +734,7 @@ std::string CodecSettingsVectorToString( } void ExtractCodecInformation( - ArrayView recv_codecs, + std::span recv_codecs, std::map& rtx_associated_payload_types, std::set& raw_payload_types, std::vector& decoders) { @@ -831,10 +829,13 @@ WebRtcVideoEngine::CreateSendChannel( const MediaConfig& config, const VideoOptions& options, const CryptoOptions& crypto_options, - VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) { + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + video_encoder_switch_request_callback) { return std::make_unique( env, call, config, options, crypto_options, encoder_factory_.get(), - video_bitrate_allocator_factory); + video_bitrate_allocator_factory, + std::move(video_encoder_switch_request_callback)); } std::unique_ptr WebRtcVideoEngine::CreateReceiveChannel(const Environment& env, @@ -860,10 +861,10 @@ std::vector WebRtcVideoEngine::LegacyRecvCodecs(bool include_rtx) const { std::vector WebRtcVideoEngine::GetRtpHeaderExtensions( - const webrtc::FieldTrialsView* field_trials) const { + const FieldTrialsView* field_trials) const { // Use field trials from PeerConnection `field_trials` or from // PeerConnectionFactory `trials_`. - const webrtc::FieldTrialsView& trials = + const FieldTrialsView& trials = (field_trials != nullptr ? *field_trials : trials_); std::vector result; @@ -922,25 +923,23 @@ WebRtcVideoSendChannel::WebRtcVideoSendChannel( const VideoOptions& options, const CryptoOptions& crypto_options, VideoEncoderFactory* encoder_factory, - VideoBitrateAllocatorFactory* bitrate_allocator_factory) + VideoBitrateAllocatorFactory* bitrate_allocator_factory, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + video_encoder_switch_request_callback) : MediaChannelUtil(call->network_thread(), config.enable_dscp), env_(env), worker_thread_(call->worker_thread()), sending_(false), - receiving_(false), call_(call), - default_sink_(nullptr), video_config_(config.video), encoder_factory_(encoder_factory), bitrate_allocator_factory_(bitrate_allocator_factory), default_send_options_(options), last_send_stats_log_ms_(-1), - last_receive_stats_log_ms_(-1), - discard_unknown_ssrc_packets_(env_.field_trials().IsEnabled( - "WebRTC-Video-DiscardPacketsWithUnknownSsrc")), - crypto_options_(crypto_options) { + crypto_options_(crypto_options), + encoder_switch_request_callback_( + std::move(video_encoder_switch_request_callback)) { RTC_DCHECK_RUN_ON(&thread_checker_); - rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc; } WebRtcVideoSendChannel::~WebRtcVideoSendChannel() { @@ -1082,8 +1081,7 @@ std::vector WebRtcVideoSendChannel::SelectSendVideoCodecs( bool WebRtcVideoSendChannel::GetChangedSenderParameters( const VideoSenderParameters& params, ChangedSenderParameters* changed_params) const { - if (!ValidateCodecFormats(params.codecs) || - !ValidateRtpExtensions(params.extensions, send_rtp_extensions_)) { + if (!ValidateCodecFormats(params.codecs)) { return false; } @@ -1134,11 +1132,7 @@ bool WebRtcVideoSendChannel::GetChangedSenderParameters( } } if (needs_update) { - RTCError error = - send_stream->SetRtpParameters(rtp_parameters, nullptr); - if (error.ok() && on_rtp_send_parameters_changed_callback_) { - on_rtp_send_parameters_changed_callback_(ssrc, rtp_parameters); - } + send_stream->SetRtpParameters(rtp_parameters, nullptr); } else { RTC_DCHECK(rtp_parameters == send_stream->GetRtpParameters()); } @@ -1185,10 +1179,7 @@ bool WebRtcVideoSendChannel::GetChangedSenderParameters( if (needs_update) { RTC_DCHECK(send_stream->GetRtpParameters() != rtp_parameters); - RTCError error = send_stream->SetRtpParameters(rtp_parameters, nullptr); - if (error.ok() && on_rtp_send_parameters_changed_callback_) { - on_rtp_send_parameters_changed_callback_(ssrc, rtp_parameters); - } + send_stream->SetRtpParameters(rtp_parameters, nullptr); } } @@ -1255,64 +1246,70 @@ bool WebRtcVideoSendChannel::SetSenderParameters( return ApplyChangedParams(changed_params); } -void WebRtcVideoSendChannel::RequestEncoderFallback() { - if (!worker_thread_->IsCurrent()) { - worker_thread_->PostTask( - SafeTask(task_safety_.flag(), [this] { RequestEncoderFallback(); })); - return; +void WebRtcVideoSendChannel::RequestEncoderSwitch( + std::optional format, + bool allow_default_fallback) { + auto task = + SafeTask(task_safety_.flag(), [this, format, allow_default_fallback] { + RTC_DCHECK_RUN_ON(&thread_checker_); + ApplyEncoderSwitch(std::move(format), allow_default_fallback); + }); + if (encoder_switch_request_callback_) { + encoder_switch_request_callback_(std::move(task)); + } else { + worker_thread_->PostTask(std::move(task)); } +} +void WebRtcVideoSendChannel::ApplyEncoderSwitch( + std::optional format, + bool allow_default_fallback) { RTC_DCHECK_RUN_ON(&thread_checker_); - if (negotiated_codecs_.size() <= 1) { - RTC_LOG(LS_WARNING) << "Encoder failed but no fallback codec is available"; - return; - } + RTC_DCHECK(format.has_value() || allow_default_fallback); ChangedSenderParameters params; - params.negotiated_codecs = negotiated_codecs_; - params.negotiated_codecs->erase(params.negotiated_codecs->begin()); - params.send_codec = params.negotiated_codecs->front(); - ApplyChangedParams(params); -} - -void WebRtcVideoSendChannel::RequestEncoderSwitch(const SdpVideoFormat& format, - bool allow_default_fallback) { - if (!worker_thread_->IsCurrent()) { - worker_thread_->PostTask( - SafeTask(task_safety_.flag(), [this, format, allow_default_fallback] { - RequestEncoderSwitch(format, allow_default_fallback); - })); - return; - } - - RTC_DCHECK_RUN_ON(&thread_checker_); + if (!format) { + if (negotiated_codecs_.size() <= 1) { + RTC_LOG(LS_WARNING) + << "Encoder failed but no fallback codec is available"; + return; + } - for (const VideoCodecSettings& codec_setting : negotiated_codecs_) { - if (format.IsSameCodec( - {codec_setting.codec.name, codec_setting.codec.params})) { - VideoCodecSettings new_codec_setting = codec_setting; - for (const auto& kv : format.parameters) { - new_codec_setting.codec.params[kv.first] = kv.second; + params.negotiated_codecs = negotiated_codecs_; + params.negotiated_codecs->erase(params.negotiated_codecs->begin()); + params.send_codec = params.negotiated_codecs->front(); + } else { + auto it = absl::c_find_if( + negotiated_codecs_, [&](const VideoCodecSettings& codec_setting) { + return format->IsSameCodec( + {codec_setting.codec.name, codec_setting.codec.params}); + }); + if (it == negotiated_codecs_.end()) { + RTC_LOG(LS_WARNING) << "Failed to switch encoder to: " + << format->ToString() + << ". Is default fallback allowed: " + << allow_default_fallback; + + if (allow_default_fallback) { + ApplyEncoderSwitch(std::nullopt, true); } + return; + } - if (send_codec() == new_codec_setting) { - // Already using this codec, no switch required. - return; - } + VideoCodecSettings new_codec_setting = *it; + for (const auto& kv : format->parameters) { + new_codec_setting.codec.params[kv.first] = kv.second; + } - ChangedSenderParameters params; - params.send_codec = new_codec_setting; - ApplyChangedParams(params); + if (send_codec() == new_codec_setting) { return; } - } - RTC_LOG(LS_WARNING) << "Failed to switch encoder to: " << format.ToString() - << ". Is default fallback allowed: " - << allow_default_fallback; + params.send_codec = new_codec_setting; + } - if (allow_default_fallback) { - RequestEncoderFallback(); + if (ApplyChangedParams(params) && parameters_changed_callback_) { + parameters_changed_callback_(); } } @@ -1380,11 +1377,6 @@ bool WebRtcVideoSendChannel::ApplyChangedParams( for (auto& kv : send_streams_) { kv.second->SetSenderParameters(changed_params); } - if (changed_params.send_codec || changed_params.rtcp_mode) { - if (send_codec_changed_callback_) { - send_codec_changed_callback_(); - } - } return true; } @@ -1416,6 +1408,16 @@ RtpParameters WebRtcVideoSendChannel::GetRtpSendParameters( return rtp_params; } +absl::AnyInvocable +WebRtcVideoSendChannel::GetRtpSendParametersCallback() const { + return [this, safety = task_safety_.flag()]( + uint32_t ssrc) mutable -> RtpParameters { + if (!safety->alive()) + return RtpParameters(); + return GetRtpSendParameters(ssrc); + }; +} + bool WebRtcVideoSendChannel::SetOptions(const VideoOptions& options) { RTC_DCHECK_RUN_ON(&thread_checker_); default_send_options_ = options; @@ -1511,22 +1513,9 @@ RTCError WebRtcVideoSendChannel::SetRtpSendParameters( SetPreferredDscp(new_dscp); } - bool changed = (parameters != current_parameters); - RTCError error = - it->second->SetRtpParameters(parameters, std::move(callback)); - if (changed && error.ok() && on_rtp_send_parameters_changed_callback_) { - on_rtp_send_parameters_changed_callback_(ssrc, parameters); - } - return error; + return it->second->SetRtpParameters(parameters, std::move(callback)); } -void WebRtcVideoSendChannel::SetOnRtpSendParametersChanged( - absl::AnyInvocable, const RtpParameters&)> - callback) { - RTC_DCHECK_RUN_ON(&thread_checker_); - RTC_DCHECK(!on_rtp_send_parameters_changed_callback_); - on_rtp_send_parameters_changed_callback_ = std::move(callback); -} std::optional WebRtcVideoSendChannel::GetSendCodec() const { RTC_DCHECK_RUN_ON(&thread_checker_); if (!send_codec()) { @@ -1611,7 +1600,6 @@ bool WebRtcVideoSendChannel::AddSendStream(const StreamParams& sp) { config.encoder_settings.encoder_factory = encoder_factory_; config.encoder_settings.bitrate_allocator_factory = bitrate_allocator_factory_; - config.encoder_settings.encoder_switch_request_callback = this; config.crypto_options = crypto_options_; config.rtp.extmap_allow_mixed = ExtmapAllowMixed(); @@ -1620,7 +1608,7 @@ bool WebRtcVideoSendChannel::AddSendStream(const StreamParams& sp) { video_config_.enable_send_packet_batching; WebRtcVideoSendStream* stream = new WebRtcVideoSendStream( - env_, call_, sp, std::move(config), default_send_options_, + this, env_, call_, sp, std::move(config), default_send_options_, video_config_.enable_cpu_adaptation, bitrate_config_.max_bitrate_bps, send_codec(), send_codecs_, send_rtp_extensions_, send_params_); @@ -1737,17 +1725,32 @@ void WebRtcVideoSendChannel::FillSendCodecStats( // we can omit the codec information for those here and only insert the // primary codec that is being used to send here. video_media_info->send_codecs.insert(std::make_pair( - send_codec()->codec.id, send_codec()->codec.ToCodecParameters())); + send_codec()->codec.id.value(), send_codec()->codec.ToCodecParameters())); for (const auto& it : send_codecs_) { auto codec_param_it = video_media_info->send_codecs.find(it.codec.id); if (codec_param_it == video_media_info->send_codecs.end()) { video_media_info->send_codecs.insert( - std::make_pair(it.codec.id, it.codec.ToCodecParameters())); + std::make_pair(it.codec.id.value(), it.codec.ToCodecParameters())); } } } +absl::AnyInvocable()> +WebRtcVideoSendChannel::GetStatsCallback() { + return [this, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VideoMediaSendInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; +} + void WebRtcVideoSendChannel::OnPacketSent(const SentPacketInfo& sent_packet) { RTC_DCHECK_RUN_ON(&network_thread_checker_); // TODO(tommi): We shouldn't need to go through call_ to deliver this @@ -1844,6 +1847,7 @@ WebRtcVideoSendChannel::WebRtcVideoSendStream::VideoSendStreamParameters:: codec_settings_list(codec_settings_list) {} WebRtcVideoSendChannel::WebRtcVideoSendStream::WebRtcVideoSendStream( + WebRtcVideoSendChannel* send_channel, const Environment& env, Call* call, const StreamParams& sp, @@ -1857,7 +1861,8 @@ WebRtcVideoSendChannel::WebRtcVideoSendStream::WebRtcVideoSendStream( // TODO(deadbeef): Don't duplicate information between send_params, // rtp_extensions, options, etc. const VideoSenderParameters& send_params) - : env_(env), + : send_channel_(send_channel), + env_(env), worker_thread_(call->worker_thread()), ssrcs_(sp.ssrcs), ssrc_groups_(sp.ssrc_groups), @@ -2492,7 +2497,8 @@ WebRtcVideoSendChannel::WebRtcVideoSendStream::GetPerLayerVideoSenderInfos( VideoSenderInfo common_info; if (parameters_.codec_settings) { common_info.codec_name = parameters_.codec_settings->codec.name; - common_info.codec_payload_type = parameters_.codec_settings->codec.id; + common_info.codec_payload_type = + parameters_.codec_settings->codec.id.value(); } // If SVC is used, one stream is configured but multiple encodings exist. This // is not spec-compliant, but it is how we've implemented SVC so this affects @@ -2732,6 +2738,13 @@ void WebRtcVideoSendChannel::WebRtcVideoSendStream::RecreateWebRtcStream() { ConfigureVideoEncoderSettings(parameters_.codec_settings->codec); VideoSendStream::Config config = parameters_.config.Copy(); + + auto encoder_switch_request_callback = + [send_channel = send_channel_](std::optional format, + bool allow_default_fallback) { + send_channel->RequestEncoderSwitch(std::move(format), + allow_default_fallback); + }; if (!config.rtp.rtx.ssrcs.empty() && config.rtp.rtx.payload_type == -1) { RTC_LOG(LS_WARNING) << "RTX SSRCs configured but there's no configured RTX " "payload type the set codec. Ignoring RTX."; @@ -2758,12 +2771,23 @@ void WebRtcVideoSendChannel::WebRtcVideoSendStream::RecreateWebRtcStream() { // GetStats and DestroyVideoSendStream. VideoSendStream::Stats stats = stream_->GetStats(); call_->DestroyVideoSendStream(stream_); - stream_ = call_->CreateVideoSendStream(std::move(config), - parameters_.encoder_config.Copy()); + stream_ = call_->CreateVideoSendStream( + std::move(config), parameters_.encoder_config.Copy(), + std::move(encoder_switch_request_callback)); + + // A new stream is created without any scaling or limitations, so these + // flags don't apply until the new stream experiences an adaptation event. + stats.bw_limited_resolution = false; + stats.bw_limited_framerate = false; + stats.cpu_limited_resolution = false; + stats.cpu_limited_framerate = false; + stats.quality_limitation_reason = QualityLimitationReason::kNone; + stream_->SetStats(stats); } else { - stream_ = call_->CreateVideoSendStream(std::move(config), - parameters_.encoder_config.Copy()); + stream_ = call_->CreateVideoSendStream( + std::move(config), parameters_.encoder_config.Copy(), + std::move(encoder_switch_request_callback)); } if (!rtp_parameters_.encodings.empty() && rtp_parameters_.encodings[0].csrcs.has_value()) { @@ -2830,19 +2854,20 @@ WebRtcVideoReceiveChannel::WebRtcVideoReceiveChannel( : MediaChannelUtil(call->network_thread(), config.enable_dscp), env_(env), worker_thread_(call->worker_thread()), + network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( + /*alive=*/true, + call->network_thread())), receiving_(false), call_(call), default_sink_(nullptr), video_config_(config.video), decoder_factory_(decoder_factory), - default_send_options_(options), last_receive_stats_log_ms_(-1), discard_unknown_ssrc_packets_(env_.field_trials().IsEnabled( "WebRTC-Video-DiscardPacketsWithUnknownSsrc")), crypto_options_(crypto_options), receive_buffer_size_(ParseReceiveBufferSize(env_.field_trials())) { RTC_DCHECK_RUN_ON(&thread_checker_); - rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc; // Crash if MapCodecs fails. recv_codecs_ = MapCodecs(GetPayloadTypesAndDefaultCodecs( decoder_factory_, /*is_decoder_factory=*/true, @@ -2870,13 +2895,16 @@ RtpParameters WebRtcVideoReceiveChannel::GetRtpReceiverParameters( return RtpParameters(); } rtp_params = it->second->GetRtpParameters(); - rtp_params.header_extensions = recv_rtp_extensions_; // Add codecs, which any stream is prepared to receive. for (const Codec& codec : recv_params_.codecs) { rtp_params.codecs.push_back(codec.ToCodecParameters()); } + rtp_params.header_extensions = FilterRtpExtensions( + recv_params_.extensions, RtpExtension::IsSupportedForVideo, false, + env_.field_trials()); + return rtp_params; } @@ -2903,8 +2931,7 @@ RtpParameters WebRtcVideoReceiveChannel::GetDefaultRtpReceiveParameters() bool WebRtcVideoReceiveChannel::GetChangedReceiverParameters( const VideoReceiverParameters& params, ChangedReceiverParameters* changed_params) const { - if (!ValidateCodecFormats(params.codecs) || - !ValidateRtpExtensions(params.extensions, recv_rtp_extensions_)) { + if (!ValidateCodecFormats(params.codecs)) { return false; } @@ -2940,15 +2967,6 @@ bool WebRtcVideoReceiveChannel::GetChangedReceiverParameters( std::optional>(mapped_codecs); } - // Handle RTP header extensions. - std::vector filtered_extensions = - FilterRtpExtensions(params.extensions, RtpExtension::IsSupportedForVideo, - false, env_.field_trials()); - if (filtered_extensions != recv_rtp_extensions_) { - changed_params->rtp_header_extensions = - std::optional>(filtered_extensions); - } - int flexfec_payload_type = mapped_codecs.front().flexfec_payload_type; if (flexfec_payload_type != recv_flexfec_payload_type_) { changed_params->flexfec_payload_type = flexfec_payload_type; @@ -2977,10 +2995,6 @@ bool WebRtcVideoReceiveChannel::SetReceiverParameters( << *changed_params.flexfec_payload_type; recv_flexfec_payload_type_ = *changed_params.flexfec_payload_type; } - if (changed_params.rtp_header_extensions) { - recv_rtp_extensions_ = *changed_params.rtp_header_extensions; - recv_rtp_extension_map_ = RtpHeaderExtensionMap(recv_rtp_extensions_); - } if (changed_params.codec_settings) { RTC_DLOG(LS_INFO) << "Changing recv codecs from " << CodecSettingsVectorToString(recv_codecs_) << " to " @@ -2996,32 +3010,6 @@ bool WebRtcVideoReceiveChannel::SetReceiverParameters( return true; } -void WebRtcVideoReceiveChannel::SetReceiverReportSsrc(uint32_t ssrc) { - RTC_DCHECK_RUN_ON(&thread_checker_); - if (ssrc == rtcp_receiver_report_ssrc_) - return; - - rtcp_receiver_report_ssrc_ = ssrc; - for (auto& [unused, receive_stream] : receive_streams_) - receive_stream->SetLocalSsrc(ssrc); -} - -void WebRtcVideoReceiveChannel::ChooseReceiverReportSsrc( - const std::set& choices) { - RTC_DCHECK_RUN_ON(&thread_checker_); - // If we can continue using the current receiver report, do so. - if (choices.find(rtcp_receiver_report_ssrc_) != choices.end()) { - return; - } - // Go back to the default if list has been emptied. - if (choices.empty()) { - SetReceiverReportSsrc(kDefaultRtcpReceiverReportSsrc); - return; - } - // Any number is as good as any other. - SetReceiverReportSsrc(*choices.begin()); -} - void WebRtcVideoReceiveChannel::SetReceive(bool receive) { RTC_DCHECK_RUN_ON(&thread_checker_); TRACE_EVENT0("webrtc", "WebRtcVideoReceiveChannel::SetReceive"); @@ -3127,32 +3115,14 @@ void WebRtcVideoReceiveChannel::ConfigureReceiverRtp( uint32_t ssrc = sp.first_ssrc(); config->rtp.remote_ssrc = ssrc; - config->rtp.local_ssrc = rtcp_receiver_report_ssrc_; - - // TODO(pbos): This protection is against setting the same local ssrc as - // remote which is not permitted by the lower-level API. RTCP requires a - // corresponding sender SSRC. Figure out what to do when we don't have - // (receive-only) or know a good local SSRC. - if (config->rtp.remote_ssrc == config->rtp.local_ssrc) { - if (config->rtp.local_ssrc != kDefaultRtcpReceiverReportSsrc) { - config->rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc; - } else { - config->rtp.local_ssrc = kDefaultRtcpReceiverReportSsrc + 1; - } - } - - // The mode and rtx time is determined by a call to the configuration - // function. - config->rtp.rtcp_mode = rtp_config_.rtcp_mode; sp.GetFidSsrc(ssrc, &config->rtp.rtx_ssrc); // TODO(brandtr): Generalize when we add support for multistream protection. flexfec_config->payload_type = recv_flexfec_payload_type_; if (!env_.field_trials().IsDisabled("WebRTC-FlexFEC-03-Advertised") && - sp.GetFecFrSsrc(ssrc, &flexfec_config->rtp.remote_ssrc)) { + sp.GetFecFrSsrc(ssrc, &flexfec_config->remote_ssrc)) { flexfec_config->protected_media_ssrcs = {ssrc}; - flexfec_config->rtp.local_ssrc = config->rtp.local_ssrc; flexfec_config->rtcp_mode = config->rtp.rtcp_mode; } } @@ -3173,6 +3143,11 @@ bool WebRtcVideoReceiveChannel::RemoveRecvStream(uint32_t ssrc) { } void WebRtcVideoReceiveChannel::ResetUnsignaledRecvStream() { + if (!worker_thread_->IsCurrent()) { + worker_thread_->PostTask(SafeTask( + task_safety_.flag(), [this]() { ResetUnsignaledRecvStream(); })); + return; + } RTC_DCHECK_RUN_ON(&thread_checker_); RTC_LOG(LS_INFO) << "ResetUnsignaledRecvStream."; unsignaled_stream_params_ = StreamParams(); @@ -3279,31 +3254,42 @@ void WebRtcVideoReceiveChannel::FillReceiveCodecStats( *receiver.codec_payload_type == c.id; }); if (codec != recv_params_.codecs.end()) { - video_media_info->receive_codecs.insert( + video_media_info->receive_codecs.emplace( std::make_pair(codec->id, codec->ToCodecParameters())); } } } -void WebRtcVideoReceiveChannel::OnPacketReceived( - const RtpPacketReceived& packet) { +absl::AnyInvocable()> +WebRtcVideoReceiveChannel::GetStatsCallback() { + return [this, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VideoMediaReceiveInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; +} + +void WebRtcVideoReceiveChannel::OnPacketReceived(RtpPacketReceived packet) { // Note: the network_thread_checker may refer to the worker thread if the two // threads are combined, but this is either always true or always false // depending on configuration set at object initialization. RTC_DCHECK_RUN_ON(&network_thread_checker_); - // TODO(crbug.com/1373439): Stop posting to the worker thread when the - // combined network/worker project launches. - if (TaskQueueBase::Current() != worker_thread_) { - worker_thread_->PostTask( - SafeTask(task_safety_.flag(), [this, packet = packet]() mutable { - RTC_DCHECK_RUN_ON(&thread_checker_); - ProcessReceivedPacket(std::move(packet)); - })); - } else { - RTC_DCHECK_RUN_ON(&thread_checker_); - ProcessReceivedPacket(packet); + packet.set_payload_type_frequency(kVideoPayloadTypeFrequency); + if (!packet.arrival_time().IsFinite()) { + packet.set_arrival_time(env_.clock().CurrentTime()); } + + call_->Receiver()->DeliverRtpPacket( + MediaType::VIDEO, std::move(packet), + absl::bind_front( + &WebRtcVideoReceiveChannel::MaybeCreateDefaultReceiveStream, this)); } bool WebRtcVideoReceiveChannel::MaybeCreateDefaultReceiveStream( @@ -3413,6 +3399,8 @@ void WebRtcVideoReceiveChannel::ReCreateDefaultReceiveStream( void WebRtcVideoReceiveChannel::SetInterface( MediaChannelNetworkInterface* iface) { RTC_DCHECK_RUN_ON(&network_thread_checker_); + iface ? network_thread_safety_->SetAlive() + : network_thread_safety_->SetNotAlive(); MediaChannelUtil::SetInterface(iface); // Set the RTP recv/send buffer to a bigger size. MediaChannelUtil::SetOption(MediaChannelNetworkInterface::ST_RTP, @@ -3965,7 +3953,7 @@ WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo( const ReceiveStatistics* fec_stats = flexfec_stream_->GetStats(); if (fec_stats) { const StreamStatistician* statistican = - fec_stats->GetStatistician(flexfec_config_.rtp.remote_ssrc); + fec_stats->GetStatistician(flexfec_config_.remote_ssrc); if (statistican) { const RtpReceiveStats fec_rtp_stats = statistican->GetStats(); info.fec_packets_received = fec_rtp_stats.packet_counter.packets; @@ -4048,14 +4036,6 @@ void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream:: stream_->SetDepacketizerToDecoderFrameTransformer(frame_transformer); } -void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::SetLocalSsrc( - uint32_t ssrc) { - config_.rtp.local_ssrc = ssrc; - call_->OnLocalSsrcUpdated(stream(), ssrc); - if (flexfec_stream_) - call_->OnLocalSsrcUpdated(*flexfec_stream_, ssrc); -} - void WebRtcVideoReceiveChannel::WebRtcVideoReceiveStream::UpdateRtxSsrc( uint32_t ssrc) { stream_->UpdateRtxSsrc(ssrc); @@ -4076,32 +4056,6 @@ WebRtcVideoReceiveChannel::FindReceiveStream(uint32_t ssrc) { return nullptr; } -// RTC_RUN_ON(worker_thread_) -void WebRtcVideoReceiveChannel::ProcessReceivedPacket( - RtpPacketReceived packet) { - // TODO(bugs.webrtc.org/11993): This code is very similar to what - // WebRtcVoiceMediaChannel::OnPacketReceived does. For maintainability and - // consistency it would be good to move the interaction with call_->Receiver() - // to a common implementation and provide a callback on the worker thread - // for the exception case (DELIVERY_UNKNOWN_SSRC) and how retry is attempted. - // TODO(bugs.webrtc.org/7135): extensions in `packet` is currently set - // in RtpTransport and does not neccessarily include extensions specific - // to this channel/MID. Also see comment in - // BaseChannel::MaybeUpdateDemuxerAndRtpExtensions_w. - // It would likely be good if extensions where merged per BUNDLE and - // applied directly in RtpTransport::DemuxPacket; - packet.IdentifyExtensions(recv_rtp_extension_map_); - packet.set_payload_type_frequency(kVideoPayloadTypeFrequency); - if (!packet.arrival_time().IsFinite()) { - packet.set_arrival_time(env_.clock().CurrentTime()); - } - - call_->Receiver()->DeliverRtpPacket( - MediaType::VIDEO, std::move(packet), - absl::bind_front( - &WebRtcVideoReceiveChannel::MaybeCreateDefaultReceiveStream, this)); -} - void WebRtcVideoReceiveChannel::SetRecordableEncodedFrameCallback( uint32_t ssrc, std::function callback) { diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h index aa3ed53e41a..41a3500a800 100644 --- a/media/engine/webrtc_video_engine.h +++ b/media/engine/webrtc_video_engine.h @@ -47,7 +47,6 @@ #include "api/video/video_frame.h" #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" -#include "api/video/video_stream_encoder_settings.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_encoder_factory.h" #include "call/call.h" @@ -61,7 +60,6 @@ #include "media/base/media_config.h" #include "media/base/media_engine.h" #include "media/base/stream_params.h" -#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/checks.h" #include "rtc_base/network/sent_packet.h" @@ -108,7 +106,9 @@ class WebRtcVideoEngine : public VideoEngineInterface { const MediaConfig& config, const VideoOptions& options, const CryptoOptions& crypto_options, - VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) override; + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + video_encoder_switch_request_callback = nullptr) override; std::unique_ptr CreateReceiveChannel( const Environment& env, Call* call, @@ -129,7 +129,7 @@ class WebRtcVideoEngine : public VideoEngineInterface { std::vector GetRtpHeaderExtensions( /* optional field trials from PeerConnection that override those from PeerConnectionFactory */ - const webrtc::FieldTrialsView* field_trials) const override; + const FieldTrialsView* field_trials) const override; private: const std::unique_ptr decoder_factory_; @@ -160,8 +160,7 @@ struct VideoCodecSettings { }; class WebRtcVideoSendChannel : public MediaChannelUtil, - public VideoMediaSendChannelInterface, - public EncoderSwitchRequestCallback { + public VideoMediaSendChannelInterface { public: WebRtcVideoSendChannel( const Environment& env, @@ -170,7 +169,9 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, const VideoOptions& options, const CryptoOptions& crypto_options, VideoEncoderFactory* encoder_factory, - VideoBitrateAllocatorFactory* bitrate_allocator_factory); + VideoBitrateAllocatorFactory* bitrate_allocator_factory, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + video_encoder_switch_request_callback); ~WebRtcVideoSendChannel() override; MediaType media_type() const override { return MediaType::VIDEO; } @@ -198,10 +199,9 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, RTCError SetRtpSendParameters(uint32_t ssrc, const RtpParameters& parameters, SetParametersCallback callback) override; - void SetOnRtpSendParametersChanged( - absl::AnyInvocable, const RtpParameters&)> - callback) override; RtpParameters GetRtpSendParameters(uint32_t ssrc) const override; + absl::AnyInvocable GetRtpSendParametersCallback() + const override; std::optional GetSendCodec() const override; bool SetSend(bool send) override; bool SetVideoSend(uint32_t ssrc, @@ -211,6 +211,8 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, bool RemoveSendStream(uint32_t ssrc) override; void FillBitrateInfo(BandwidthEstimationInfo* bwe_info) override; bool GetStats(VideoMediaSendInfo* info) override; + absl::AnyInvocable()> GetStatsCallback() + override; void OnPacketSent(const SentPacketInfo& sent_packet) override; void OnReadyToSend(bool ready) override; @@ -236,6 +238,12 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, ssrc_list_changed_callback_ = std::move(callback); } + void SetParametersChangedCallback( + absl::AnyInvocable callback) override { + RTC_DCHECK_RUN_ON(&thread_checker_); + parameters_changed_callback_ = std::move(callback); + } + // Implemented for VideoMediaChannelTest. bool sending() const { RTC_DCHECK_RUN_ON(&thread_checker_); @@ -251,10 +259,10 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, ADAPTREASON_BANDWIDTH = 2, }; - // Implements EncoderSwitchRequestCallback. - void RequestEncoderFallback() override; - void RequestEncoderSwitch(const SdpVideoFormat& format, - bool allow_default_fallback) override; + // Called to request an encoder switch or fallback. + // See also EncoderSwitchRequestCallback. + void RequestEncoderSwitch(std::optional format, + bool allow_default_fallback); void GenerateSendKeyFrame(uint32_t ssrc, const std::vector& rids) override; @@ -263,19 +271,6 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, uint32_t ssrc, scoped_refptr frame_transformer) override; // Information queries to support SetReceiverFeedbackParameters - RtcpMode SendCodecRtcpMode() const override { - RTC_DCHECK_RUN_ON(&thread_checker_); - return send_params_.rtcp.reduced_size ? RtcpMode::kReducedSize - : RtcpMode::kCompound; - } - - bool SendCodecHasLntf() const override { - RTC_DCHECK_RUN_ON(&thread_checker_); - if (!send_codec()) { - return false; - } - return HasLntf(send_codec()->codec); - } bool SendCodecHasNack() const override { RTC_DCHECK_RUN_ON(&thread_checker_); if (!send_codec()) { @@ -283,13 +278,6 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, } return HasNack(send_codec()->codec); } - std::optional SendCodecRtxTime() const override { - RTC_DCHECK_RUN_ON(&thread_checker_); - if (!send_codec()) { - return std::nullopt; - } - return send_codec()->rtx_time; - } private: struct ChangedSenderParameters { @@ -311,11 +299,16 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, bool ApplyChangedParams(const ChangedSenderParameters& changed_params); bool ValidateSendSsrcAvailability(const StreamParams& sp) const RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_); + // Executes the actual encoder switch operation. + void ApplyEncoderSwitch(std::optional format, + bool allow_default_fallback) + RTC_RUN_ON(thread_checker_); // Wrapper for the sender part. class WebRtcVideoSendStream { public: WebRtcVideoSendStream( + WebRtcVideoSendChannel* send_channel, const Environment& env, Call* call, const StreamParams& sp, @@ -400,6 +393,7 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, DegradationPreference GetDegradationPreference() const RTC_EXCLUSIVE_LOCKS_REQUIRED(&thread_checker_); + WebRtcVideoSendChannel* const send_channel_; const Environment env_; RTC_NO_UNIQUE_ADDRESS SequenceChecker thread_checker_; TaskQueueBase* const worker_thread_; @@ -430,8 +424,6 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, const bool disable_automatic_resize_; }; - void Construct(Call* call, WebRtcVideoEngine* engine); - // Get all codecs that are compatible with the receiver. std::vector SelectSendVideoCodecs( const std::vector& remote_mapped_codecs) const @@ -439,9 +431,6 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, void FillSenderStats(VideoMediaSendInfo* info, bool log_stats) RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_); - void FillBandwidthEstimationStats(const Call::Stats& stats, - VideoMediaInfo* info) - RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_); void FillSendCodecStats(VideoMediaSendInfo* video_media_info) RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_); @@ -461,45 +450,15 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, SequenceChecker::kDetached}; RTC_NO_UNIQUE_ADDRESS SequenceChecker thread_checker_; - uint32_t rtcp_receiver_report_ssrc_ RTC_GUARDED_BY(thread_checker_); bool sending_ RTC_GUARDED_BY(thread_checker_); - bool receiving_ RTC_GUARDED_BY(&thread_checker_); Call* const call_; - VideoSinkInterface* default_sink_ RTC_GUARDED_BY(thread_checker_); - - // Delay for unsignaled streams, which may be set before the stream exists. - int default_recv_base_minimum_delay_ms_ RTC_GUARDED_BY(thread_checker_) = 0; - const MediaConfig::Video video_config_ RTC_GUARDED_BY(thread_checker_); - mutable absl::AnyInvocable, - const RtpParameters&)> - on_rtp_send_parameters_changed_callback_ RTC_GUARDED_BY(thread_checker_); - // Using primary-ssrc (first ssrc) as key. std::map send_streams_ RTC_GUARDED_BY(thread_checker_); - // When the channel and demuxer get reconfigured, there is a window of time - // where we have to be prepared for packets arriving based on the old demuxer - // criteria because the streams live on the worker thread and the demuxer - // lives on the network thread. Because packets are posted from the network - // thread to the worker thread, they can still be in-flight when streams are - // reconfgured. This can happen when `demuxer_criteria_id_` and - // `demuxer_criteria_completed_id_` don't match. During this time, we do not - // want to create unsignalled receive streams and should instead drop the - // packets. E.g: - // * If RemoveRecvStream(old_ssrc) was recently called, there may be packets - // in-flight for that ssrc. This happens when a receiver becomes inactive. - // * If we go from one to many m= sections, the demuxer may change from - // forwarding all packets to only forwarding the configured ssrcs, so there - // is a risk of receiving ssrcs for other, recently added m= sections. - uint32_t demuxer_criteria_id_ RTC_GUARDED_BY(thread_checker_) = 0; - uint32_t demuxer_criteria_completed_id_ RTC_GUARDED_BY(thread_checker_) = 0; - std::optional last_unsignalled_ssrc_creation_time_ms_ - RTC_GUARDED_BY(thread_checker_); std::set send_ssrcs_ RTC_GUARDED_BY(thread_checker_); - std::set receive_ssrcs_ RTC_GUARDED_BY(thread_checker_); std::optional send_codec_ RTC_GUARDED_BY(thread_checker_); std::vector negotiated_codecs_ @@ -514,40 +473,25 @@ class WebRtcVideoSendChannel : public MediaChannelUtil, RTC_GUARDED_BY(thread_checker_); // See reason for keeping track of the FlexFEC payload type separately in // comment in WebRtcVideoChannel::ChangedReceiverParameters. - int recv_flexfec_payload_type_ RTC_GUARDED_BY(thread_checker_); BitrateConstraints bitrate_config_ RTC_GUARDED_BY(thread_checker_); - // TODO(deadbeef): Don't duplicate information between - // send_params/recv_params, rtp_extensions, options, etc. VideoSenderParameters send_params_ RTC_GUARDED_BY(thread_checker_); VideoOptions default_send_options_ RTC_GUARDED_BY(thread_checker_); - VideoReceiverParameters recv_params_ RTC_GUARDED_BY(thread_checker_); int64_t last_send_stats_log_ms_ RTC_GUARDED_BY(thread_checker_); - int64_t last_receive_stats_log_ms_ RTC_GUARDED_BY(thread_checker_); - const bool discard_unknown_ssrc_packets_ RTC_GUARDED_BY(thread_checker_); - // This is a stream param that comes from the remote description, but wasn't - // signaled with any a=ssrc lines. It holds information that was signaled - // before the unsignaled receive stream is created when the first packet is - // received. - StreamParams unsignaled_stream_params_ RTC_GUARDED_BY(thread_checker_); // Per peer connection crypto options that last for the lifetime of the peer // connection. const CryptoOptions crypto_options_ RTC_GUARDED_BY(thread_checker_); - // Optional frame transformer set on unsignaled streams. - scoped_refptr unsignaled_frame_transformer_ - RTC_GUARDED_BY(thread_checker_); - - // RTP parameters that need to be set when creating a video receive stream. - // Only used in Receiver mode - in Both mode, it reads those things from the - // codec. - VideoReceiveStreamInterface::Config::Rtp rtp_config_; - - // Callback invoked whenever the send codec changes. - // TODO(bugs.webrtc.org/13931): Remove again when coupling isn't needed. - absl::AnyInvocable send_codec_changed_callback_; // Callback invoked whenever the list of SSRCs changes. absl::AnyInvocable&)> ssrc_list_changed_callback_; + + // Callback invoked whenever the sender parameters change. + absl::AnyInvocable parameters_changed_callback_ + RTC_GUARDED_BY(&thread_checker_); + + // Callback to intercept the encoder switch request. + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + encoder_switch_request_callback_; }; class WebRtcVideoReceiveChannel : public MediaChannelUtil, @@ -591,15 +535,13 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil, bool SetSink(uint32_t ssrc, VideoSinkInterface* sink) override; void SetDefaultSink(VideoSinkInterface* sink) override; bool GetStats(VideoMediaReceiveInfo* info) override; - void OnPacketReceived(const RtpPacketReceived& packet) override; + absl::AnyInvocable()> GetStatsCallback() + override; + void OnPacketReceived(RtpPacketReceived packet) override; bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override; std::optional GetBaseMinimumPlayoutDelayMs(uint32_t ssrc) const override; - // Choose one of the available SSRCs (or default if none) as the current - // receiver report SSRC. - void ChooseReceiverReportSsrc(const std::set& choices) override; - // E2E Encrypted Video Frame API // Set a frame decryptor to a particular ssrc that will intercept all // incoming video frames and attempt to decrypt them before forwarding the @@ -637,8 +579,6 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil, WebRtcVideoReceiveStream* FindReceiveStream(uint32_t ssrc) RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_); - void ProcessReceivedPacket(RtpPacketReceived packet) - RTC_RUN_ON(thread_checker_); // Expected to be invoked once per packet that belongs to this channel that // can not be demuxed. @@ -659,10 +599,6 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil, void DeleteReceiveStream(WebRtcVideoReceiveStream* stream) RTC_EXCLUSIVE_LOCKS_REQUIRED(thread_checker_); - // Called when the local ssrc changes. Sets `rtcp_receiver_report_ssrc_` and - // updates the receive streams. - void SetReceiverReportSsrc(uint32_t ssrc) RTC_RUN_ON(&thread_checker_); - // Wrapper for the receiver part, contains configs etc. that are needed to // reconstruct the underlying VideoReceiveStreamInterface. class WebRtcVideoReceiveStream : public VideoSinkInterface { @@ -718,7 +654,6 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil, void SetDepacketizerToDecoderFrameTransformer( scoped_refptr frame_transformer); - void SetLocalSsrc(uint32_t local_ssrc); void UpdateRtxSsrc(uint32_t ssrc); void StartReceiveStream(); void StopReceiveStream(); @@ -779,11 +714,11 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil, const Environment env_; TaskQueueBase* const worker_thread_; ScopedTaskSafety task_safety_; + scoped_refptr network_thread_safety_; RTC_NO_UNIQUE_ADDRESS SequenceChecker network_thread_checker_{ SequenceChecker::kDetached}; RTC_NO_UNIQUE_ADDRESS SequenceChecker thread_checker_; - uint32_t rtcp_receiver_report_ssrc_ RTC_GUARDED_BY(thread_checker_); bool receiving_ RTC_GUARDED_BY(&thread_checker_); Call* const call_; @@ -812,29 +747,15 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil, uint32_t demuxer_criteria_completed_id_ RTC_GUARDED_BY(thread_checker_) = 0; std::optional last_unsignalled_ssrc_creation_time_ms_ RTC_GUARDED_BY(thread_checker_); - std::set send_ssrcs_ RTC_GUARDED_BY(thread_checker_); std::set receive_ssrcs_ RTC_GUARDED_BY(thread_checker_); - std::optional send_codec_ RTC_GUARDED_BY(thread_checker_); - std::vector negotiated_codecs_ - RTC_GUARDED_BY(thread_checker_); - - std::vector send_rtp_extensions_ - RTC_GUARDED_BY(thread_checker_); - VideoDecoderFactory* const decoder_factory_ RTC_GUARDED_BY(thread_checker_); std::vector recv_codecs_ RTC_GUARDED_BY(thread_checker_); - RtpHeaderExtensionMap recv_rtp_extension_map_ RTC_GUARDED_BY(thread_checker_); - std::vector recv_rtp_extensions_ - RTC_GUARDED_BY(thread_checker_); // See reason for keeping track of the FlexFEC payload type separately in // comment in WebRtcVideoChannel::ChangedReceiverParameters. int recv_flexfec_payload_type_ RTC_GUARDED_BY(thread_checker_); - BitrateConstraints bitrate_config_ RTC_GUARDED_BY(thread_checker_); // TODO(deadbeef): Don't duplicate information between // send_params/recv_params, rtp_extensions, options, etc. - VideoSenderParameters send_params_ RTC_GUARDED_BY(thread_checker_); - VideoOptions default_send_options_ RTC_GUARDED_BY(thread_checker_); VideoReceiverParameters recv_params_ RTC_GUARDED_BY(thread_checker_); int64_t last_receive_stats_log_ms_ RTC_GUARDED_BY(thread_checker_); const bool discard_unknown_ssrc_packets_ RTC_GUARDED_BY(thread_checker_); @@ -851,18 +772,6 @@ class WebRtcVideoReceiveChannel : public MediaChannelUtil, scoped_refptr unsignaled_frame_transformer_ RTC_GUARDED_BY(thread_checker_); - // RTP parameters that need to be set when creating a video receive stream. - // Only used in Receiver mode - in Both mode, it reads those things from the - // codec. - VideoReceiveStreamInterface::Config::Rtp rtp_config_; - - // Callback invoked whenever the send codec changes. - // TODO(bugs.webrtc.org/13931): Remove again when coupling isn't needed. - absl::AnyInvocable send_codec_changed_callback_; - // Callback invoked whenever the list of SSRCs changes. - absl::AnyInvocable&)> - ssrc_list_changed_callback_; - const int receive_buffer_size_; }; diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index ccfd0c2abe9..c9de2e55a8c 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -36,6 +36,7 @@ #include "api/field_trials.h" #include "api/make_ref_counted.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/priority.h" #include "api/rtc_error.h" #include "api/rtp_headers.h" @@ -115,6 +116,7 @@ #include "rtc_base/experiments/min_video_bitrate_experiment.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/socket.h" +#include "rtc_base/thread.h" #include "test/create_test_environment.h" #include "test/create_test_field_trials.h" #include "test/fake_decoder.h" @@ -122,6 +124,7 @@ #include "test/gmock.h" #include "test/gtest.h" #include "test/rtcp_packet_parser.h" +#include "test/run_loop.h" #include "test/time_controller/simulated_time_controller.h" #include "video/config/encoder_stream_factory.h" #include "video/config/simulcast.h" @@ -786,40 +789,6 @@ TEST_F(WebRtcVideoEngineTest, UseFactoryForVp8WhenSupported) { EXPECT_EQ(0u, encoder_factory_->encoders().size()); } -TEST_F(WebRtcVideoEngineTest, OnRtpSendParametersChangedCallback) { - AddSupportedVideoCodecType("VP8"); - auto send_channel = SetSendParamsWithAllSupportedCodecs(); - EXPECT_TRUE(send_channel->AddSendStream(StreamParams::CreateLegacy(kSsrc))); - - std::optional callback_ssrc; - RtpParameters callback_params; - int callback_count = 0; - send_channel->SetOnRtpSendParametersChanged( - [&](std::optional ssrc, const RtpParameters& params) { - callback_ssrc = ssrc; - callback_params = params; - ++callback_count; - }); - - RtpParameters parameters = send_channel->GetRtpSendParameters(kSsrc); - EXPECT_EQ(callback_count, 0); - - // Change parameters. - parameters.encodings[0].max_bitrate_bps = 500000; - EXPECT_TRUE(send_channel->SetRtpSendParameters(kSsrc, parameters).ok()); - - EXPECT_EQ(callback_count, 1); - EXPECT_EQ(callback_ssrc, kSsrc); - EXPECT_EQ(callback_params.encodings[0].max_bitrate_bps, 500000); - - // Now set the parameters again. This time we're setting them to the same - // value as they were already set to. That should not result in the - // callback being issued. - EXPECT_TRUE(send_channel->SetRtpSendParameters(kSsrc, parameters).ok()); - // No additional callback should have been issued. - EXPECT_EQ(callback_count, 1); -} - // Test that when an encoder factory supports H264, we add an RTX // codec for it. // TODO(deadbeef): This test should be updated if/when we start @@ -868,6 +837,47 @@ TEST_F(WebRtcVideoEngineTest, CanConstructDecoderForVp9EncoderFactory) { } #endif // defined(RTC_ENABLE_VP9) +TEST_F(WebRtcVideoEngineTest, EncoderSwitchRequestCallback) { + AddSupportedVideoCodecType("VP8"); + + bool callback_called = false; + auto callback = + [&callback_called, call = call_.get(), main_thread = Thread::Current()]( + VideoMediaSendChannelInterface::EncoderSwitchRequestAction action) { + call->worker_thread()->PostTask([action = std::move(action), + main_thread, + &callback_called]() mutable { + std::move(action)(); + main_thread->PostTask([&callback_called] { callback_called = true; }); + }); + }; + + std::unique_ptr send_channel = + engine_->CreateSendChannel( + env_, call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions(), + video_bitrate_allocator_factory_.get(), std::move(callback)); + + VideoSenderParameters parameters; + parameters.codecs.push_back(GetEngineCodec("VP8")); + EXPECT_TRUE(send_channel->SetSenderParameters(parameters)); + + send_channel->OnReadyToSend(true); + EXPECT_TRUE(send_channel->AddSendStream(StreamParams::CreateLegacy(kSsrc))); + EXPECT_TRUE(send_channel->SetSend(true)); + + auto* web_rtc_send_channel = + static_cast(send_channel.get()); + + std::unique_ptr bg_thread = Thread::Create(); + bg_thread->Start(); + bg_thread->BlockingCall( + [&] { web_rtc_send_channel->RequestEncoderSwitch(std::nullopt, true); }); + + EXPECT_TRUE(time_controller_.Wait([&] { return callback_called; })); + + EXPECT_TRUE(send_channel->RemoveSendStream(kSsrc)); +} + TEST_F(WebRtcVideoEngineTest, PropagatesInputFrameTimestamp) { AddSupportedVideoCodecType("VP8"); FakeCall* fake_call = new FakeCall(env_); @@ -2631,19 +2641,19 @@ TEST_F(WebRtcVideoChannelBaseTest, ASSERT_TRUE(codec); EXPECT_EQ("VP9", codec->name); - SendImpl()->RequestEncoderFallback(); + SendImpl()->RequestEncoderSwitch(std::nullopt, true); time_controller_.AdvanceTime(kFrameDuration); codec = send_channel_->GetSendCodec(); ASSERT_TRUE(codec); EXPECT_EQ("AV1", codec->name); - SendImpl()->RequestEncoderFallback(); + SendImpl()->RequestEncoderSwitch(std::nullopt, true); time_controller_.AdvanceTime(kFrameDuration); codec = send_channel_->GetSendCodec(); ASSERT_TRUE(codec); EXPECT_EQ("VP8", codec->name); - SendImpl()->RequestEncoderFallback(); + SendImpl()->RequestEncoderSwitch(std::nullopt, true); time_controller_.AdvanceTime(kFrameDuration); FrameForwarder frame_forwarder; @@ -2653,7 +2663,7 @@ TEST_F(WebRtcVideoChannelBaseTest, #if defined(RTC_ENABLE_VP9) -TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderFallback) { +TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderSwitchWithNullopt) { VideoSenderParameters parameters; parameters.codecs.push_back(GetEngineCodec("VP9")); parameters.codecs.push_back(GetEngineCodec("VP8")); @@ -2663,16 +2673,16 @@ TEST_F(WebRtcVideoChannelBaseTest, RequestEncoderFallback) { ASSERT_TRUE(codec); EXPECT_EQ("VP9", codec->name); - // RequestEncoderFallback will post a task to the worker thread (which is also + // RequestEncoderSwitch will post a task to the worker thread (which is also // the current thread), hence the ProcessMessages call. - SendImpl()->RequestEncoderFallback(); + SendImpl()->RequestEncoderSwitch(std::nullopt, true); time_controller_.AdvanceTime(kFrameDuration); codec = send_channel_->GetSendCodec(); ASSERT_TRUE(codec); EXPECT_EQ("VP8", codec->name); // No other codec to fall back to, keep using VP8. - SendImpl()->RequestEncoderFallback(); + SendImpl()->RequestEncoderSwitch(std::nullopt, true); time_controller_.AdvanceTime(kFrameDuration); codec = send_channel_->GetSendCodec(); ASSERT_TRUE(codec); @@ -2745,9 +2755,9 @@ TEST_F(WebRtcVideoChannelBaseTest, SendCodecIsMovedToFrontInRtpParameters) { ASSERT_EQ(send_codecs.size(), 2u); EXPECT_THAT("VP9", send_codecs[0].name); - // RequestEncoderFallback will post a task to the worker thread (which is also + // RequestEncoderSwitch will post a task to the worker thread (which is also // the current thread), hence the ProcessMessages call. - SendImpl()->RequestEncoderFallback(); + SendImpl()->RequestEncoderSwitch(std::nullopt, true); time_controller_.AdvanceTime(kFrameDuration); send_codecs = send_channel_->GetRtpSendParameters(kSsrc).codecs; @@ -2783,11 +2793,6 @@ class WebRtcVideoChannelTest : public WebRtcVideoEngineTest { receive_channel_ = engine_->CreateReceiveChannel(env_, fake_call_.get(), GetMediaConfig(), VideoOptions(), CryptoOptions()); - send_channel_->SetSsrcListChangedCallback( - [receive_channel = - receive_channel_.get()](const std::set& choices) { - receive_channel->ChooseReceiverReportSsrc(choices); - }); send_channel_->OnReadyToSend(true); receive_channel_->SetReceive(true); last_ssrc_ = 123; @@ -3392,69 +3397,22 @@ TEST_F(WebRtcVideoChannelTest, RtpExtension::kTimestampOffsetUri); } -TEST_F(WebRtcVideoChannelTest, SetSendRtpHeaderExtensionsRejectsIncorrectIds) { - const int kIncorrectIds[] = {-2, -1, 0, 15, 16}; - for (int incorrect_id : kIncorrectIds) { - send_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kTimestampOffsetUri, incorrect_id)); - EXPECT_FALSE(send_channel_->SetSenderParameters(send_parameters_)) - << "Bad extension id '" << incorrect_id << "' accepted."; - } -} - -TEST_F(WebRtcVideoChannelTest, SetRecvRtpHeaderExtensionsRejectsIncorrectIds) { - const int kIncorrectIds[] = {-2, -1, 0, 15, 16}; - for (int incorrect_id : kIncorrectIds) { - recv_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kTimestampOffsetUri, incorrect_id)); - EXPECT_FALSE(receive_channel_->SetReceiverParameters(recv_parameters_)) - << "Bad extension id '" << incorrect_id << "' accepted."; - } -} - -TEST_F(WebRtcVideoChannelTest, SetSendRtpHeaderExtensionsRejectsDuplicateIds) { - const int id = 1; - send_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kTimestampOffsetUri, id)); - send_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kAbsSendTimeUri, id)); - EXPECT_FALSE(send_channel_->SetSenderParameters(send_parameters_)); - - // Duplicate entries are also not supported. - send_parameters_.extensions.clear(); - send_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kTimestampOffsetUri, id)); - send_parameters_.extensions.push_back(send_parameters_.extensions.back()); - EXPECT_FALSE(send_channel_->SetSenderParameters(send_parameters_)); -} - -TEST_F(WebRtcVideoChannelTest, SetRecvRtpHeaderExtensionsRejectsDuplicateIds) { - const int id = 1; - recv_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kTimestampOffsetUri, id)); - recv_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kAbsSendTimeUri, id)); - EXPECT_FALSE(receive_channel_->SetReceiverParameters(recv_parameters_)); - // Duplicate entries are also not supported. - recv_parameters_.extensions.clear(); - recv_parameters_.extensions.push_back( - RtpExtension(RtpExtension::kTimestampOffsetUri, id)); - recv_parameters_.extensions.push_back(recv_parameters_.extensions.back()); - EXPECT_FALSE(receive_channel_->SetReceiverParameters(recv_parameters_)); -} TEST_F(WebRtcVideoChannelTest, OnPacketReceivedIdentifiesExtensions) { VideoReceiverParameters parameters = recv_parameters_; parameters.extensions.push_back( RtpExtension(RtpExtension::kVideoRotationUri, /*id=*/1)); ASSERT_TRUE(receive_channel_->SetReceiverParameters(parameters)); + time_controller_.AdvanceTime(TimeDelta::Zero()); RtpHeaderExtensionMap extension_map(parameters.extensions); RtpPacketReceived reference_packet(&extension_map); reference_packet.SetExtension( VideoRotation::kVideoRotation_270); // Create a packet without the extension map but with the same content. - RtpPacketReceived received_packet; + // We need to parse it using the extension map to identify extensions properly + // since RtpTransport is effectively skipped in this test. + RtpPacketReceived received_packet(&extension_map); ASSERT_TRUE(received_packet.Parse(reference_packet.Buffer())); receive_channel_->OnPacketReceived(received_packet); @@ -3988,8 +3946,6 @@ TEST_F(Vp9SettingsTest, MultipleSsrcsEnablesSvc) { FakeVideoSendStream* stream = AddSendStream(CreateSimStreamParams("cname", ssrcs)); - VideoSendStream::Config config = stream->GetConfig().Copy(); - FrameForwarder frame_forwarder; EXPECT_TRUE(send_channel_->SetVideoSend(ssrcs[0], nullptr, &frame_forwarder)); send_channel_->SetSend(true); @@ -4017,7 +3973,7 @@ TEST_F(Vp9SettingsTest, SvcModeCreatesSingleRtpStream) { FakeVideoSendStream* stream = AddSendStream(CreateSimStreamParams("cname", ssrcs)); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); // Despite 3 ssrcs provided, single layer is used. EXPECT_EQ(1u, config.rtp.ssrcs.size()); @@ -4079,8 +4035,6 @@ TEST_F(Vp9SettingsTest, MaxBitrateDeterminedBySvcResolutions) { FakeVideoSendStream* stream = AddSendStream(CreateSimStreamParams("cname", ssrcs)); - VideoSendStream::Config config = stream->GetConfig().Copy(); - FrameForwarder frame_forwarder; EXPECT_TRUE(send_channel_->SetVideoSend(ssrcs[0], nullptr, &frame_forwarder)); send_channel_->SetSend(true); @@ -4122,8 +4076,6 @@ TEST_F(Vp9SettingsTest, Vp9SvcTargetBitrateCappedByMax) { FakeVideoSendStream* stream = AddSendStream(CreateSimStreamParams("cname", ssrcs)); - VideoSendStream::Config config = stream->GetConfig().Copy(); - FrameForwarder frame_forwarder; EXPECT_TRUE(send_channel_->SetVideoSend(ssrcs[0], nullptr, &frame_forwarder)); send_channel_->SetSend(true); @@ -4471,7 +4423,7 @@ TEST_F(WebRtcVideoChannelTest, SetDefaultSendCodecs) { const std::vector rtx_ssrcs = MAKE_VECTOR(kRtxSsrcs1); FakeVideoSendStream* stream = AddSendStream(CreateSimWithRtxStreamParams("cname", ssrcs, rtx_ssrcs)); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); // Make sure NACK and FEC are enabled on the correct payload types. EXPECT_EQ(1000, config.rtp.nack.rtp_history_ms); @@ -4490,7 +4442,7 @@ TEST_F(WebRtcVideoChannelTest, SetSendCodecsWithoutPacketization) { EXPECT_TRUE(send_channel_->SetSenderParameters(parameters)); FakeVideoSendStream* stream = AddSendStream(); - const VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_FALSE(config.rtp.raw_payload); } @@ -4501,7 +4453,7 @@ TEST_F(WebRtcVideoChannelTest, SetSendCodecsWithPacketization) { EXPECT_TRUE(send_channel_->SetSenderParameters(parameters)); FakeVideoSendStream* stream = AddSendStream(); - const VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_TRUE(config.rtp.raw_payload); } @@ -4511,7 +4463,7 @@ TEST_F(WebRtcVideoChannelTest, SetSendCodecsWithPacketization) { // default. TEST_F(WebRtcVideoChannelTest, FlexfecSendCodecWithoutSsrcNotExposedByDefault) { FakeVideoSendStream* stream = AddSendStream(); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(-1, config.rtp.flexfec.payload_type); EXPECT_EQ(0U, config.rtp.flexfec.ssrc); @@ -4521,7 +4473,7 @@ TEST_F(WebRtcVideoChannelTest, FlexfecSendCodecWithoutSsrcNotExposedByDefault) { TEST_F(WebRtcVideoChannelTest, FlexfecSendCodecWithSsrcNotExposedByDefault) { FakeVideoSendStream* stream = AddSendStream( CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc)); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(-1, config.rtp.flexfec.payload_type); EXPECT_EQ(0U, config.rtp.flexfec.ssrc); @@ -4586,7 +4538,7 @@ TEST_F(WebRtcVideoChannelFlexfecRecvTest, SetDefaultRecvCodecsWithSsrc) { const auto* stream = streams.front(); const FlexfecReceiveStream::Config& config = stream->GetConfig(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, config.payload_type); - EXPECT_EQ(kFlexfecSsrc, config.rtp.remote_ssrc); + EXPECT_EQ(kFlexfecSsrc, config.remote_ssrc); ASSERT_EQ(1U, config.protected_media_ssrcs.size()); EXPECT_EQ(kSsrcs1[0], config.protected_media_ssrcs[0]); @@ -4719,7 +4671,7 @@ class WebRtcVideoChannelFlexfecSendRecvTest : public WebRtcVideoChannelTest { TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, SetDefaultSendCodecsWithoutSsrc) { FakeVideoSendStream* stream = AddSendStream(); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, config.rtp.flexfec.payload_type); EXPECT_EQ(0U, config.rtp.flexfec.ssrc); @@ -4729,7 +4681,7 @@ TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, SetDefaultSendCodecsWithoutSsrc) { TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, SetDefaultSendCodecsWithSsrc) { FakeVideoSendStream* stream = AddSendStream( CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc)); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, config.rtp.flexfec.payload_type); EXPECT_EQ(kFlexfecSsrc, config.rtp.flexfec.ssrc); @@ -4743,7 +4695,7 @@ TEST_F(WebRtcVideoChannelTest, SetSendCodecsWithoutFec) { ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); FakeVideoSendStream* stream = AddSendStream(); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(-1, config.rtp.ulpfec.ulpfec_payload_type); EXPECT_EQ(-1, config.rtp.ulpfec.red_payload_type); @@ -4755,7 +4707,7 @@ TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, SetSendCodecsWithoutFec) { ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); FakeVideoSendStream* stream = AddSendStream(); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(-1, config.rtp.flexfec.payload_type); } @@ -4777,7 +4729,7 @@ TEST_F(WebRtcVideoChannelFlexfecRecvTest, SetRecvCodecsWithFec) { flexfec_stream->GetConfig(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, flexfec_stream_config.payload_type); - EXPECT_EQ(kFlexfecSsrc, flexfec_stream_config.rtp.remote_ssrc); + EXPECT_EQ(kFlexfecSsrc, flexfec_stream_config.remote_ssrc); ASSERT_EQ(1U, flexfec_stream_config.protected_media_ssrcs.size()); EXPECT_EQ(kSsrcs1[0], flexfec_stream_config.protected_media_ssrcs[0]); const std::vector& video_streams = @@ -4785,8 +4737,6 @@ TEST_F(WebRtcVideoChannelFlexfecRecvTest, SetRecvCodecsWithFec) { const FakeVideoReceiveStream* video_stream = video_streams.front(); const VideoReceiveStreamInterface::Config& video_stream_config = video_stream->GetConfig(); - EXPECT_EQ(video_stream_config.rtp.local_ssrc, - flexfec_stream_config.rtp.local_ssrc); EXPECT_EQ(video_stream_config.rtp.rtcp_mode, flexfec_stream_config.rtcp_mode); EXPECT_EQ(video_stream_config.rtcp_send_transport, flexfec_stream_config.rtcp_send_transport); @@ -4804,7 +4754,7 @@ TEST_F(WebRtcVideoChannelFlexfecRecvTest, ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); FakeVideoSendStream* stream = AddSendStream(); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(-1, config.rtp.flexfec.payload_type); EXPECT_EQ(0u, config.rtp.flexfec.ssrc); @@ -4820,7 +4770,7 @@ TEST_F(WebRtcVideoChannelFlexfecRecvTest, FakeVideoSendStream* stream = AddSendStream( CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc)); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(-1, config.rtp.flexfec.payload_type); EXPECT_EQ(0u, config.rtp.flexfec.ssrc); @@ -4907,7 +4857,7 @@ TEST_F(WebRtcVideoChannelTest, SetSendCodecsWithoutFecDisablesFec) { ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); FakeVideoSendStream* stream = AddSendStream(); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(GetEngineCodec("ulpfec").id, config.rtp.ulpfec.ulpfec_payload_type); @@ -4915,8 +4865,8 @@ TEST_F(WebRtcVideoChannelTest, SetSendCodecsWithoutFecDisablesFec) { ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); stream = fake_call_->GetVideoSendStreams()[0]; ASSERT_TRUE(stream != nullptr); - config = stream->GetConfig().Copy(); - EXPECT_EQ(-1, config.rtp.ulpfec.ulpfec_payload_type) + const VideoSendStream::Config& config2 = stream->GetConfig(); + EXPECT_EQ(-1, config2.rtp.ulpfec.ulpfec_payload_type) << "SetSendCodec without ULPFEC should disable current ULPFEC."; } @@ -4929,7 +4879,7 @@ TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, FakeVideoSendStream* stream = AddSendStream( CreatePrimaryWithFecFrStreamParams("cname", kSsrcs1[0], kFlexfecSsrc)); - VideoSendStream::Config config = stream->GetConfig().Copy(); + const VideoSendStream::Config& config = stream->GetConfig(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, config.rtp.flexfec.payload_type); EXPECT_EQ(kFlexfecSsrc, config.rtp.flexfec.ssrc); @@ -4940,8 +4890,8 @@ TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); stream = fake_call_->GetVideoSendStreams()[0]; ASSERT_TRUE(stream != nullptr); - config = stream->GetConfig().Copy(); - EXPECT_EQ(-1, config.rtp.flexfec.payload_type) + const VideoSendStream::Config& config2 = stream->GetConfig(); + EXPECT_EQ(-1, config2.rtp.flexfec.payload_type) << "SetSendCodec without FlexFEC should disable current FlexFEC."; } @@ -5374,6 +5324,7 @@ TEST_F(WebRtcVideoChannelTest, DuplicateUlpfecCodecIsDropped) { parameters.codecs.push_back( CreateVideoCodec(kSecondUlpfecPayloadType, kUlpfecCodecName)); ASSERT_TRUE(receive_channel_->SetReceiverParameters(parameters)); + time_controller_.AdvanceTime(TimeDelta::Zero()); FakeVideoReceiveStream* recv_stream = AddRecvStream(); EXPECT_EQ(kFirstUlpfecPayloadType, @@ -5391,6 +5342,7 @@ TEST_F(WebRtcVideoChannelTest, DuplicateRedCodecIsDropped) { parameters.codecs.push_back( CreateVideoCodec(kSecondRedPayloadType, kRedCodecName)); ASSERT_TRUE(receive_channel_->SetReceiverParameters(parameters)); + time_controller_.AdvanceTime(TimeDelta::Zero()); FakeVideoReceiveStream* recv_stream = AddRecvStream(); EXPECT_EQ(kFirstRedPayloadType, @@ -5624,7 +5576,7 @@ TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, const FakeFlexfecReceiveStream* stream_with_recv_params = streams.front(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, stream_with_recv_params->GetConfig().payload_type); - EXPECT_EQ(kFlexfecSsrc, stream_with_recv_params->GetConfig().rtp.remote_ssrc); + EXPECT_EQ(kFlexfecSsrc, stream_with_recv_params->GetConfig().remote_ssrc); EXPECT_EQ(1U, stream_with_recv_params->GetConfig().protected_media_ssrcs.size()); EXPECT_EQ(kSsrcs1[0], @@ -5638,7 +5590,7 @@ TEST_F(WebRtcVideoChannelFlexfecSendRecvTest, const FakeFlexfecReceiveStream* stream_with_send_params = streams.front(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, stream_with_send_params->GetConfig().payload_type); - EXPECT_EQ(kFlexfecSsrc, stream_with_send_params->GetConfig().rtp.remote_ssrc); + EXPECT_EQ(kFlexfecSsrc, stream_with_send_params->GetConfig().remote_ssrc); EXPECT_EQ(1U, stream_with_send_params->GetConfig().protected_media_ssrcs.size()); EXPECT_EQ(kSsrcs1[0], @@ -5675,7 +5627,7 @@ TEST_F(WebRtcVideoChannelTest, VideoReceiverParameters parameters; parameters.codecs.push_back(GetEngineCodec("VP8")); parameters.codecs.push_back(GetEngineCodec("VP8")); - parameters.codecs[1].id += 1; + parameters.codecs[1].id = PayloadType(parameters.codecs[1].id + 1); EXPECT_TRUE(receive_channel_->SetReceiverParameters(parameters)); } @@ -5706,6 +5658,54 @@ TEST_F(WebRtcVideoChannelTest, ReceiveStreamReceivingByDefault) { EXPECT_TRUE(AddRecvStream()->IsReceiving()); } +TEST_F(WebRtcVideoChannelTest, + SetSendParametersRecreatesStreamAndClearsAdaptationStats) { + FakeVideoSendStream* stream = AddSendStream(); + webrtc::VideoSendStream::Stats stats; + // Set cumulative stats that should be retained. + stats.number_of_cpu_adapt_changes = 2; + stats.number_of_quality_adapt_changes = 3; + stats.quality_limitation_durations_ms[webrtc::QualityLimitationReason::kCpu] = + 100; + + // Set ephemeral flags that should be cleared by RecreateWebRtcStream. + stats.bw_limited_resolution = true; + stats.bw_limited_framerate = true; + stats.cpu_limited_resolution = true; + stats.cpu_limited_framerate = true; + stats.quality_limitation_reason = webrtc::QualityLimitationReason::kBandwidth; + + stream->SetStats(stats); + + // Trigger stream recreation by changing construction-time parameters. + EXPECT_EQ(1, fake_call_->GetNumCreatedSendStreams()); + VideoSenderParameters parameters; + parameters.codecs.push_back(GetEngineCodec("VP8")); + parameters.extmap_allow_mixed = true; // This forces recreation. + EXPECT_TRUE(send_channel_->SetSenderParameters(parameters)); + EXPECT_EQ(2, fake_call_->GetNumCreatedSendStreams()); + + ASSERT_EQ(1U, fake_call_->GetVideoSendStreams().size()); + FakeVideoSendStream* new_stream = fake_call_->GetVideoSendStreams()[0]; + + webrtc::VideoSendStream::Stats new_stats = + static_cast(new_stream)->GetStats(); + + // Check that ephemeral flags were cleared. + EXPECT_FALSE(new_stats.bw_limited_resolution); + EXPECT_FALSE(new_stats.bw_limited_framerate); + EXPECT_FALSE(new_stats.cpu_limited_resolution); + EXPECT_FALSE(new_stats.cpu_limited_framerate); + EXPECT_EQ(webrtc::QualityLimitationReason::kNone, + new_stats.quality_limitation_reason); + + // Check that cumulative stats were retained. + EXPECT_EQ(2, new_stats.number_of_cpu_adapt_changes); + EXPECT_EQ(3, new_stats.number_of_quality_adapt_changes); + EXPECT_EQ(100, new_stats.quality_limitation_durations_ms + [webrtc::QualityLimitationReason::kCpu]); +} + TEST_F(WebRtcVideoChannelTest, SetSend) { FakeVideoSendStream* stream = AddSendStream(); EXPECT_FALSE(stream->IsSending()); @@ -7706,6 +7706,7 @@ TEST_F(WebRtcVideoChannelTest, VideoReceiverParameters parameters; parameters.codecs.push_back(GetEngineCodec("VP8")); ASSERT_TRUE(receive_channel_->SetReceiverParameters(parameters)); + time_controller_.AdvanceTime(TimeDelta::Zero()); // No streams signaled and no packets received, so we should not have any // stream objects created yet. @@ -9619,56 +9620,6 @@ TEST_F(WebRtcVideoChannelTest, EXPECT_TRUE(receive_channel_->AddRecvStream(params)); } -void WebRtcVideoChannelTest::TestReceiverLocalSsrcConfiguration( - bool receiver_first) { - EXPECT_TRUE(send_channel_->SetSenderParameters(send_parameters_)); - - const uint32_t kSenderSsrc = 0xC0FFEE; - const uint32_t kSecondSenderSsrc = 0xBADCAFE; - const uint32_t kReceiverSsrc = 0x4711; - const uint32_t kExpectedDefaultReceiverSsrc = 1; - - if (receiver_first) { - AddRecvStream(StreamParams::CreateLegacy(kReceiverSsrc)); - std::vector receive_streams = - fake_call_->GetVideoReceiveStreams(); - ASSERT_EQ(1u, receive_streams.size()); - // Default local SSRC when we have no sender. - EXPECT_EQ(kExpectedDefaultReceiverSsrc, - receive_streams[0]->GetConfig().rtp.local_ssrc); - } - AddSendStream(StreamParams::CreateLegacy(kSenderSsrc)); - if (!receiver_first) - AddRecvStream(StreamParams::CreateLegacy(kReceiverSsrc)); - std::vector receive_streams = - fake_call_->GetVideoReceiveStreams(); - ASSERT_EQ(1u, receive_streams.size()); - EXPECT_EQ(kSenderSsrc, receive_streams[0]->GetConfig().rtp.local_ssrc); - - // Removing first sender should fall back to another (in this case the second) - // local send stream's SSRC. - AddSendStream(StreamParams::CreateLegacy(kSecondSenderSsrc)); - ASSERT_TRUE(send_channel_->RemoveSendStream(kSenderSsrc)); - receive_streams = fake_call_->GetVideoReceiveStreams(); - ASSERT_EQ(1u, receive_streams.size()); - EXPECT_EQ(kSecondSenderSsrc, receive_streams[0]->GetConfig().rtp.local_ssrc); - - // Removing the last sender should fall back to default local SSRC. - ASSERT_TRUE(send_channel_->RemoveSendStream(kSecondSenderSsrc)); - receive_streams = fake_call_->GetVideoReceiveStreams(); - ASSERT_EQ(1u, receive_streams.size()); - EXPECT_EQ(kExpectedDefaultReceiverSsrc, - receive_streams[0]->GetConfig().rtp.local_ssrc); -} - -TEST_F(WebRtcVideoChannelTest, ConfiguresLocalSsrc) { - TestReceiverLocalSsrcConfiguration(false); -} - -TEST_F(WebRtcVideoChannelTest, ConfiguresLocalSsrcOnExistingReceivers) { - TestReceiverLocalSsrcConfiguration(true); -} - TEST_F(WebRtcVideoChannelTest, Simulcast_QualityScalingNotAllowed) { FakeVideoSendStream* stream = SetUpSimulcast(true, /*with_rtx=*/true); EXPECT_FALSE(stream->GetEncoderConfig().is_quality_scaling_allowed); @@ -9794,7 +9745,8 @@ TEST_F(WebRtcVideoChannelTest, GenerateKeyFrameSimulcast) { class WebRtcVideoChannelSimulcastTest : public ::testing::Test { public: WebRtcVideoChannelSimulcastTest() - : env_(CreateTestEnvironment()), + : run_loop_(), + env_(CreateTestEnvironment()), fake_call_(env_), encoder_factory_(new FakeWebRtcVideoEncoderFactory), decoder_factory_(new FakeWebRtcVideoDecoderFactory), @@ -9960,6 +9912,7 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test { return streams[streams.size() - 1]; } + test::RunLoop run_loop_; Environment env_; FakeCall fake_call_; FakeWebRtcVideoEncoderFactory* encoder_factory_; @@ -10068,6 +10021,11 @@ TEST_F(WebRtcVideoChannelBaseTest, EncoderSelectorSwitchCodec) { EXPECT_TRUE(send_channel_->SetSenderParameters(parameters)); send_channel_->SetSend(true); + RtpParameters rtp_parameters = send_channel_->GetRtpSendParameters(kSsrc); + ASSERT_EQ(1u, rtp_parameters.encodings.size()); + rtp_parameters.encodings[0].max_bitrate_bps = 1000000; + EXPECT_TRUE(send_channel_->SetRtpSendParameters(kSsrc, rtp_parameters).ok()); + std::optional codec = send_channel_->GetSendCodec(); ASSERT_TRUE(codec); EXPECT_EQ("VP8", codec->name); @@ -10089,23 +10047,20 @@ TEST_F(WebRtcVideoChannelBaseTest, EncoderSelectorSwitchCodec) { TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecast) { VideoSenderParameters parameters; - parameters.codecs.push_back(GetEngineCodec("VP8")); - ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); - FakeVideoSendStream* stream = AddSendStream(); + const uint32_t ssrc = stream->GetConfig().rtp.ssrcs[0]; FrameForwarder frame_forwarder; FakeFrameSource frame_source(1280, 720, TimeDelta::Seconds(1) / 30, env_.clock().CurrentTime()); - EXPECT_TRUE( - send_channel_->SetVideoSend(last_ssrc_, nullptr, &frame_forwarder)); + EXPECT_TRUE(send_channel_->SetVideoSend(ssrc, nullptr, &frame_forwarder)); + frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); { // TEST scale_resolution_down_to < frame size - RtpParameters rtp_parameters = - send_channel_->GetRtpSendParameters(last_ssrc_); + RtpParameters rtp_parameters = send_channel_->GetRtpSendParameters(ssrc); EXPECT_EQ(1UL, rtp_parameters.encodings.size()); rtp_parameters.encodings[0].scale_resolution_down_to = {.width = 640, .height = 360}; - send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters); + send_channel_->SetRtpSendParameters(ssrc, rtp_parameters); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); @@ -10116,11 +10071,11 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecast) { } { // TEST scale_resolution_down_to == frame size - auto rtp_parameters = send_channel_->GetRtpSendParameters(last_ssrc_); + auto rtp_parameters = send_channel_->GetRtpSendParameters(ssrc); EXPECT_EQ(1UL, rtp_parameters.encodings.size()); rtp_parameters.encodings[0].scale_resolution_down_to = {.width = 1280, .height = 720}; - send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters); + send_channel_->SetRtpSendParameters(ssrc, rtp_parameters); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); auto streams = stream->GetVideoStreams(); @@ -10130,11 +10085,11 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecast) { } { // TEST scale_resolution_down_to > frame size - auto rtp_parameters = send_channel_->GetRtpSendParameters(last_ssrc_); + auto rtp_parameters = send_channel_->GetRtpSendParameters(ssrc); EXPECT_EQ(1UL, rtp_parameters.encodings.size()); rtp_parameters.encodings[0].scale_resolution_down_to = {.width = 2 * 1280, .height = 2 * 720}; - send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters); + send_channel_->SetRtpSendParameters(ssrc, rtp_parameters); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); auto streams = stream->GetVideoStreams(); @@ -10143,7 +10098,7 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecast) { EXPECT_EQ(checked_cast(720), streams[0].height); } - EXPECT_TRUE(send_channel_->SetVideoSend(last_ssrc_, nullptr, nullptr)); + EXPECT_TRUE(send_channel_->SetVideoSend(ssrc, nullptr, nullptr)); } TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecastScaling) { @@ -10152,18 +10107,18 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecastScaling) { ASSERT_TRUE(send_channel_->SetSenderParameters(parameters)); FakeVideoSendStream* stream = AddSendStream(); + const uint32_t ssrc = stream->GetConfig().rtp.ssrcs[0]; FrameForwarder frame_forwarder; FakeFrameSource frame_source(1280, 720, TimeDelta::Seconds(1) / 30, env_.clock().CurrentTime()); - EXPECT_TRUE( - send_channel_->SetVideoSend(last_ssrc_, nullptr, &frame_forwarder)); + EXPECT_TRUE(send_channel_->SetVideoSend(ssrc, nullptr, &frame_forwarder)); { - auto rtp_parameters = send_channel_->GetRtpSendParameters(last_ssrc_); + auto rtp_parameters = send_channel_->GetRtpSendParameters(ssrc); EXPECT_EQ(1UL, rtp_parameters.encodings.size()); rtp_parameters.encodings[0].scale_resolution_down_to = {.width = 720, .height = 720}; - send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters); + send_channel_->SetRtpSendParameters(ssrc, rtp_parameters); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); @@ -10176,11 +10131,11 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecastScaling) { } { - auto rtp_parameters = send_channel_->GetRtpSendParameters(last_ssrc_); + auto rtp_parameters = send_channel_->GetRtpSendParameters(ssrc); EXPECT_EQ(1UL, rtp_parameters.encodings.size()); rtp_parameters.encodings[0].scale_resolution_down_to = {.width = 1280, .height = 1280}; - send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters); + send_channel_->SetRtpSendParameters(ssrc, rtp_parameters); frame_forwarder.IncomingCapturedFrame(frame_source.GetFrame()); @@ -10192,11 +10147,11 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecastScaling) { } { - auto rtp_parameters = send_channel_->GetRtpSendParameters(last_ssrc_); + auto rtp_parameters = send_channel_->GetRtpSendParameters(ssrc); EXPECT_EQ(1UL, rtp_parameters.encodings.size()); rtp_parameters.encodings[0].scale_resolution_down_to = {.width = 650, .height = 650}; - send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters); + send_channel_->SetRtpSendParameters(ssrc, rtp_parameters); auto streams = stream->GetVideoStreams(); ASSERT_EQ(streams.size(), 1u); @@ -10207,11 +10162,11 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecastScaling) { } { - auto rtp_parameters = send_channel_->GetRtpSendParameters(last_ssrc_); + auto rtp_parameters = send_channel_->GetRtpSendParameters(ssrc); EXPECT_EQ(1UL, rtp_parameters.encodings.size()); rtp_parameters.encodings[0].scale_resolution_down_to = {.width = 2560, .height = 1440}; - send_channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters); + send_channel_->SetRtpSendParameters(ssrc, rtp_parameters); auto streams = stream->GetVideoStreams(); ASSERT_EQ(streams.size(), 1u); @@ -10220,7 +10175,7 @@ TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSinglecastScaling) { EXPECT_EQ(checked_cast(720), streams[0].height); } - EXPECT_TRUE(send_channel_->SetVideoSend(last_ssrc_, nullptr, nullptr)); + EXPECT_TRUE(send_channel_->SetVideoSend(ssrc, nullptr, nullptr)); } TEST_F(WebRtcVideoChannelTest, ScaleResolutionDownToSimulcast) { diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index b017378fc87..67a27e44cb7 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -50,6 +50,7 @@ #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/priority.h" #include "api/rtc_error.h" #include "api/rtp_headers.h" @@ -85,7 +86,7 @@ #include "media/engine/webrtc_media_engine.h" #include "modules/async_audio_processing/async_audio_processing.h" #include "modules/audio_mixer/audio_mixer_impl.h" -#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/checks.h" #include "rtc_base/dscp.h" @@ -287,12 +288,10 @@ struct AdaptivePtimeConfig { // Move some of this boiler plate code into the config structs themselves. AudioReceiveStreamInterface::Config BuildReceiveStreamConfig( uint32_t remote_ssrc, - uint32_t local_ssrc, bool use_nack, bool enable_non_sender_rtt, RtcpMode rtcp_mode, const std::vector& stream_ids, - const std::vector& /* extensions */, Transport* rtcp_send_transport, const scoped_refptr& decoder_factory, const std::map& decoder_map, @@ -304,7 +303,6 @@ AudioReceiveStreamInterface::Config BuildReceiveStreamConfig( scoped_refptr frame_transformer) { AudioReceiveStreamInterface::Config config; config.rtp.remote_ssrc = remote_ssrc; - config.rtp.local_ssrc = local_ssrc; config.rtp.nack.rtp_history_ms = use_nack ? kNackRtpHistoryMs : 0; config.rtp.rtcp_mode = rtcp_mode; if (!stream_ids.empty()) { @@ -527,7 +525,7 @@ void WebRtcVoiceEngine::Init() { // TaskQueue expects to be created/destroyed on the same thread. RTC_DCHECK(!low_priority_worker_queue_); low_priority_worker_queue_ = env_.task_queue_factory().CreateTaskQueue( - "rtc-low-prio", TaskQueueFactory::Priority::LOW); + "rtc-low-prio", TaskQueueFactory::Priority::kLow); adm_helpers::Init(adm()); @@ -765,7 +763,7 @@ const std::vector& WebRtcVoiceEngine::LegacyRecvCodecs() const { std::vector WebRtcVoiceEngine::GetRtpHeaderExtensions( - const webrtc::FieldTrialsView* field_trials) const { + const FieldTrialsView* field_trials) const { RTC_DCHECK(signal_thread_checker_.IsCurrent()); std::vector result; // id is *not* incremented for non-default extensions, UsedIds needs to @@ -1132,6 +1130,11 @@ class WebRtcVoiceSendChannel::WebRtcAudioSendStream : public AudioSource::Sink { config_.send_codec_spec->target_bitrate_bps = send_rate; } } + + rtp_parameters_.rtcp.cname = config_.rtp.c_name; + rtp_parameters_.rtcp.reduced_size = + config_.rtp.rtcp_mode == RtcpMode::kReducedSize; + if (reconfigure_send_stream) { // Changing adaptive_ptime may update the audio network adaptor config // used. @@ -1142,10 +1145,6 @@ class WebRtcVoiceSendChannel::WebRtcAudioSendStream : public AudioSource::Sink { InvokeSetParametersCallback(callback, RTCError::OK()); } - rtp_parameters_.rtcp.cname = config_.rtp.c_name; - rtp_parameters_.rtcp.reduced_size = - config_.rtp.rtcp_mode == RtcpMode::kReducedSize; - // parameters.encodings[0].active could have changed. UpdateSendState(); return RTCError::OK(); @@ -1358,11 +1357,7 @@ bool WebRtcVoiceSendChannel::SetSenderParameters( } } if (needs_update) { - RTCError error = - send_stream->SetRtpParameters(rtp_parameters, nullptr); - if (error.ok() && on_rtp_send_parameters_changed_callback_) { - on_rtp_send_parameters_changed_callback_(ssrc, rtp_parameters); - } + send_stream->SetRtpParameters(rtp_parameters, nullptr); } else { RTC_DCHECK(rtp_parameters == send_stream->rtp_parameters()); } @@ -1374,10 +1369,6 @@ bool WebRtcVoiceSendChannel::SetSenderParameters( return false; } - if (!ValidateRtpExtensions(params.extensions, send_rtp_extensions_)) { - return false; - } - if (ExtmapAllowMixed() != params.extmap_allow_mixed) { SetExtmapAllowMixed(params.extmap_allow_mixed); for (auto& it : send_streams_) { @@ -1442,7 +1433,7 @@ bool WebRtcVoiceSendChannel::SetSendCodecs( const std::vector& codecs, std::optional preferred_codec) { RTC_DCHECK_RUN_ON(worker_thread_); - dtmf_payload_type_ = std::nullopt; + dtmf_payload_type_ = PayloadType::NotSet(); dtmf_payload_freq_ = -1; // Validate supplied codecs list. @@ -1464,7 +1455,8 @@ bool WebRtcVoiceSendChannel::SetSendCodecs( for (const Codec& codec : codecs) { if (IsCodec(codec, kDtmfCodecName)) { dtmf_codecs.push_back(codec); - if (!dtmf_payload_type_ || codec.clockrate < dtmf_payload_freq_) { + if ((dtmf_payload_type_ == PayloadType::NotSet()) || + codec.clockrate < dtmf_payload_freq_) { dtmf_payload_type_ = codec.id; dtmf_payload_freq_ = codec.clockrate; } @@ -1524,7 +1516,7 @@ bool WebRtcVoiceSendChannel::SetSendCodecs( RTC_LOG(LS_WARNING) << "CN frequency " << cn_codec.clockrate << " not supported."; } else { - send_codec_spec->cng_payload_type = cn_codec.id; + send_codec_spec->cng_payload_type = cn_codec.id.value(); } break; } @@ -1548,7 +1540,7 @@ bool WebRtcVoiceSendChannel::SetSendCodecs( const Codec& red_codec = codecs[i]; if (IsCodec(red_codec, kRedCodecName) && CheckRedParameters(red_codec, *send_codec_spec)) { - send_codec_spec->red_payload_type = red_codec.id; + send_codec_spec->red_payload_type = red_codec.id.value(); break; } } @@ -1668,10 +1660,6 @@ bool WebRtcVoiceSendChannel::RemoveSendStream(uint32_t ssrc) { it->second->SetSend(false); - // TODO(solenberg): If we're removing the receiver_reports_ssrc_ stream, find - // the first active send stream and use that instead, reassociating receive - // streams. - delete it->second; send_streams_.erase(it); if (send_streams_.empty()) { @@ -1687,14 +1675,6 @@ void WebRtcVoiceSendChannel::SetSsrcListChangedCallback( ssrc_list_changed_callback_ = std::move(callback); } -void WebRtcVoiceSendChannel::SetOnRtpSendParametersChanged( - absl::AnyInvocable, const RtpParameters&)> - callback) { - RTC_DCHECK_RUN_ON(worker_thread_); - RTC_DCHECK(!on_rtp_send_parameters_changed_callback_); - on_rtp_send_parameters_changed_callback_ = std::move(callback); -} - bool WebRtcVoiceSendChannel::SetLocalSource(uint32_t ssrc, AudioSource* source) { RTC_DCHECK_RUN_ON(worker_thread_); @@ -1721,7 +1701,7 @@ bool WebRtcVoiceSendChannel::SetLocalSource(uint32_t ssrc, bool WebRtcVoiceSendChannel::CanInsertDtmf() { RTC_DCHECK_RUN_ON(worker_thread_); - return dtmf_payload_type_.has_value() && send_; + return (dtmf_payload_type_ != PayloadType::NotSet()) && send_; } void WebRtcVoiceSendChannel::SetFrameEncryptor( @@ -1885,6 +1865,21 @@ bool WebRtcVoiceSendChannel::GetStats(VoiceMediaSendInfo* info) { return true; } +absl::AnyInvocable()> +WebRtcVoiceSendChannel::GetStatsCallback() { + return [this, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VoiceMediaSendInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; +} + bool WebRtcVoiceSendChannel::SenderNackEnabled() const { RTC_DCHECK_RUN_ON(worker_thread_); if (!send_codec_spec_) { @@ -1910,7 +1905,7 @@ void WebRtcVoiceSendChannel::FillSendCodecStats( }); if (codec != send_codecs_.end()) { voice_media_info->send_codecs.insert( - std::make_pair(codec->id, codec->ToCodecParameters())); + std::make_pair(codec->id.value(), codec->ToCodecParameters())); } } } @@ -1949,6 +1944,16 @@ RtpParameters WebRtcVoiceSendChannel::GetRtpSendParameters( return rtp_params; } +absl::AnyInvocable +WebRtcVoiceSendChannel::GetRtpSendParametersCallback() const { + return [this, safety = task_safety_.flag()]( + uint32_t ssrc) mutable -> RtpParameters { + if (!safety->alive()) + return RtpParameters(); + return GetRtpSendParameters(ssrc); + }; +} + RTCError WebRtcVoiceSendChannel::SetRtpSendParameters( uint32_t ssrc, const RtpParameters& parameters, @@ -2029,12 +2034,7 @@ RTCError WebRtcVoiceSendChannel::SetRtpSendParameters( // Codecs are handled at the WebRtcVoiceMediaChannel level. RtpParameters reduced_params = parameters; reduced_params.codecs.clear(); - RTCError error = - it->second->SetRtpParameters(reduced_params, std::move(callback)); - if (error.ok() && on_rtp_send_parameters_changed_callback_) { - on_rtp_send_parameters_changed_callback_(ssrc, parameters); - } - return error; + return it->second->SetRtpParameters(reduced_params, std::move(callback)); } // -------------------------- WebRtcVoiceReceiveChannel ---------------------- @@ -2073,7 +2073,7 @@ class WebRtcVoiceReceiveChannel::WebRtcAudioReceiveStream { stream_->SetNackHistory(use_nack ? kNackRtpHistoryMs : 0); } - void SetRtcpMode(::webrtc::RtcpMode mode) { + void SetRtcpMode(webrtc::RtcpMode mode) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); stream_->SetRtcpMode(mode); } @@ -2173,6 +2173,9 @@ WebRtcVoiceReceiveChannel::WebRtcVoiceReceiveChannel( : MediaChannelUtil(call->network_thread(), config.enable_dscp), env_(env), worker_thread_(call->worker_thread()), + network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( + /*alive=*/true, + call->network_thread())), engine_(engine), call_(call), audio_config_(config.audio), @@ -2208,20 +2211,11 @@ bool WebRtcVoiceReceiveChannel::SetReceiverParameters( return false; } - if (!ValidateRtpExtensions(params.extensions, recv_rtp_extensions_)) { - return false; - } - std::vector filtered_extensions = - FilterRtpExtensions(params.extensions, RtpExtension::IsSupportedForAudio, - false, env_.field_trials()); - if (recv_rtp_extensions_ != filtered_extensions) { - recv_rtp_extensions_.swap(filtered_extensions); - recv_rtp_extension_map_ = RtpHeaderExtensionMap(recv_rtp_extensions_); - } // RTCP mode, NACK, and receive-side RTT are not configured here because they // enable send functionality in the receive channels. This functionality is // instead configured using the SetReceiveRtcpMode, SetReceiveNackEnabled, and // SetReceiveNonSenderRttEnabled methods. + recv_params_ = params; return true; } @@ -2239,7 +2233,10 @@ RtpParameters WebRtcVoiceReceiveChannel::GetRtpReceiverParameters( } rtp_params.encodings.emplace_back(); rtp_params.encodings.back().ssrc = it->second->stream().remote_ssrc(); - rtp_params.header_extensions = recv_rtp_extensions_; + + rtp_params.header_extensions = FilterRtpExtensions( + recv_params_.extensions, RtpExtension::IsSupportedForAudio, false, + env_.field_trials()); for (const Codec& codec : recv_codecs_) { rtp_params.codecs.push_back(codec.ToCodecParameters()); @@ -2379,7 +2376,7 @@ bool WebRtcVoiceReceiveChannel::SetRecvCodecs( return true; } -void WebRtcVoiceReceiveChannel::SetRtcpMode(::webrtc::RtcpMode mode) { +void WebRtcVoiceReceiveChannel::SetRtcpMode(webrtc::RtcpMode mode) { RTC_DCHECK_RUN_ON(worker_thread_); // Check if the reduced size RTCP status changed on the // preferred send codec, and in that case reconfigure all receive streams. @@ -2467,9 +2464,8 @@ bool WebRtcVoiceReceiveChannel::AddRecvStream(const StreamParams& sp) { // Create a new channel for receiving audio data. auto config = BuildReceiveStreamConfig( - ssrc, receiver_reports_ssrc_, recv_nack_enabled_, enable_non_sender_rtt_, - recv_rtcp_mode_, sp.stream_ids(), recv_rtp_extensions_, transport(), - engine()->decoder_factory_, decoder_map_, + ssrc, recv_nack_enabled_, enable_non_sender_rtt_, recv_rtcp_mode_, + sp.stream_ids(), transport(), engine()->decoder_factory_, decoder_map_, engine()->audio_jitter_buffer_max_packets_, engine()->audio_jitter_buffer_fast_accelerate_, engine()->audio_jitter_buffer_min_delay_ms_, unsignaled_frame_decryptor_, @@ -2503,6 +2499,11 @@ bool WebRtcVoiceReceiveChannel::RemoveRecvStream(uint32_t ssrc) { } void WebRtcVoiceReceiveChannel::ResetUnsignaledRecvStream() { + if (!worker_thread_->IsCurrent()) { + worker_thread_->PostTask(SafeTask( + task_safety_.flag(), [this]() { ResetUnsignaledRecvStream(); })); + return; + } RTC_DCHECK_RUN_ON(worker_thread_); RTC_LOG(LS_INFO) << "ResetUnsignaledRecvStream."; unsignaled_stream_params_ = StreamParams(); @@ -2523,24 +2524,6 @@ std::optional WebRtcVoiceReceiveChannel::GetUnsignaledSsrc() const { return unsignaled_recv_ssrcs_.back(); } -void WebRtcVoiceReceiveChannel::ChooseReceiverReportSsrc( - const std::set& choices) { - RTC_DCHECK_RUN_ON(worker_thread_); - // Don't change SSRC if set is empty. Note that this differs from - // the behavior of video. - if (choices.empty()) { - return; - } - if (choices.find(receiver_reports_ssrc_) != choices.end()) { - return; - } - uint32_t ssrc = *(choices.begin()); - receiver_reports_ssrc_ = ssrc; - for (auto& kv : recv_streams_) { - call_->OnLocalSsrcUpdated(kv.second->stream(), ssrc); - } -} - // Not implemented. // TODO(https://crbug.com/webrtc/12676): Implement a fix for the unsignalled // SSRC race that can happen when an m= section goes from receiving to not @@ -2635,37 +2618,17 @@ void WebRtcVoiceReceiveChannel::SetFrameDecryptor( } } -void WebRtcVoiceReceiveChannel::OnPacketReceived( - const RtpPacketReceived& packet) { +void WebRtcVoiceReceiveChannel::OnPacketReceived(RtpPacketReceived packet) { RTC_DCHECK_RUN_ON(&network_thread_checker_); - // TODO(bugs.webrtc.org/11993): This code is very similar to what - // WebRtcVideoChannel::OnPacketReceived does. For maintainability and - // consistency it would be good to move the interaction with - // call_->Receiver() to a common implementation and provide a callback on - // the worker thread for the exception case (DELIVERY_UNKNOWN_SSRC) and - // how retry is attempted. - worker_thread_->PostTask( - SafeTask(task_safety_.flag(), [this, packet = packet]() mutable { - RTC_DCHECK_RUN_ON(worker_thread_); - - // TODO(bugs.webrtc.org/7135): extensions in `packet` is currently set - // in RtpTransport and does not necessarily include extensions specific - // to this channel/MID. Also see comment in - // BaseChannel::MaybeUpdateDemuxerAndRtpExtensions_w. - // It would likely be good if extensions where merged per BUNDLE and - // applied directly in RtpTransport::DemuxPacket; - packet.IdentifyExtensions(recv_rtp_extension_map_); - if (!packet.arrival_time().IsFinite()) { - packet.set_arrival_time(env_.clock().CurrentTime()); - } + if (!packet.arrival_time().IsFinite()) { + packet.set_arrival_time(env_.clock().CurrentTime()); + } - call_->Receiver()->DeliverRtpPacket( - MediaType::AUDIO, std::move(packet), - absl::bind_front( - &WebRtcVoiceReceiveChannel::MaybeCreateDefaultReceiveStream, - this)); - })); + call_->Receiver()->DeliverRtpPacket( + MediaType::AUDIO, std::move(packet), + absl::bind_front( + &WebRtcVoiceReceiveChannel::MaybeCreateDefaultReceiveStream, this)); } bool WebRtcVoiceReceiveChannel::MaybeCreateDefaultReceiveStream( @@ -2705,9 +2668,14 @@ bool WebRtcVoiceReceiveChannel::MaybeCreateDefaultReceiveStream( // it up to the *latest* unsignaled stream we've seen, in order to support // the case where the SSRC of one unsignaled stream changes. if (default_sink_) { - for (uint32_t drop_ssrc : unsignaled_recv_ssrcs_) { - auto it = recv_streams_.find(drop_ssrc); - it->second->SetRawAudioSink(nullptr); + // The new ssrc has already been appended to `unsignaled_recv_ssrcs_`. + // If there are 2 or more streams, the stream at `size - 2` is the previous + // latest stream which currently possesses the default sink. + if (unsignaled_recv_ssrcs_.size() >= 2) { + // Detach the default sink from the previous latest stream. + uint32_t prev_ssrc = + unsignaled_recv_ssrcs_[unsignaled_recv_ssrcs_.size() - 2]; + SetRawAudioSink(prev_ssrc, nullptr); } std::unique_ptr proxy_sink( new ProxySink(default_sink_.get())); @@ -2840,11 +2808,27 @@ void WebRtcVoiceReceiveChannel::FillReceiveCodecStats( }); if (codec != recv_codecs_.end()) { voice_media_info->receive_codecs.insert( - std::make_pair(codec->id, codec->ToCodecParameters())); + std::make_pair(codec->id.value(), codec->ToCodecParameters())); } } } +absl::AnyInvocable()> +WebRtcVoiceReceiveChannel::GetStatsCallback(bool get_and_clear_legacy_stats) { + return + [this, get_and_clear_legacy_stats, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VoiceMediaReceiveInfo info; + if (GetStats(&info, get_and_clear_legacy_stats)) { + return info; + } + return std::nullopt; + }; +} + void WebRtcVoiceReceiveChannel::SetRawAudioSink( uint32_t ssrc, std::unique_ptr sink) { @@ -2914,7 +2898,22 @@ bool WebRtcVoiceReceiveChannel::MaybeDeregisterUnsignaledRecvStream( RTC_DCHECK_RUN_ON(worker_thread_); auto it = absl::c_find(unsignaled_recv_ssrcs_, ssrc); if (it != unsignaled_recv_ssrcs_.end()) { + bool is_latest_unsignaled = (it == unsignaled_recv_ssrcs_.end() - 1); unsignaled_recv_ssrcs_.erase(it); + if (default_sink_) { + // Detach the default sink from the deregistered stream. This is needed + // to prevent the ProxySink from holding a dangling pointer to the + // default_sink_. + SetRawAudioSink(ssrc, nullptr); + if (is_latest_unsignaled && !unsignaled_recv_ssrcs_.empty()) { + // The deregistered stream was the latest unsignaled stream, so it held + // the default sink. Since it was removed, we must pass the default sink + // to the *new* latest unsignaled stream via a new ProxySink. + std::unique_ptr proxy_sink( + new ProxySink(default_sink_.get())); + SetRawAudioSink(unsignaled_recv_ssrcs_.back(), std::move(proxy_sink)); + } + } return true; } return false; diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h index c55c40327e7..f033c2179e7 100644 --- a/media/engine/webrtc_voice_engine.h +++ b/media/engine/webrtc_voice_engine.h @@ -39,6 +39,7 @@ #include "api/field_trials_view.h" #include "api/frame_transformer_interface.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/rtc_error.h" #include "api/rtp_headers.h" #include "api/rtp_parameters.h" @@ -58,7 +59,6 @@ #include "media/base/media_config.h" #include "media/base/media_engine.h" #include "media/base/stream_params.h" -#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/checks.h" #include "rtc_base/network/sent_packet.h" @@ -120,7 +120,7 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface { return decoder_factory_.get(); } std::vector GetRtpHeaderExtensions( - const webrtc::FieldTrialsView* field_trials) const override; + const FieldTrialsView* field_trials) const override; // Starts AEC dump using an existing file. A maximum file size in bytes can be // specified. When the maximum file size is reached, logging is stopped and @@ -163,7 +163,6 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface { scoped_refptr audio_state_ RTC_GUARDED_BY(worker_thread_checker_); const std::vector legacy_send_codecs_; const std::vector legacy_recv_codecs_; - bool is_dumping_aec_ RTC_GUARDED_BY(worker_thread_checker_) = false; bool initialized_ RTC_GUARDED_BY(worker_thread_checker_) = false; // Jitter buffer settings for new streams. @@ -219,13 +218,11 @@ class WebRtcVoiceSendChannel final : public MediaChannelUtil, bool SetSenderParameters(const AudioSenderParameter& params) override; RtpParameters GetRtpSendParameters(uint32_t ssrc) const override; + absl::AnyInvocable GetRtpSendParametersCallback() + const override; RTCError SetRtpSendParameters(uint32_t ssrc, const RtpParameters& parameters, SetParametersCallback callback) override; - void SetOnRtpSendParametersChanged( - absl::AnyInvocable, const RtpParameters&)> - callback) override; - void SetSend(bool send) override; bool SetAudioSend(uint32_t ssrc, bool enable, @@ -253,6 +250,8 @@ class WebRtcVoiceSendChannel final : public MediaChannelUtil, const NetworkRoute& network_route) override; void OnReadyToSend(bool ready) override; bool GetStats(VoiceMediaSendInfo* info) override; + absl::AnyInvocable()> GetStatsCallback() + override; bool SetOptions(const AudioOptions& options) override; // Sets a frame transformer between encoder and packetizer, to transform @@ -286,7 +285,7 @@ class WebRtcVoiceSendChannel final : public MediaChannelUtil, int max_send_bitrate_bps_ RTC_GUARDED_BY(worker_thread_) = 0; AudioOptions options_ RTC_GUARDED_BY(worker_thread_); - std::optional dtmf_payload_type_ RTC_GUARDED_BY(worker_thread_); + PayloadType dtmf_payload_type_ RTC_GUARDED_BY(worker_thread_); int dtmf_payload_freq_ RTC_GUARDED_BY(worker_thread_) = -1; bool enable_non_sender_rtt_ RTC_GUARDED_BY(worker_thread_) = false; bool send_ RTC_GUARDED_BY(worker_thread_) = false; @@ -309,14 +308,10 @@ class WebRtcVoiceSendChannel final : public MediaChannelUtil, // Per peer connection crypto options that last for the lifetime of the peer // connection. const CryptoOptions crypto_options_; - scoped_refptr unsignaled_frame_transformer_ - RTC_GUARDED_BY(worker_thread_); // Callback invoked whenever the list of SSRCs changes. absl::AnyInvocable&)> ssrc_list_changed_callback_ RTC_GUARDED_BY(worker_thread_); - absl::AnyInvocable, const RtpParameters&)> - on_rtp_send_parameters_changed_callback_ RTC_GUARDED_BY(worker_thread_); }; class WebRtcVoiceReceiveChannel final @@ -350,6 +345,9 @@ class WebRtcVoiceReceiveChannel final const AudioOptions& options() const { return options_; } void SetInterface(MediaChannelNetworkInterface* iface) override { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + iface ? network_thread_safety_->SetAlive() + : network_thread_safety_->SetNotAlive(); MediaChannelUtil::SetInterface(iface); } bool SetReceiverParameters(const AudioReceiverParameters& params) override; @@ -362,8 +360,6 @@ class WebRtcVoiceReceiveChannel final void ResetUnsignaledRecvStream() override; std::optional GetUnsignaledSsrc() const override; - void ChooseReceiverReportSsrc(const std::set& choices) override; - void OnDemuxerCriteriaUpdatePending() override; void OnDemuxerCriteriaUpdateComplete() override; @@ -382,9 +378,11 @@ class WebRtcVoiceReceiveChannel final bool SetBaseMinimumPlayoutDelayMs(uint32_t ssrc, int delay_ms) override; std::optional GetBaseMinimumPlayoutDelayMs(uint32_t ssrc) const override; - void OnPacketReceived(const RtpPacketReceived& packet) override; + void OnPacketReceived(RtpPacketReceived packet) override; bool GetStats(VoiceMediaReceiveInfo* info, bool get_and_clear_legacy_stats) override; + absl::AnyInvocable()> GetStatsCallback( + bool get_and_clear_legacy_stats) override; // Set the audio sink for an existing stream. void SetRawAudioSink(uint32_t ssrc, @@ -426,6 +424,7 @@ class WebRtcVoiceReceiveChannel final const Environment env_; TaskQueueBase* const worker_thread_; ScopedTaskSafety task_safety_; + scoped_refptr network_thread_safety_; SequenceChecker network_thread_checker_{SequenceChecker::kDetached}; WebRtcVoiceEngine* const engine_ = nullptr; @@ -463,21 +462,13 @@ class WebRtcVoiceReceiveChannel final // Sink for latest unsignaled stream - may be set before the stream exists. std::unique_ptr default_sink_ RTC_GUARDED_BY(worker_thread_); - // Default SSRC to use for RTCP receiver reports in case of no signaled - // send streams. See: https://code.google.com/p/webrtc/issues/detail?id=4740 - // and https://code.google.com/p/chromium/issues/detail?id=547661 - uint32_t receiver_reports_ssrc_ RTC_GUARDED_BY(worker_thread_) = 0xFA17FA17u; - std::string mid_ RTC_GUARDED_BY(worker_thread_); class WebRtcAudioReceiveStream; std::map recv_streams_ RTC_GUARDED_BY(worker_thread_); - std::vector recv_rtp_extensions_ RTC_GUARDED_BY(worker_thread_); - RtpHeaderExtensionMap recv_rtp_extension_map_ RTC_GUARDED_BY(worker_thread_); - std::optional send_codec_spec_ - RTC_GUARDED_BY(worker_thread_); + AudioReceiverParameters recv_params_ RTC_GUARDED_BY(worker_thread_); // Per peer connection crypto options that last for the lifetime of the peer // connection. diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc index 0f7b5f9f5ea..526a6cbf25a 100644 --- a/media/engine/webrtc_voice_engine_unittest.cc +++ b/media/engine/webrtc_voice_engine_unittest.cc @@ -17,14 +17,13 @@ #include #include #include +#include #include #include #include -#include "absl/functional/any_invocable.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/audio/builtin_audio_processing_builder.h" #include "api/audio_codecs/audio_format.h" @@ -39,6 +38,7 @@ #include "api/field_trials.h" #include "api/make_ref_counted.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/priority.h" #include "api/ref_count.h" #include "api/rtc_error.h" @@ -74,12 +74,12 @@ #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/dscp.h" #include "rtc_base/numerics/safe_conversions.h" -#include "rtc_base/thread.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_audio_decoder_factory.h" #include "test/mock_audio_encoder_factory.h" +#include "test/run_loop.h" namespace webrtc { namespace { @@ -115,7 +115,6 @@ constexpr uint32_t kSsrc1 = 1; constexpr uint32_t kSsrcX = 0x99; constexpr uint32_t kSsrcY = 0x17; constexpr uint32_t kSsrcZ = 0x42; -constexpr uint32_t kSsrcW = 0x02; constexpr uint32_t kSsrcs4[] = {11, 200, 30, 44}; constexpr int kRtpHistoryMs = 5000; @@ -297,11 +296,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { AudioOptions(), CryptoOptions()); receive_channel_ = engine_->CreateReceiveChannel( env_, &call_, MediaConfig(), AudioOptions(), CryptoOptions()); - send_channel_->SetSsrcListChangedCallback( - [receive_channel = - receive_channel_.get()](const std::set& choices) { - receive_channel->ChooseReceiverReportSsrc(choices); - }); return true; } @@ -343,11 +337,11 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { EXPECT_FALSE(call_.GetAudioSendStream(kSsrcX)); } - void DeliverPacket(ArrayView data) { + void DeliverPacket(std::span data) { RtpPacketReceived packet; packet.Parse(data); receive_channel_->OnPacketReceived(packet); - Thread::Current()->ProcessMessages(0); + run_loop_.Flush(); } const FakeAudioSendStream& GetSendStream(uint32_t ssrc) { @@ -870,7 +864,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { void VerifyEchoCancellationSettings(bool enabled) { EXPECT_EQ(apm_config_.echo_canceller.enabled, enabled); - EXPECT_EQ(apm_config_.echo_canceller.mobile_mode, false); } bool IsHighPassFilterEnabled() { @@ -894,7 +887,7 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { } protected: - AutoThread main_thread_; + test::RunLoop run_loop_; const bool use_null_apm_; FieldTrials field_trials_; const Environment env_; @@ -911,31 +904,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { PayloadTypePicker pt_mapper_; }; -TEST_P(WebRtcVoiceEngineTestFake, OnRtpSendParametersChangedCallback) { - EXPECT_TRUE(SetupSendStream()); - - std::optional callback_ssrc; - RtpParameters callback_params; - int callback_count = 0; - send_channel_->SetOnRtpSendParametersChanged( - [&](std::optional ssrc, const RtpParameters& params) { - callback_ssrc = ssrc; - callback_params = params; - ++callback_count; - }); - - RtpParameters parameters = send_channel_->GetRtpSendParameters(kSsrcX); - EXPECT_EQ(callback_count, 0); - - // Change parameters. - parameters.encodings[0].max_bitrate_bps = 132000; - EXPECT_TRUE(send_channel_->SetRtpSendParameters(kSsrcX, parameters).ok()); - - EXPECT_EQ(callback_count, 1); - EXPECT_EQ(callback_ssrc, kSsrcX); // SetupSendStream adds kSsrcX. - EXPECT_EQ(callback_params.encodings[0].max_bitrate_bps, 132000); -} - INSTANTIATE_TEST_SUITE_P(TestBothWithAndWithoutNullApm, WebRtcVoiceEngineTestFake, ::testing::Values(false, true)); @@ -984,7 +952,6 @@ TEST_P(WebRtcVoiceEngineTestFake, CreateRecvStream) { const AudioReceiveStreamInterface::Config& config = GetRecvStreamConfig(kSsrcX); EXPECT_EQ(kSsrcX, config.rtp.remote_ssrc); - EXPECT_EQ(0xFA17FA17, config.rtp.local_ssrc); EXPECT_EQ(ReceiveImpl()->transport(), config.rtcp_send_transport); EXPECT_EQ("", config.sync_group); } @@ -1144,7 +1111,7 @@ TEST_P(WebRtcVoiceEngineTestFake, ChangeRecvCodecPayloadType) { parameters.codecs.push_back(kOpusCodec); EXPECT_TRUE(receive_channel_->SetReceiverParameters(parameters)); - ++parameters.codecs[0].id; + parameters.codecs[0].id = PayloadType(parameters.codecs[0].id + 1); EXPECT_TRUE(receive_channel_->SetReceiverParameters(parameters)); } @@ -1632,17 +1599,18 @@ TEST_P(WebRtcVoiceEngineTestFake, OnPacketReceivedIdentifiesExtensions) { parameters.extensions.push_back( RtpExtension(RtpExtension::kAudioLevelUri, /*id=*/1)); ASSERT_TRUE(receive_channel_->SetReceiverParameters(parameters)); + run_loop_.Flush(); RtpHeaderExtensionMap extension_map(parameters.extensions); RtpPacketReceived reference_packet(&extension_map); constexpr uint8_t kAudioLevel = 123; reference_packet.SetExtension( AudioLevel(/*voice_activity=*/true, kAudioLevel)); // Create a packet without the extension map but with the same content. - RtpPacketReceived received_packet; + RtpPacketReceived received_packet(&extension_map); ASSERT_TRUE(received_packet.Parse(reference_packet.Buffer())); receive_channel_->OnPacketReceived(received_packet); - Thread::Current()->ProcessMessages(0); + run_loop_.Flush(); AudioLevel audio_level; EXPECT_TRUE( @@ -2779,7 +2747,6 @@ TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrcWithMultipleStreams) { EXPECT_TRUE(SetupSendStream()); EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX)); EXPECT_TRUE(AddRecvStream(kSsrcY)); - EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc); } // Test that the local SSRC is the same on sending and receiving channels if the @@ -2789,7 +2756,6 @@ TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrcAfterCreatingReceiveChannel) { EXPECT_TRUE(AddRecvStream(kSsrcY)); EXPECT_TRUE(send_channel_->AddSendStream(StreamParams::CreateLegacy(kSsrcX))); EXPECT_TRUE(call_.GetAudioSendStream(kSsrcX)); - EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc); } // Test that we can properly receive packets. @@ -2814,7 +2780,7 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvWithMultipleStreams) { uint8_t packets[4][sizeof(kPcmuFrame)]; for (size_t i = 0; i < std::size(packets); ++i) { memcpy(packets[i], kPcmuFrame, sizeof(kPcmuFrame)); - SetBE32(packets[i] + 8, static_cast(i)); + SetBE32(std::span(packets[i] + 8, 4), static_cast(i)); } const FakeAudioReceiveStream& s1 = GetRecvStream(ssrc1); @@ -2903,9 +2869,9 @@ TEST_P(WebRtcVoiceEngineTestFake, // Deliver a couple packets with unsignaled SSRCs. uint8_t packet[sizeof(kPcmuFrame)]; memcpy(packet, kPcmuFrame, sizeof(kPcmuFrame)); - SetBE32(&packet[8], 0x1234); + SetBE32(std::span(&packet[8], 4), 0x1234); DeliverPacket(packet); - SetBE32(&packet[8], 0x5678); + SetBE32(std::span(&packet[8], 4), 0x5678); DeliverPacket(packet); // Verify that the receive streams were created. @@ -2927,7 +2893,7 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvMultipleUnsignaled) { // Note that SSRC = 0 is not supported. for (uint32_t ssrc = 1; ssrc < (1 + kMaxUnsignaledRecvStreams); ++ssrc) { - SetBE32(&packet[8], ssrc); + SetBE32(std::span(&packet[8], 4), ssrc); DeliverPacket(packet); // Verify we have one new stream for each loop iteration. @@ -2938,7 +2904,7 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvMultipleUnsignaled) { // Sending on the same SSRCs again should not create new streams. for (uint32_t ssrc = 1; ssrc < (1 + kMaxUnsignaledRecvStreams); ++ssrc) { - SetBE32(&packet[8], ssrc); + SetBE32(std::span(&packet[8], 4), ssrc); DeliverPacket(packet); EXPECT_EQ(kMaxUnsignaledRecvStreams, call_.GetAudioReceiveStreams().size()); @@ -2948,7 +2914,7 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvMultipleUnsignaled) { // Send on another SSRC, the oldest unsignaled stream (SSRC=1) is replaced. constexpr uint32_t kAnotherSsrc = 667; - SetBE32(&packet[8], kAnotherSsrc); + SetBE32(std::span(&packet[8], 4), kAnotherSsrc); DeliverPacket(packet); const auto& streams = call_.GetAudioReceiveStreams(); @@ -2973,7 +2939,7 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvUnsignaledAfterSignaled) { // Add a known stream, send packet and verify we got it. const uint32_t signaled_ssrc = 1; - SetBE32(&packet[8], signaled_ssrc); + SetBE32(std::span(&packet[8], 4), signaled_ssrc); EXPECT_TRUE(AddRecvStream(signaled_ssrc)); DeliverPacket(packet); EXPECT_TRUE(GetRecvStream(signaled_ssrc).VerifyLastPacket(packet)); @@ -2982,7 +2948,7 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvUnsignaledAfterSignaled) { // Note that the first unknown SSRC cannot be 0, because we only support // creating receive streams for SSRC!=0. const uint32_t unsignaled_ssrc = 7011; - SetBE32(&packet[8], unsignaled_ssrc); + SetBE32(std::span(&packet[8], 4), unsignaled_ssrc); DeliverPacket(packet); EXPECT_TRUE(GetRecvStream(unsignaled_ssrc).VerifyLastPacket(packet)); EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size()); @@ -2990,7 +2956,7 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvUnsignaledAfterSignaled) { DeliverPacket(packet); EXPECT_EQ(2, GetRecvStream(unsignaled_ssrc).received_packets()); - SetBE32(&packet[8], signaled_ssrc); + SetBE32(std::span(&packet[8], 4), signaled_ssrc); DeliverPacket(packet); EXPECT_EQ(2, GetRecvStream(signaled_ssrc).received_packets()); EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size()); @@ -3455,7 +3421,7 @@ TEST_P(WebRtcVoiceEngineTestFake, SetOutputVolumeUnsignaledRecvStream) { // Spawn an unsignaled stream by sending a packet - gain should be 2. uint8_t pcmuFrame2[sizeof(kPcmuFrame)]; memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame)); - SetBE32(&pcmuFrame2[8], kSsrcX); + SetBE32(std::span(&pcmuFrame2[8], 4), kSsrcX); DeliverPacket(pcmuFrame2); EXPECT_DOUBLE_EQ(2, GetRecvStream(kSsrcX).gain()); @@ -3517,7 +3483,7 @@ TEST_P(WebRtcVoiceEngineTestFake, // Spawn an unsignaled stream by sending a packet - delay should be 100. uint8_t pcmuFrame2[sizeof(kPcmuFrame)]; memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame)); - SetBE32(&pcmuFrame2[8], kSsrcX); + SetBE32(std::span(&pcmuFrame2[8], 4), kSsrcX); DeliverPacket(pcmuFrame2); EXPECT_EQ( 100, receive_channel_->GetBaseMinimumPlayoutDelayMs(kSsrcX).value_or(-1)); @@ -3638,36 +3604,11 @@ TEST_P(WebRtcVoiceEngineTestFake, DeliverAudioPacket_Call) { RtpPacketReceived parsed_packet; RTC_CHECK(parsed_packet.Parse(kPcmuPacket)); receive_channel_->OnPacketReceived(parsed_packet); - Thread::Current()->ProcessMessages(0); + run_loop_.Flush(); EXPECT_EQ(1, s->received_packets()); } -// All receive channels should be associated with the first send channel, -// since they do not send RTCP SR. -TEST_P(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel_SendCreatedFirst) { - EXPECT_TRUE(SetupSendStream()); - EXPECT_TRUE(AddRecvStream(kSsrcY)); - EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc); - EXPECT_TRUE(send_channel_->AddSendStream(StreamParams::CreateLegacy(kSsrcZ))); - EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcY).rtp.local_ssrc); - EXPECT_TRUE(AddRecvStream(kSsrcW)); - EXPECT_EQ(kSsrcX, GetRecvStreamConfig(kSsrcW).rtp.local_ssrc); -} - -TEST_P(WebRtcVoiceEngineTestFake, AssociateFirstSendChannel_RecvCreatedFirst) { - EXPECT_TRUE(SetupRecvStream()); - EXPECT_EQ(0xFA17FA17u, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc); - EXPECT_TRUE(send_channel_->AddSendStream(StreamParams::CreateLegacy(kSsrcY))); - EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc); - EXPECT_TRUE(AddRecvStream(kSsrcZ)); - EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcZ).rtp.local_ssrc); - EXPECT_TRUE(send_channel_->AddSendStream(StreamParams::CreateLegacy(kSsrcW))); - - EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcX).rtp.local_ssrc); - EXPECT_EQ(kSsrcY, GetRecvStreamConfig(kSsrcZ).rtp.local_ssrc); -} - TEST_P(WebRtcVoiceEngineTestFake, SetRawAudioSink) { EXPECT_TRUE(SetupChannel()); std::unique_ptr fake_sink_1(new FakeAudioSink()); @@ -3719,7 +3660,7 @@ TEST_P(WebRtcVoiceEngineTestFake, SetRawAudioSinkUnsignaledRecvStream) { // and the previous unsignaled stream should lose it. uint8_t pcmuFrame2[sizeof(kPcmuFrame)]; memcpy(pcmuFrame2, kPcmuFrame, sizeof(kPcmuFrame)); - SetBE32(&pcmuFrame2[8], kSsrcX); + SetBE32(std::span(&pcmuFrame2[8], 4), kSsrcX); DeliverPacket(pcmuFrame2); if (kMaxUnsignaledRecvStreams > 1) { EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink()); @@ -3768,6 +3709,37 @@ TEST_P(WebRtcVoiceEngineTestFake, OnReadyToSendSignalsNetworkState) { EXPECT_EQ(kNetworkUp, call_.GetNetworkState(MediaType::VIDEO)); } +// Test that when an unsignaled stream is promoted to a signaled stream, +// its ProxySink doesn't hold a dangling raw pointer if the default sink +// is subsequently destroyed. +TEST_P(WebRtcVoiceEngineTestFake, + ProxySinkSurvivesUnsignaledToSignaledPromotion) { + EXPECT_TRUE(SetupChannel()); + std::unique_ptr fake_sink(new FakeAudioSink()); + + // Set the default sink. + receive_channel_->SetDefaultRawAudioSink(std::move(fake_sink)); + + // Deliver an RTP packet to create an unsignaled stream. + DeliverPacket(kPcmuFrame); + const AudioSinkInterface* proxy_sink = GetRecvStream(kSsrc1).sink(); + EXPECT_NE(nullptr, proxy_sink); + + // Promote the unsignaled stream to a signaled stream. + StreamParams sp = StreamParams::CreateLegacy(kSsrc1); + EXPECT_TRUE(receive_channel_->AddRecvStream(sp)); + + // The proxy sink should be removed from the stream upon promotion. + EXPECT_EQ(nullptr, GetRecvStream(kSsrc1).sink()); + + // Destroy the original sink by passing nullptr. + receive_channel_->SetDefaultRawAudioSink(nullptr); + + // Note: calling proxy_sink->OnData would crash here if the proxy_sink + // would still be attached to the stream and hold a dangling pointer to + // default_sink_. But we've verified that it's detached, so that won't happen. +} + // Test that playout is still started after changing parameters TEST_P(WebRtcVoiceEngineTestFake, PreservePlayoutWhenRecreateRecvStream) { SetupRecvStream(); @@ -3797,7 +3769,7 @@ TEST_P(WebRtcVoiceEngineTestFake, GetSourcesWithNonExistingSsrc) { // Tests that the library initializes and shuts down properly. TEST(WebRtcVoiceEngineTest, StartupShutdown) { - AutoThread main_thread; + test::RunLoop run_loop; for (bool use_null_apm : {false, true}) { // If the VoiceEngine wants to gather available codecs early, that's fine // but we never want it to create a decoder at this stage. @@ -3824,7 +3796,7 @@ TEST(WebRtcVoiceEngineTest, StartupShutdown) { // Tests that reference counting on the external ADM is correct. TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) { - AutoThread main_thread; + test::RunLoop run_loop; for (bool use_null_apm : {false, true}) { Environment env = CreateEnvironment(); auto adm = @@ -3901,7 +3873,7 @@ TEST(WebRtcVoiceEngineTest, HasCorrectPayloadTypeMapping) { // Tests that VoE supports at least 32 channels TEST(WebRtcVoiceEngineTest, Has32Channels) { - AutoThread main_thread; + test::RunLoop run_loop; for (bool use_null_apm : {false, true}) { Environment env = CreateEnvironment(); scoped_refptr adm = @@ -3930,7 +3902,7 @@ TEST(WebRtcVoiceEngineTest, Has32Channels) { // Test that we set our preferred codecs properly. TEST(WebRtcVoiceEngineTest, SetRecvCodecs) { - AutoThread main_thread; + test::RunLoop run_loop; for (bool use_null_apm : {false, true}) { Environment env = CreateEnvironment(); // TODO(ossu): I'm not sure of the intent of this test. It's either: @@ -3959,7 +3931,7 @@ TEST(WebRtcVoiceEngineTest, SetRecvCodecs) { } TEST(WebRtcVoiceEngineTest, SetRtpSendParametersMaxBitrate) { - AutoThread main_thread; + test::RunLoop run_loop; Environment env = CreateEnvironment(); scoped_refptr adm = test::MockAudioDeviceModule::CreateNice(); diff --git a/media/sctp/dcsctp_transport.cc b/media/sctp/dcsctp_transport.cc index 63f3d52eebe..03ffc69ae86 100644 --- a/media/sctp/dcsctp_transport.cc +++ b/media/sctp/dcsctp_transport.cc @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/data_channel_interface.h" #include "api/dtls_transport_interface.h" #include "api/environment/environment.h" @@ -440,7 +440,7 @@ void DcSctpTransport::SetBufferedAmountLowThreshold(int sid, size_t bytes) { } SendPacketStatus DcSctpTransport::SendPacketWithStatus( - ArrayView data) { + std::span data) { RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK(socket_); @@ -591,7 +591,7 @@ void DcSctpTransport::OnConnectionRestarted() { } void DcSctpTransport::OnStreamsResetFailed( - ArrayView outgoing_streams, + std::span outgoing_streams, absl::string_view reason) { // TODO(orphis): Need a test to check for correct behavior for (auto& stream_id : outgoing_streams) { @@ -603,7 +603,7 @@ void DcSctpTransport::OnStreamsResetFailed( } void DcSctpTransport::OnStreamsResetPerformed( - ArrayView outgoing_streams) { + std::span outgoing_streams) { RTC_DCHECK_RUN_ON(network_thread_); for (auto& stream_id : outgoing_streams) { RTC_LOG(LS_INFO) << debug_name_ @@ -631,7 +631,7 @@ void DcSctpTransport::OnStreamsResetPerformed( } void DcSctpTransport::OnIncomingStreamsReset( - ArrayView incoming_streams) { + std::span incoming_streams) { RTC_DCHECK_RUN_ON(network_thread_); for (auto& stream_id : incoming_streams) { RTC_LOG(LS_INFO) << debug_name_ diff --git a/media/sctp/dcsctp_transport.h b/media/sctp/dcsctp_transport.h index 143d4b3c263..bf945c94621 100644 --- a/media/sctp/dcsctp_transport.h +++ b/media/sctp/dcsctp_transport.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/dtls_transport_interface.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" @@ -83,7 +83,7 @@ class DcSctpTransport : public SctpTransportInternal, private: // dcsctp::DcSctpSocketCallbacks dcsctp::SendPacketStatus SendPacketWithStatus( - ArrayView data) override; + std::span data) override; std::unique_ptr CreateTimeout( TaskQueueBase::DelayPrecision precision) override; dcsctp::TimeMs TimeMillis() override; @@ -96,12 +96,12 @@ class DcSctpTransport : public SctpTransportInternal, void OnConnected() override; void OnClosed() override; void OnConnectionRestarted() override; - void OnStreamsResetFailed(ArrayView outgoing_streams, + void OnStreamsResetFailed(std::span outgoing_streams, absl::string_view reason) override; void OnStreamsResetPerformed( - ArrayView outgoing_streams) override; + std::span outgoing_streams) override; void OnIncomingStreamsReset( - ArrayView incoming_streams) override; + std::span incoming_streams) override; // Transport callbacks void ConnectTransportSignals(); diff --git a/media/sctp/dcsctp_transport_unittest.cc b/media/sctp/dcsctp_transport_unittest.cc index dffbc7a011a..bfe797cb919 100644 --- a/media/sctp/dcsctp_transport_unittest.cc +++ b/media/sctp/dcsctp_transport_unittest.cc @@ -31,6 +31,7 @@ #include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" using ::testing::_; using ::testing::ByMove; @@ -100,7 +101,7 @@ class Peer { } // namespace TEST(DcSctpTransportTest, OpenSequence) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; peer_a.fake_dtls_transport_.SetWritable(true); @@ -118,7 +119,7 @@ TEST(DcSctpTransportTest, OpenSequence) { // Tests that the close sequence invoked from one end results in the stream to // be reset from both ends and all the proper signals are sent. TEST(DcSctpTransportTest, CloseSequence) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; Peer peer_b; peer_a.fake_dtls_transport_.SetDestination(&peer_b.fake_dtls_transport_, @@ -168,7 +169,7 @@ TEST(DcSctpTransportTest, CloseSequence) { // terminates properly. Both peers will think they initiated it, so no // OnClosingProcedureStartedRemotely should be called. TEST(DcSctpTransportTest, CloseSequenceSimultaneous) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; Peer peer_b; peer_a.fake_dtls_transport_.SetDestination(&peer_b.fake_dtls_transport_, @@ -212,7 +213,7 @@ TEST(DcSctpTransportTest, CloseSequenceSimultaneous) { } TEST(DcSctpTransportTest, SetStreamPriority) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; { @@ -236,7 +237,7 @@ TEST(DcSctpTransportTest, SetStreamPriority) { } TEST(DcSctpTransportTest, DiscardMessageClosedChannel) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); @@ -252,7 +253,7 @@ TEST(DcSctpTransportTest, DiscardMessageClosedChannel) { } TEST(DcSctpTransportTest, DiscardMessageClosingChannel) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); @@ -270,7 +271,7 @@ TEST(DcSctpTransportTest, DiscardMessageClosingChannel) { } TEST(DcSctpTransportTest, SendDataOpenChannel) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; dcsctp::DcSctpOptions options; @@ -288,7 +289,7 @@ TEST(DcSctpTransportTest, SendDataOpenChannel) { } TEST(DcSctpTransportTest, DeliversMessage) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; EXPECT_CALL(peer_a.sink_, OnDataReceived(1, DataMessageType::kBinary, _)) @@ -305,7 +306,7 @@ TEST(DcSctpTransportTest, DeliversMessage) { } TEST(DcSctpTransportTest, DropMessageWithUnknownPpid) { - AutoThread main_thread; + test::RunLoop main_thread; Peer peer_a; EXPECT_CALL(peer_a.sink_, OnDataReceived(_, _, _)).Times(0); diff --git a/modules/BUILD.gn b/modules/BUILD.gn index 57a6f1881d9..0cad3291aad 100644 --- a/modules/BUILD.gn +++ b/modules/BUILD.gn @@ -95,7 +95,6 @@ if (rtc_include_tests && !build_with_chromium) { "remote_bitrate_estimator:remote_bitrate_estimator_unittests", "rtp_rtcp:rtp_rtcp_unittests", "video_coding:video_coding_unittests", - "video_coding/deprecated:deprecated_unittests", "video_coding/timing:timing_unittests", ] diff --git a/modules/async_audio_processing/async_audio_processing.cc b/modules/async_audio_processing/async_audio_processing.cc index eb62853109f..ce7d87790ec 100644 --- a/modules/async_audio_processing/async_audio_processing.cc +++ b/modules/async_audio_processing/async_audio_processing.cc @@ -62,7 +62,7 @@ AsyncAudioProcessing::AsyncAudioProcessing( frame_processor_(frame_processor), task_queue_(task_queue_factory.CreateTaskQueue( "AsyncAudioProcessing", - TaskQueueFactory::Priority::NORMAL)) { + TaskQueueFactory::Priority::kNormal)) { frame_processor_.SetSink([this](std::unique_ptr frame) { task_queue_->PostTask([this, frame = std::move(frame)]() mutable { on_frame_processed_callback_(std::move(frame)); @@ -79,7 +79,7 @@ AsyncAudioProcessing::AsyncAudioProcessing( owned_frame_processor_(std::move(frame_processor)), task_queue_(task_queue_factory.CreateTaskQueue( "AsyncAudioProcessing", - TaskQueueFactory::Priority::NORMAL)) { + TaskQueueFactory::Priority::kNormal)) { owned_frame_processor_->SetSink([this](std::unique_ptr frame) { task_queue_->PostTask([this, frame = std::move(frame)]() mutable { on_frame_processed_callback_(std::move(frame)); diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn index 1f94c303553..25748a0a168 100644 --- a/modules/audio_coding/BUILD.gn +++ b/modules/audio_coding/BUILD.gn @@ -36,27 +36,20 @@ rtc_library("audio_coding") { deps = [ ":audio_coding_module_typedefs", - ":neteq", - "..:module_api", "..:module_api_public", - "../../api:array_view", "../../api:function_view", "../../api/audio:audio_frame_api", "../../api/audio_codecs:audio_codecs_api", - "../../api/environment", - "../../api/neteq:default_neteq_factory", - "../../api/neteq:neteq_api", - "../../api/units:timestamp", + "../../audio/utility:audio_frame_operations", "../../common_audio", - "../../common_audio:common_audio_c", "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:macromagic", "../../rtc_base:safe_conversions", "../../rtc_base/synchronization:mutex", - "../../system_wrappers", "../../system_wrappers:metrics", + "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -68,7 +61,6 @@ rtc_library("legacy_encoded_audio_frame") { "codecs/legacy_encoded_audio_frame.h", ] deps = [ - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../rtc_base:buffer", "../../rtc_base:checks", @@ -83,7 +75,6 @@ rtc_library("webrtc_cng") { ] deps = [ - "../../api:array_view", "../../common_audio:common_audio_c", "../../rtc_base:buffer", "../../rtc_base:checks", @@ -100,7 +91,6 @@ rtc_library("audio_encoder_cng") { deps = [ ":webrtc_cng", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../api/units:time_delta", "../../common_audio", @@ -117,16 +107,13 @@ rtc_library("red") { ] deps = [ - "../../api:array_view", "../../api:bitrate_allocation", "../../api:field_trials_view", "../../api/audio_codecs:audio_codecs_api", "../../api/units:time_delta", - "../../common_audio", "../../rtc_base:buffer", "../../rtc_base:byte_order", "../../rtc_base:checks", - "../../rtc_base:logging", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -143,7 +130,6 @@ rtc_library("g711") { deps = [ ":legacy_encoded_audio_frame", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../api/units:time_delta", "../../rtc_base:buffer", @@ -173,13 +159,11 @@ rtc_library("g722") { deps = [ ":legacy_encoded_audio_frame", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs/g722:audio_encoder_g722_config", "../../api/units:time_delta", "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:safe_conversions", ] public_deps += [ ":g722_c" ] # no-presubmit-check TODO(webrtc:8603) } @@ -211,7 +195,6 @@ rtc_library("isac_vad") { deps = [ ":isac_bwinfo", "../../rtc_base:compile_assert_c", - "../../rtc_base/system:arch", "../../rtc_base/system:ignore_warnings", "../third_party/fft", ] @@ -237,7 +220,6 @@ rtc_library("pcm16b") { deps = [ ":g711", ":legacy_encoded_audio_frame", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../rtc_base:buffer", "../../rtc_base:checks", @@ -260,10 +242,8 @@ rtc_library("audio_coding_opus_common") { ] deps = [ - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../rtc_base:buffer", - "../../rtc_base:checks", "../../rtc_base:stringutils", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -282,7 +262,6 @@ rtc_library("webrtc_opus") { deps = [ ":audio_coding_opus_common", ":audio_network_adaptor", - "../../api:array_view", "../../api:bitrate_allocation", "../../api:field_trials_view", "../../api/audio_codecs:audio_codecs_api", @@ -322,7 +301,6 @@ rtc_library("webrtc_multiopus") { deps = [ ":audio_coding_opus_common", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs/opus:audio_decoder_opus_config", "../../api/audio_codecs/opus:audio_encoder_opus_config", @@ -331,7 +309,6 @@ rtc_library("webrtc_multiopus") { "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:safe_conversions", - "../../rtc_base:safe_minmax", "../../rtc_base:stringutils", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings", @@ -353,7 +330,6 @@ rtc_library("webrtc_opus_wrapper") { defines = audio_coding_defines deps = [ - "../../api:array_view", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", ] @@ -421,7 +397,6 @@ rtc_library("audio_network_adaptor") { [ ":audio_network_adaptor_config" ] deps = [ - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../api/environment", "../../api/rtc_event_log", @@ -515,10 +490,8 @@ rtc_library("neteq") { ] deps = [ - ":audio_coding_module_typedefs", ":webrtc_cng", "..:module_api_public", - "../../api:array_view", "../../api:field_trials_view", "../../api:rtp_headers", "../../api:rtp_packet_info", @@ -532,21 +505,20 @@ rtc_library("neteq") { "../../api/neteq:tick_timer", "../../api/units:time_delta", "../../api/units:timestamp", - "../../common_audio", + + # TODO: bugs.webrtc.org/496700738 - fix dependency. + "../../common_audio", # keep "../../common_audio:common_audio_c", "../../common_audio:common_audio_cc", "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:event_tracer", - "../../rtc_base:gtest_prod", "../../rtc_base:logging", - "../../rtc_base:macromagic", "../../rtc_base:rtc_numerics", "../../rtc_base:safe_conversions", "../../rtc_base:safe_minmax", "../../rtc_base:sanitizer", "../../rtc_base/experiments:field_trial_parser", - "../../rtc_base/synchronization:mutex", "../../system_wrappers", "../../system_wrappers:metrics", "//third_party/abseil-cpp/absl/strings", @@ -574,9 +546,7 @@ rtc_library("neteq_tools_minimal") { ] deps = [ - ":neteq", - "../../api:array_view", - "../../api:field_trials", + "../../api:field_trials_view", "../../api:neteq_simulator_api", "../../api:rtp_headers", "../../api:scoped_refptr", @@ -584,14 +554,11 @@ rtc_library("neteq_tools_minimal") { "../../api/audio_codecs:audio_codecs_api", "../../api/environment", "../../api/environment:environment_factory", - "../../api/neteq:custom_neteq_factory", - "../../api/neteq:default_neteq_controller_factory", "../../api/neteq:default_neteq_factory", "../../api/neteq:neteq_api", "../../api/units:timestamp", "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:copy_on_write_buffer", "../../rtc_base:safe_conversions", "../../rtc_base:stringutils", "../../system_wrappers", @@ -626,7 +593,6 @@ rtc_library("neteq_test_tools") { ":neteq_tools", ":neteq_tools_minimal", ":pcm16b", - "../../api:array_view", "../../api:rtp_headers", "../../api/units:timestamp", "../../common_audio", @@ -660,8 +626,6 @@ rtc_library("neteq_tools") { deps = [ ":neteq_input_audio_tools", ":neteq_tools_minimal", - "..:module_api_public", - "../../api:array_view", "../../api:rtp_headers", "../../api:rtp_packet_info", "../../api/audio:audio_frame_api", @@ -675,7 +639,6 @@ rtc_library("neteq_tools") { "../../rtc_base:safe_conversions", "../../rtc_base:stringutils", "../../rtc_base:timeutils", - "../rtp_rtcp", "../rtp_rtcp:rtp_rtcp_format", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -744,7 +707,6 @@ rtc_library("audio_coding_modules_tests_shared") { ":neteq_tools_minimal", ":webrtc_opus_wrapper", "..:module_api", - "../../api:array_view", "../../api:rtp_headers", "../../api:rtp_parameters", "../../api/audio:audio_frame_api", @@ -864,8 +826,6 @@ if (rtc_include_tests) { ":audio_encoder_cng", ":pcm16b_c", ":red", - "../../api:array_view", - "../../api:field_trials", "../../api:rtp_headers", "../../api:scoped_refptr", "../../api/audio:audio_frame_api", @@ -920,8 +880,6 @@ if (rtc_include_tests) { "../../api/test/metrics:global_metrics_logger_and_exporter", "../../api/test/metrics:metric", "../../rtc_base:buffer", - "../../rtc_base:timeutils", - "../../system_wrappers", "../../test:fileutils", "../../test:test_flags", "../../test:test_support", @@ -940,9 +898,7 @@ if (rtc_include_tests) { deps = [ ":audio_coding", - ":neteq_tools", ":neteq_tools_minimal", - "../../api:array_view", "../../api:scoped_refptr", "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs:builtin_audio_decoder_factory", @@ -970,18 +926,13 @@ if (rtc_include_tests) { ":audio_coding", ":audio_coding_module_typedefs", ":neteq_input_audio_tools", - ":neteq_tools", ":neteq_tools_minimal", "../../api/audio:audio_frame_api", "../../api/audio_codecs:audio_codecs_api", - "../../api/audio_codecs:builtin_audio_decoder_factory", "../../api/audio_codecs:builtin_audio_encoder_factory", "../../api/environment", "../../api/environment:environment_factory", "../../rtc_base:checks", - "../../rtc_base:copy_on_write_buffer", - "../../rtc_base:stringutils", - "../../test:test_support", "../rtp_rtcp:rtp_rtcp_format", "//testing/gtest", "//third_party/abseil-cpp/absl/strings", @@ -1010,7 +961,6 @@ if (rtc_include_tests) { deps = [ ":neteq_input_audio_tools", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs/g722:audio_encoder_g722_config", "../../api/audio_codecs/opus:audio_encoder_opus", @@ -1042,13 +992,12 @@ if (rtc_include_tests) { visibility += webrtc_default_visibility defines = audio_codec_defines deps = [ - ":neteq", ":neteq_input_audio_tools", ":neteq_test_tools", ":neteq_tools", ":neteq_tools_minimal", + "../../api:field_trials", "../../api:make_ref_counted", - "../../api:rtp_headers", "../../api:scoped_refptr", "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs:builtin_audio_decoder_factory", @@ -1056,10 +1005,7 @@ if (rtc_include_tests) { "../../api/neteq:neteq_api", "../../logging:rtc_event_log_parser", "../../rtc_base:checks", - "../../rtc_base:refcount", "../../test:audio_test_common", - "../../test:fileutils", - "../../test:test_support", "../rtp_rtcp:rtp_rtcp_format", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -1161,7 +1107,6 @@ if (rtc_include_tests) { "../../rtc_base:checks", "../../system_wrappers", "../../test:fileutils", - "../../test:test_support", "//testing/gtest", ] } @@ -1175,11 +1120,9 @@ if (rtc_include_tests) { ] deps = [ - ":neteq", ":neteq_input_audio_tools", ":neteq_test_tools", ":neteq_tools_minimal", - "../../api:array_view", "../../api:rtp_headers", "../../api:scoped_refptr", "../../api/audio_codecs:audio_codecs_api", @@ -1191,7 +1134,6 @@ if (rtc_include_tests) { "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:stringutils", - "../../system_wrappers", "../../test:fileutils", "../../test:test_support", "//testing/gtest", @@ -1206,7 +1148,6 @@ if (rtc_include_tests) { deps = [ ":audio_coding", ":audio_coding_module_typedefs", - ":audio_encoder_cng", ":neteq_input_audio_tools", "../../api/audio:audio_frame_api", "../../api/audio_codecs/L16:audio_encoder_L16", @@ -1232,7 +1173,6 @@ if (rtc_include_tests) { testonly = true deps = [ - "../../api:array_view", "../../rtc_base:buffer", "../../rtc_base:checks", "../rtp_rtcp:rtp_rtcp_format", @@ -1263,10 +1203,7 @@ if (rtc_include_tests) { sources = [ "neteq/tools/rtp_analyze.cc" ] deps = [ - ":neteq", ":neteq_test_tools", - ":neteq_tools_minimal", - ":pcm16b", "../../api:rtp_headers", "../../rtc_base:checks", "../rtp_rtcp:rtp_rtcp_format", @@ -1276,7 +1213,7 @@ if (rtc_include_tests) { ] } - rtc_executable("neteq_opus_quality_test") { + rtc_test("neteq_opus_quality_test") { testonly = true sources = [ "neteq/test/neteq_opus_quality_test.cc" ] @@ -1286,12 +1223,10 @@ if (rtc_include_tests) { ":neteq_quality_test_support", ":neteq_tools", ":webrtc_opus", - "../../api:array_view", "../../api:rtp_parameters", "../../api/audio_codecs:audio_codecs_api", "../../rtc_base:buffer", "../../rtc_base:checks", - "../../test:test_main", "../../test:test_support", "//testing/gtest", "//third_party/abseil-cpp/absl/flags:flag", @@ -1305,53 +1240,43 @@ if (rtc_include_tests) { sources = [ "neteq/test/neteq_speed_test.cc" ] deps = [ - ":neteq", ":neteq_test_support", "../../rtc_base:checks", - "../../test:test_support", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] } - rtc_executable("neteq_pcmu_quality_test") { + rtc_test("neteq_pcmu_quality_test") { testonly = true sources = [ "neteq/test/neteq_pcmu_quality_test.cc" ] deps = [ ":g711", - ":neteq", ":neteq_quality_test_support", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:safe_conversions", - "../../test:fileutils", - "../../test:test_main", "../../test:test_support", "//testing/gtest", "//third_party/abseil-cpp/absl/flags:flag", ] } - rtc_executable("neteq_pcm16b_quality_test") { + rtc_test("neteq_pcm16b_quality_test") { testonly = true sources = [ "neteq/test/neteq_pcm16b_quality_test.cc" ] deps = [ - ":neteq", ":neteq_quality_test_support", ":pcm16b", - "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:safe_conversions", - "../../test:fileutils", - "../../test:test_main", "../../test:test_support", "//testing/gtest", "//third_party/abseil-cpp/absl/flags:flag", @@ -1376,7 +1301,7 @@ if (rtc_include_tests) { } if (!build_with_chromium) { - rtc_executable("webrtc_opus_fec_test") { + rtc_test("webrtc_opus_fec_test") { testonly = true sources = [ "codecs/opus/opus_fec_test.cc" ] @@ -1385,7 +1310,6 @@ if (rtc_include_tests) { ":webrtc_opus", "../../common_audio", "../../test:fileutils", - "../../test:test_main", "../../test:test_support", "//testing/gtest", ] @@ -1397,6 +1321,7 @@ if (rtc_include_tests) { sources = [ "acm2/acm_remixing_unittest.cc", + "acm2/acm_resampler_unittest.cc", "acm2/audio_coding_module_unittest.cc", "acm2/call_statistics_unittest.cc", "audio_network_adaptor/audio_network_adaptor_impl_unittest.cc", @@ -1491,7 +1416,6 @@ if (rtc_include_tests) { ":webrtc_opus", ":webrtc_opus_wrapper", "..:module_api_public", - "../../api:array_view", "../../api:bitrate_allocation", "../../api:field_trials", "../../api:field_trials_view", @@ -1527,18 +1451,12 @@ if (rtc_include_tests) { "../../logging:rtc_event_audio", "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:copy_on_write_buffer", "../../rtc_base:digest", "../../rtc_base:ip_address", - "../../rtc_base:macromagic", - "../../rtc_base:platform_thread", "../../rtc_base:random", "../../rtc_base:rtc_base_tests_utils", - "../../rtc_base:rtc_event", "../../rtc_base:safe_conversions", "../../rtc_base:stringutils", - "../../rtc_base:threading", - "../../rtc_base/synchronization:mutex", "../../rtc_base/system:arch", "../../system_wrappers", "../../test:audio_codec_mocks", diff --git a/modules/audio_coding/DEPS b/modules/audio_coding/DEPS index 3dc9624a4b1..be1291bf2cc 100644 --- a/modules/audio_coding/DEPS +++ b/modules/audio_coding/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+audio/utility", "+call", "+common_audio", "+logging/rtc_event_log", diff --git a/modules/audio_coding/acm2/acm_remixing.cc b/modules/audio_coding/acm2/acm_remixing.cc index 3ad995542b5..812929f06c1 100644 --- a/modules/audio_coding/acm2/acm_remixing.cc +++ b/modules/audio_coding/acm2/acm_remixing.cc @@ -13,16 +13,16 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { -void DownMixFrame(const AudioFrame& input, ArrayView output) { +void DownMixFrame(const AudioFrame& input, std::span output) { RTC_DCHECK_EQ(input.num_channels_, 2); RTC_DCHECK_EQ(output.size(), input.samples_per_channel_); diff --git a/modules/audio_coding/acm2/acm_remixing.h b/modules/audio_coding/acm2/acm_remixing.h index 0ccb7c0bf4b..fc2d0824056 100644 --- a/modules/audio_coding/acm2/acm_remixing.h +++ b/modules/audio_coding/acm2/acm_remixing.h @@ -13,16 +13,16 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" namespace webrtc { // Stereo-to-mono downmixing. The length of the output must equal to the number // of samples per channel in the input. -void DownMixFrame(const AudioFrame& input, ArrayView output); +void DownMixFrame(const AudioFrame& input, std::span output); // Remixes the interleaved input frame to an interleaved output data vector. The // remixed data replaces the data in the output vector which is resized if diff --git a/modules/audio_coding/acm2/acm_resampler.cc b/modules/audio_coding/acm2/acm_resampler.cc index 9daa04d14b8..ddefdbe8f14 100644 --- a/modules/audio_coding/acm2/acm_resampler.cc +++ b/modules/audio_coding/acm2/acm_resampler.cc @@ -11,17 +11,22 @@ #include "modules/audio_coding/acm2/acm_resampler.h" #include +#include #include +#include "absl/algorithm/container.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_view.h" +#include "api/audio/channel_layout.h" +#include "audio/utility/audio_frame_operations.h" #include "rtc_base/checks.h" +#include "rtc_base/logging.h" namespace webrtc { namespace acm2 { ResamplerHelper::ResamplerHelper() { - ClearSamples(last_audio_buffer_); + absl::c_fill(last_audio_buffer_, 0); } bool ResamplerHelper::MaybeResample(int desired_sample_rate_hz, @@ -37,6 +42,21 @@ bool ResamplerHelper::MaybeResample(int desired_sample_rate_hz, (desired_sample_rate_hz != -1) && (current_sample_rate_hz != desired_sample_rate_hz); + if (need_resampling) { + const size_t target_size = + audio_frame->num_channels_ * + SampleRateToDefaultChannelSize(desired_sample_rate_hz); + if (target_size > AudioFrame::kMaxDataSizeSamples) { + RTC_LOG(LS_ERROR) << "AudioFrame cannot hold resampled data."; + AudioFrameOperations::Mute(audio_frame); + audio_frame->SetSampleRateAndChannelSize(desired_sample_rate_hz); + audio_frame->SetLayoutAndNumChannels( + CHANNEL_LAYOUT_UNSUPPORTED, + AudioFrame::kMaxDataSizeSamples / audio_frame->samples_per_channel()); + return false; + } + } + if (need_resampling && !resampled_last_output_frame_) { // Prime the resampler with the last frame. InterleavedView src(last_audio_buffer_.data(), diff --git a/modules/audio_coding/acm2/acm_resampler_unittest.cc b/modules/audio_coding/acm2/acm_resampler_unittest.cc new file mode 100644 index 00000000000..49713a97d48 --- /dev/null +++ b/modules/audio_coding/acm2/acm_resampler_unittest.cc @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_coding/acm2/acm_resampler.h" + +#include +#include +#include + +#include "api/audio/audio_frame.h" +#include "test/gtest.h" + +namespace webrtc { +namespace acm2 { + +TEST(ResamplerHelperTest, MaybeResampleCheckForMaxSize) { + ResamplerHelper resampler; + AudioFrame audio_frame; + + // Create an audio frame that requires resampling from 48kHz to 96kHz + // with a high number of channels (16) to exceed the buffer size. + const int kCurrentSampleRateHz = 48000; + const int kDesiredSampleRateHz = 96000; + const size_t kChannels = 16; + + // 10 ms of data at 48kHz = 480 samples per channel. + std::vector dummy_data(480 * 16, 0); + audio_frame.UpdateFrame(0, dummy_data.data(), 480, kCurrentSampleRateHz, + AudioFrame::kNormalSpeech, AudioFrame::kVadActive, + kChannels); + + // The resampler prime path will attempt to allocate a buffer that is + // kChannels * (kDesiredSampleRateHz / 100) = 16 * 960 = 15360 samples, + // which exceeds AudioFrame::kMaxDataSizeSamples (7680). + const bool resample_success = + resampler.MaybeResample(kDesiredSampleRateHz, &audio_frame); + + // Verify that MaybeResample correctly detects the buffer size condition and + // safely aborts the operation by returning false, muting the frame, and + // capping the channel count to avoid a buffer overflow in the muted data + // array. + EXPECT_FALSE(resample_success); + EXPECT_TRUE(audio_frame.muted()); + EXPECT_EQ(audio_frame.sample_rate_hz_, kDesiredSampleRateHz); + EXPECT_EQ(audio_frame.num_channels_, AudioFrame::kMaxDataSizeSamples / + audio_frame.samples_per_channel()); +} + +TEST(ResamplerHelperTest, MaybeResampleValidMaxSize) { + ResamplerHelper resampler; + AudioFrame audio_frame; + + // Ensure that resampling within the valid buffer size does not trigger the + // muting behavior. We'll use a valid number of channels (e.g. 1) that will + // not exceed the bounds. + const int kCurrentSampleRateHz = 32000; + const int kDesiredSampleRateHz = 48000; + const size_t kChannels = 1; + + std::vector dummy_data(320 * 1, 1000); + audio_frame.UpdateFrame(0, dummy_data.data(), 320, kCurrentSampleRateHz, + AudioFrame::kNormalSpeech, AudioFrame::kVadActive, + kChannels); + + const bool resample_success = + resampler.MaybeResample(kDesiredSampleRateHz, &audio_frame); + + EXPECT_TRUE(resample_success); + EXPECT_FALSE(audio_frame.muted()); + EXPECT_EQ(audio_frame.sample_rate_hz_, kDesiredSampleRateHz); + EXPECT_EQ(audio_frame.num_channels_, kChannels); +} + +} // namespace acm2 +} // namespace webrtc diff --git a/modules/audio_coding/acm2/audio_coding_module.cc b/modules/audio_coding/acm2/audio_coding_module.cc index dfbfe18a2dd..784ceb011f2 100644 --- a/modules/audio_coding/acm2/audio_coding_module.cc +++ b/modules/audio_coding/acm2/audio_coding_module.cc @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/function_view.h" @@ -262,7 +262,7 @@ int32_t AudioCodingModuleImpl::Encode( encode_buffer_.Clear(); encoded_info = encoder_stack_->Encode( rtp_timestamp, - ArrayView( + std::span( input_data.audio, input_data.audio_channel * input_data.length_per_channel), &encode_buffer_); diff --git a/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/acm2/audio_coding_module_unittest.cc index ae96aa49857..cf9a385cc23 100644 --- a/modules/audio_coding/acm2/audio_coding_module_unittest.cc +++ b/modules/audio_coding/acm2/audio_coding_module_unittest.cc @@ -18,12 +18,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_encoder_factory.h" @@ -208,7 +208,7 @@ class AudioCodingModuleTestOldApi : public ::testing::Test { const uint8_t kPayload[kPayloadSizeBytes] = {0}; ASSERT_EQ(0, neteq_->InsertPacket( rtp_header_, - ArrayView(kPayload, kPayloadSizeBytes), + std::span(kPayload, kPayloadSizeBytes), /*receive_time=*/Timestamp::MinusInfinity())); rtp_utility_->Forward(&rtp_header_); } @@ -575,8 +575,12 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test, // Extract and verify the payload checksum. Buffer checksum_result = - Buffer::CreateUninitializedWithSize(payload_checksum_->Size()); - payload_checksum_->Finish(checksum_result.data(), checksum_result.size()); + Buffer::CreateWithCapacity(payload_checksum_->Size()); + checksum_result.AppendData( + payload_checksum_->Size(), [&](std::span checksum_view) { + payload_checksum_->Finish(checksum_view.data(), checksum_view.size()); + return checksum_view.size(); + }); checksum_string = hex_encode(checksum_result); ExpectChecksumEq(payload_checksum_ref, checksum_string); @@ -1113,7 +1117,7 @@ TEST_F(AcmSenderBitExactnessOldApi, External_Pcmu_20ms) { .Times(AtLeast(1)) .WillRepeatedly(Invoke( &encoder, static_cast, Buffer*)>( + uint32_t, std::span, Buffer*)>( &AudioEncoderPcmU::Encode))); ASSERT_TRUE(SetUpSender(kTestFileMono32kHz, 32000)); ASSERT_NO_FATAL_FAILURE( diff --git a/modules/audio_coding/audio_network_adaptor/controller_manager.cc b/modules/audio_coding/audio_network_adaptor/controller_manager.cc index 87f771b9112..f5cae7d822d 100644 --- a/modules/audio_coding/audio_network_adaptor/controller_manager.cc +++ b/modules/audio_coding/audio_network_adaptor/controller_manager.cc @@ -18,12 +18,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "modules/audio_coding/audio_network_adaptor/bitrate_controller.h" #include "modules/audio_coding/audio_network_adaptor/channel_controller.h" @@ -88,7 +88,7 @@ std::unique_ptr CreateFecControllerPlrBased( std::unique_ptr CreateFrameLengthController( const audio_network_adaptor::config::FrameLengthController& config, - ArrayView encoder_frame_lengths_ms, + std::span encoder_frame_lengths_ms, int initial_frame_length_ms, int min_encoder_bitrate_bps) { RTC_CHECK(config.has_fl_increasing_packet_loss_fraction()); @@ -212,7 +212,7 @@ std::unique_ptr CreateBitrateController( std::unique_ptr CreateFrameLengthControllerV2( const audio_network_adaptor::config::FrameLengthControllerV2& config, - ArrayView encoder_frame_lengths_ms) { + std::span encoder_frame_lengths_ms) { return std::make_unique( encoder_frame_lengths_ms, config.min_payload_bitrate_bps(), config.use_slow_adaptation()); @@ -232,7 +232,7 @@ std::unique_ptr ControllerManagerImpl::Create( const Environment& env, absl::string_view config_string, size_t num_encoder_channels, - ArrayView encoder_frame_lengths_ms, + std::span encoder_frame_lengths_ms, int min_encoder_bitrate_bps, size_t intial_channels_to_encode, int initial_frame_length_ms, diff --git a/modules/audio_coding/audio_network_adaptor/controller_manager.h b/modules/audio_coding/audio_network_adaptor/controller_manager.h index 318db0c72b8..f85a5839194 100644 --- a/modules/audio_coding/audio_network_adaptor/controller_manager.h +++ b/modules/audio_coding/audio_network_adaptor/controller_manager.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "modules/audio_coding/audio_network_adaptor/controller.h" @@ -55,7 +55,7 @@ class ControllerManagerImpl final : public ControllerManager { const Environment& env, absl::string_view config_string, size_t num_encoder_channels, - ArrayView encoder_frame_lengths_ms, + std::span encoder_frame_lengths_ms, int min_encoder_bitrate_bps, size_t intial_channels_to_encode, int initial_frame_length_ms, diff --git a/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.cc b/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.cc index bbb0a2de29d..bb033b228e6 100644 --- a/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.cc +++ b/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.cc @@ -10,8 +10,9 @@ #include "modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.h" +#include + #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" #include "rtc_base/checks.h" @@ -25,7 +26,7 @@ int OverheadBps(int overhead_bytes_per_packet, int frame_length_ms) { } // namespace FrameLengthControllerV2::FrameLengthControllerV2( - ArrayView encoder_frame_lengths_ms, + std::span encoder_frame_lengths_ms, int min_payload_bitrate_bps, bool use_slow_adaptation) : encoder_frame_lengths_ms_(encoder_frame_lengths_ms.begin(), diff --git a/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.h b/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.h index e8ff5adfde7..4ecfb0f4b6b 100644 --- a/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.h +++ b/modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.h @@ -12,9 +12,9 @@ #define MODULES_AUDIO_CODING_AUDIO_NETWORK_ADAPTOR_FRAME_LENGTH_CONTROLLER_V2_H_ #include +#include #include -#include "api/array_view.h" #include "modules/audio_coding/audio_network_adaptor/controller.h" #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h" @@ -22,7 +22,7 @@ namespace webrtc { class FrameLengthControllerV2 final : public Controller { public: - FrameLengthControllerV2(ArrayView encoder_frame_lengths_ms, + FrameLengthControllerV2(std::span encoder_frame_lengths_ms, int min_payload_bitrate_bps, bool use_slow_adaptation); diff --git a/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc b/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc index fe3adbd8233..0d678de98bf 100644 --- a/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc +++ b/modules/audio_coding/codecs/builtin_audio_decoder_factory_unittest.cc @@ -64,9 +64,9 @@ TEST(AudioDecoderFactoryTest, CreateL16) { const Environment env = CreateEnvironment(); scoped_refptr adf = CreateBuiltinAudioDecoderFactory(); ASSERT_TRUE(adf); - // L16 supports any clock rate and any number of channels up to 24. + // L16 supports any clock rate and any number of channels up to 16. const int clockrates[] = {8000, 16000, 32000, 48000}; - const int num_channels[] = {1, 2, 3, 24}; + const int num_channels[] = {1, 2, 3, 16}; for (int clockrate : clockrates) { EXPECT_FALSE( adf->Create(env, SdpAudioFormat("l16", clockrate, 0), std::nullopt)); diff --git a/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc b/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc index f1250a29f44..d651d7888fc 100644 --- a/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc +++ b/modules/audio_coding/codecs/builtin_audio_encoder_factory_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/audio_format.h" @@ -83,7 +83,7 @@ TEST_P(AudioEncoderFactoryTest, CanRunAllSupportedEncoders) { encoder->NumChannels() / 100); Buffer out; BufferT audio; - audio.SetData(num_samples, [](ArrayView audio) { + audio.SetData(num_samples, [](std::span audio) { for (size_t i = 0; i != audio.size(); ++i) { // Just put some numbers in there, ensure they're within range. audio[i] = diff --git a/modules/audio_coding/codecs/cng/audio_encoder_cng.cc b/modules/audio_coding/codecs/cng/audio_encoder_cng.cc index e93887606fb..384038c0575 100644 --- a/modules/audio_coding/codecs/cng/audio_encoder_cng.cc +++ b/modules/audio_coding/codecs/cng/audio_encoder_cng.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/units/time_delta.h" #include "common_audio/vad/include/vad.h" @@ -49,14 +49,14 @@ class AudioEncoderCng final : public AudioEncoder { size_t Max10MsFramesInAPacket() const override; int GetTargetBitrate() const override; EncodedInfo EncodeImpl(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) override; void Reset() override; bool SetFec(bool enable) override; bool SetDtx(bool enable) override; bool SetApplication(Application application) override; void SetMaxPlaybackRate(int frequency_hz) override; - ArrayView> ReclaimContainedEncoders() override; + std::span> ReclaimContainedEncoders() override; void OnReceivedUplinkPacketLossFraction( float uplink_packet_loss_fraction) override; void OnReceivedUplinkBandwidth(int target_audio_bitrate_bps, @@ -125,14 +125,14 @@ int AudioEncoderCng::GetTargetBitrate() const { AudioEncoder::EncodedInfo AudioEncoderCng::EncodeImpl( uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) { const size_t samples_per_10ms_frame = SamplesPer10msFrame(); RTC_CHECK_EQ(speech_buffer_.size(), rtp_timestamps_.size() * samples_per_10ms_frame); rtp_timestamps_.push_back(rtp_timestamp); RTC_DCHECK_EQ(samples_per_10ms_frame, audio.size()); - speech_buffer_.insert(speech_buffer_.end(), audio.cbegin(), audio.cend()); + speech_buffer_.insert(speech_buffer_.end(), audio.begin(), audio.end()); const size_t frames_to_encode = speech_encoder_->Num10MsFramesInNextPacket(); if (rtp_timestamps_.size() < frames_to_encode) { return EncodedInfo(); @@ -216,9 +216,9 @@ void AudioEncoderCng::SetMaxPlaybackRate(int frequency_hz) { speech_encoder_->SetMaxPlaybackRate(frequency_hz); } -ArrayView> +std::span> AudioEncoderCng::ReclaimContainedEncoders() { - return ArrayView>(&speech_encoder_, 1); + return std::span>(&speech_encoder_, 1); } void AudioEncoderCng::OnReceivedUplinkPacketLossFraction( @@ -253,7 +253,7 @@ AudioEncoder::EncodedInfo AudioEncoderCng::EncodePassive( // that value, in which case we don't want to overwrite any value from // an earlier iteration. size_t encoded_bytes_tmp = cng_encoder_->Encode( - ArrayView(&speech_buffer_[i * samples_per_10ms_frame], + std::span(&speech_buffer_[i * samples_per_10ms_frame], samples_per_10ms_frame), force_sid, encoded); @@ -279,7 +279,7 @@ AudioEncoder::EncodedInfo AudioEncoderCng::EncodeActive(size_t frames_to_encode, for (size_t i = 0; i < frames_to_encode; ++i) { info = speech_encoder_->Encode( rtp_timestamps_.front(), - ArrayView(&speech_buffer_[i * samples_per_10ms_frame], + std::span(&speech_buffer_[i * samples_per_10ms_frame], samples_per_10ms_frame), encoded); if (i + 1 == frames_to_encode) { diff --git a/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc b/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc index 720e2366934..81642e24c47 100644 --- a/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc +++ b/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/units/time_delta.h" #include "common_audio/vad/include/vad.h" @@ -98,7 +98,7 @@ class AudioEncoderCngTest : public ::testing::Test { void Encode() { ASSERT_TRUE(cng_) << "Must call CreateCng() first."; encoded_info_ = cng_->Encode( - timestamp_, ArrayView(audio_, num_audio_samples_10ms_), + timestamp_, std::span(audio_, num_audio_samples_10ms_), &encoded_); timestamp_ += static_cast(num_audio_samples_10ms_); } diff --git a/modules/audio_coding/codecs/cng/cng_unittest.cc b/modules/audio_coding/codecs/cng/cng_unittest.cc index 309e2b6880d..d6a13056752 100644 --- a/modules/audio_coding/codecs/cng/cng_unittest.cc +++ b/modules/audio_coding/codecs/cng/cng_unittest.cc @@ -10,9 +10,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_coding/codecs/cng/webrtc_cng.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -66,11 +66,11 @@ void CngTest::TestCngEncode(int sample_rate_hz, int quality) { ComfortNoiseEncoder cng_encoder(sample_rate_hz, kSidNormalIntervalUpdate, quality); EXPECT_EQ(0U, cng_encoder.Encode( - ArrayView(speech_data_, num_samples_10ms), + std::span(speech_data_, num_samples_10ms), kNoSid, &sid_data)); EXPECT_EQ(static_cast(quality + 1), cng_encoder.Encode( - ArrayView(speech_data_, num_samples_10ms), + std::span(speech_data_, num_samples_10ms), kForceSid, &sid_data)); } @@ -100,7 +100,7 @@ TEST_F(CngDeathTest, CngEncodeTooLong) { ComfortNoiseEncoder cng_encoder(8000, kSidNormalIntervalUpdate, kCNGNumParamsNormal); // Run encoder with too much data. - EXPECT_DEATH(cng_encoder.Encode(ArrayView(speech_data_, 641), + EXPECT_DEATH(cng_encoder.Encode(std::span(speech_data_, 641), kNoSid, &sid_data), ""); } @@ -137,7 +137,7 @@ TEST_F(CngTest, CngUpdateSid) { // Run normal Encode and UpdateSid. EXPECT_EQ(kCNGNumParamsNormal + 1, - cng_encoder.Encode(ArrayView(speech_data_, 160), + cng_encoder.Encode(std::span(speech_data_, 160), kForceSid, &sid_data)); cng_decoder.UpdateSid(sid_data); @@ -146,14 +146,14 @@ TEST_F(CngTest, CngUpdateSid) { cng_decoder.Reset(); // Expect 0 because of unstable parameters after switching length. - EXPECT_EQ(0U, cng_encoder.Encode(ArrayView(speech_data_, 160), + EXPECT_EQ(0U, cng_encoder.Encode(std::span(speech_data_, 160), kForceSid, &sid_data)); EXPECT_EQ( kCNGNumParamsHigh + 1, - cng_encoder.Encode(ArrayView(speech_data_ + 160, 160), + cng_encoder.Encode(std::span(speech_data_ + 160, 160), kForceSid, &sid_data)); cng_decoder.UpdateSid( - ArrayView(sid_data.data(), kCNGNumParamsNormal + 1)); + std::span(sid_data.data(), kCNGNumParamsNormal + 1)); } // Update SID parameters, with wrong parameters or without calling decode. @@ -165,7 +165,7 @@ TEST_F(CngTest, CngUpdateSidErroneous) { kCNGNumParamsNormal); ComfortNoiseDecoder cng_decoder; EXPECT_EQ(kCNGNumParamsNormal + 1, - cng_encoder.Encode(ArrayView(speech_data_, 160), + cng_encoder.Encode(std::span(speech_data_, 160), kForceSid, &sid_data)); // First run with valid parameters, then with too many CNG parameters. @@ -193,18 +193,18 @@ TEST_F(CngTest, CngGenerate) { // Normal Encode. EXPECT_EQ(kCNGNumParamsNormal + 1, - cng_encoder.Encode(ArrayView(speech_data_, 160), + cng_encoder.Encode(std::span(speech_data_, 160), kForceSid, &sid_data)); // Normal UpdateSid. cng_decoder.UpdateSid(sid_data); // Two normal Generate, one with new_period. - EXPECT_TRUE(cng_decoder.Generate(ArrayView(out_data, 640), 1)); - EXPECT_TRUE(cng_decoder.Generate(ArrayView(out_data, 640), 0)); + EXPECT_TRUE(cng_decoder.Generate(std::span(out_data, 640), 1)); + EXPECT_TRUE(cng_decoder.Generate(std::span(out_data, 640), 0)); // Call Genereate with too much data. - EXPECT_FALSE(cng_decoder.Generate(ArrayView(out_data, 641), 0)); + EXPECT_FALSE(cng_decoder.Generate(std::span(out_data, 641), 0)); } // Test automatic SID. @@ -219,13 +219,13 @@ TEST_F(CngTest, CngAutoSid) { // Normal Encode, 100 msec, where no SID data should be generated. for (int i = 0; i < 10; i++) { EXPECT_EQ(0U, - cng_encoder.Encode(ArrayView(speech_data_, 160), + cng_encoder.Encode(std::span(speech_data_, 160), kNoSid, &sid_data)); } // We have reached 100 msec, and SID data should be generated. EXPECT_EQ(kCNGNumParamsNormal + 1, - cng_encoder.Encode(ArrayView(speech_data_, 160), + cng_encoder.Encode(std::span(speech_data_, 160), kNoSid, &sid_data)); } @@ -239,13 +239,13 @@ TEST_F(CngTest, CngAutoSidShort) { ComfortNoiseDecoder cng_decoder; // First call will never generate SID, unless forced to. - EXPECT_EQ(0U, cng_encoder.Encode(ArrayView(speech_data_, 160), + EXPECT_EQ(0U, cng_encoder.Encode(std::span(speech_data_, 160), kNoSid, &sid_data)); // Normal Encode, 100 msec, SID data should be generated all the time. for (int i = 0; i < 10; i++) { EXPECT_EQ(kCNGNumParamsNormal + 1, - cng_encoder.Encode(ArrayView(speech_data_, 160), + cng_encoder.Encode(std::span(speech_data_, 160), kNoSid, &sid_data)); } } diff --git a/modules/audio_coding/codecs/cng/webrtc_cng.cc b/modules/audio_coding/codecs/cng/webrtc_cng.cc index 5961cb0e9c4..f729bae42b7 100644 --- a/modules/audio_coding/codecs/cng/webrtc_cng.cc +++ b/modules/audio_coding/codecs/cng/webrtc_cng.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "common_audio/signal_processing/include/signal_processing_library.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -75,7 +75,7 @@ void ComfortNoiseDecoder::Reset() { dec_used_scale_factor_ = 0; } -void ComfortNoiseDecoder::UpdateSid(ArrayView sid) { +void ComfortNoiseDecoder::UpdateSid(std::span sid) { int16_t refCs[WEBRTC_CNG_MAX_LPC_ORDER]; int32_t targetEnergy; size_t length = sid.size(); @@ -112,7 +112,7 @@ void ComfortNoiseDecoder::UpdateSid(ArrayView sid) { } } -bool ComfortNoiseDecoder::Generate(ArrayView out_data, +bool ComfortNoiseDecoder::Generate(std::span out_data, bool new_period) { int16_t excitation[kCngMaxOutsizeOrder]; int16_t low[kCngMaxOutsizeOrder]; @@ -236,7 +236,7 @@ void ComfortNoiseEncoder::Reset(int fs, int interval, int quality) { enc_seed_ = 7777; /* For debugging only. */ } -size_t ComfortNoiseEncoder::Encode(ArrayView speech, +size_t ComfortNoiseEncoder::Encode(std::span speech, bool force_sid, Buffer* output) { int16_t arCoefs[WEBRTC_CNG_MAX_LPC_ORDER + 1]; @@ -367,7 +367,7 @@ size_t ComfortNoiseEncoder::Encode(ArrayView speech, index = 94; const size_t output_coefs = enc_nrOfCoefs_ + 1; - output->AppendData(output_coefs, [&](ArrayView output) { + output->AppendData(output_coefs, [&](std::span output) { output[0] = (uint8_t)index; /* Quantize coefficients with tweak for WebRtc implementation of diff --git a/modules/audio_coding/codecs/cng/webrtc_cng.h b/modules/audio_coding/codecs/cng/webrtc_cng.h index 738f60aa1ff..4a12612b29a 100644 --- a/modules/audio_coding/codecs/cng/webrtc_cng.h +++ b/modules/audio_coding/codecs/cng/webrtc_cng.h @@ -14,8 +14,8 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/buffer.h" #define WEBRTC_CNG_MAX_LPC_ORDER 12 @@ -34,7 +34,7 @@ class ComfortNoiseDecoder { // Updates the CN state when a new SID packet arrives. // `sid` is a view of the SID packet without the headers. - void UpdateSid(ArrayView sid); + void UpdateSid(std::span sid); // Generates comfort noise. // `out_data` will be filled with samples - its size determines the number of @@ -43,7 +43,7 @@ class ComfortNoiseDecoder { // currently 640 bytes (equalling 10ms at 64kHz). // TODO(ossu): Specify better limits for the size of out_data. Either let it // be unbounded or limit to 10ms in the current sample rate. - bool Generate(ArrayView out_data, bool new_period); + bool Generate(std::span out_data, bool new_period); private: uint32_t dec_seed_; @@ -79,7 +79,7 @@ class ComfortNoiseEncoder { // true, a SID frame is forced and the internal sid interval counter is reset. // Will fail if the input size is too large (> 640 samples, see // ComfortNoiseDecoder::Generate). - size_t Encode(ArrayView speech, + size_t Encode(std::span speech, bool force_sid, Buffer* output); diff --git a/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc b/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc index 281be597cf7..eaaa9b63214 100644 --- a/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc +++ b/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/units/time_delta.h" #include "modules/audio_coding/codecs/g711/g711_interface.h" @@ -69,7 +69,7 @@ int AudioEncoderPcm::GetTargetBitrate() const { AudioEncoder::EncodedInfo AudioEncoderPcm::EncodeImpl( uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) { if (speech_buffer_.empty()) { first_timestamp_in_buffer_ = rtp_timestamp; @@ -83,7 +83,7 @@ AudioEncoder::EncodedInfo AudioEncoderPcm::EncodeImpl( info.encoded_timestamp = first_timestamp_in_buffer_; info.payload_type = payload_type_; info.encoded_bytes = encoded->AppendData( - full_frame_samples_ * BytesPerSample(), [&](ArrayView encoded) { + full_frame_samples_ * BytesPerSample(), [&](std::span encoded) { return EncodeCall(&speech_buffer_[0], full_frame_samples_, encoded.data()); }); diff --git a/modules/audio_coding/codecs/g711/audio_encoder_pcm.h b/modules/audio_coding/codecs/g711/audio_encoder_pcm.h index 0d56e58d02e..1ba2e1f7468 100644 --- a/modules/audio_coding/codecs/g711/audio_encoder_pcm.h +++ b/modules/audio_coding/codecs/g711/audio_encoder_pcm.h @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/units/time_delta.h" #include "rtc_base/buffer.h" @@ -54,7 +54,7 @@ class AudioEncoderPcm : public AudioEncoder { AudioEncoderPcm(const Config& config, int sample_rate_hz); EncodedInfo EncodeImpl(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) override; virtual size_t EncodeCall(const int16_t* audio, diff --git a/modules/audio_coding/codecs/g722/audio_encoder_g722.cc b/modules/audio_coding/codecs/g722/audio_encoder_g722.cc index 45ffeff413e..36c8440b724 100644 --- a/modules/audio_coding/codecs/g722/audio_encoder_g722.cc +++ b/modules/audio_coding/codecs/g722/audio_encoder_g722.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/g722/audio_encoder_g722_config.h" #include "api/units/time_delta.h" @@ -94,7 +94,7 @@ AudioEncoderG722Impl::GetFrameLengthRange() const { AudioEncoder::EncodedInfo AudioEncoderG722Impl::EncodeImpl( uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) { if (num_10ms_frames_buffered_ == 0) first_timestamp_in_buffer_ = rtp_timestamp; @@ -124,7 +124,7 @@ AudioEncoder::EncodedInfo AudioEncoderG722Impl::EncodeImpl( const size_t bytes_to_encode = samples_per_channel / 2 * num_channels_; EncodedInfo info; info.encoded_bytes = - encoded->AppendData(bytes_to_encode, [&](ArrayView encoded) { + encoded->AppendData(bytes_to_encode, [&](std::span encoded) { // Interleave the encoded bytes of the different channels. Each separate // channel and the interleaved stream encodes two samples per byte, most // significant half first. diff --git a/modules/audio_coding/codecs/g722/audio_encoder_g722.h b/modules/audio_coding/codecs/g722/audio_encoder_g722.h index c7942029eb0..97e5cfa271a 100644 --- a/modules/audio_coding/codecs/g722/audio_encoder_g722.h +++ b/modules/audio_coding/codecs/g722/audio_encoder_g722.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/g722/audio_encoder_g722_config.h" #include "api/units/time_delta.h" @@ -47,7 +47,7 @@ class AudioEncoderG722Impl final : public AudioEncoder { protected: EncodedInfo EncodeImpl(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) override; private: diff --git a/modules/audio_coding/codecs/legacy_encoded_audio_frame.cc b/modules/audio_coding/codecs/legacy_encoded_audio_frame.cc index 8c914ca28fa..cd26ef9ba36 100644 --- a/modules/audio_coding/codecs/legacy_encoded_audio_frame.cc +++ b/modules/audio_coding/codecs/legacy_encoded_audio_frame.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -37,7 +37,7 @@ size_t LegacyEncodedAudioFrame::Duration() const { } std::optional -LegacyEncodedAudioFrame::Decode(ArrayView decoded) const { +LegacyEncodedAudioFrame::Decode(std::span decoded) const { AudioDecoder::SpeechType speech_type = AudioDecoder::kSpeech; const int ret = decoder_->Decode( payload_.data(), payload_.size(), decoder_->SampleRateHz(), diff --git a/modules/audio_coding/codecs/legacy_encoded_audio_frame.h b/modules/audio_coding/codecs/legacy_encoded_audio_frame.h index 50349e0aed6..e8353bad342 100644 --- a/modules/audio_coding/codecs/legacy_encoded_audio_frame.h +++ b/modules/audio_coding/codecs/legacy_encoded_audio_frame.h @@ -15,9 +15,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "rtc_base/buffer.h" @@ -37,7 +37,7 @@ class LegacyEncodedAudioFrame final : public AudioDecoder::EncodedAudioFrame { size_t Duration() const override; - std::optional Decode(ArrayView decoded) const override; + std::optional Decode(std::span decoded) const override; // For testing: const Buffer& payload() const { return payload_; } diff --git a/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc b/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc index 1512493ec8c..a9be6de2c6c 100644 --- a/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc +++ b/modules/audio_coding/codecs/legacy_encoded_audio_frame_unittest.cc @@ -12,6 +12,7 @@ #include #include +#include #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -131,12 +132,15 @@ TEST_P(SplitBySamplesTest, PayloadSizes) { // resulting frames can be checked and we can be reasonably certain no // sample was missed or repeated. const auto generate_payload = [](size_t num_bytes) { - Buffer payload = Buffer::CreateUninitializedWithSize(num_bytes); - uint8_t value = 0; - // Allow wrap-around of value in counter below. - for (size_t i = 0; i != payload.size(); ++i, ++value) { - payload[i] = value; - } + Buffer payload = Buffer::CreateWithCapacity(num_bytes); + payload.AppendData(num_bytes, [](std::span payload_view) { + uint8_t value = 0; + // Allow wrap-around of value in counter below. + for (size_t i = 0; i != payload_view.size(); ++i, ++value) { + payload_view[i] = value; + } + return payload_view.size(); + }); return payload; }; diff --git a/modules/audio_coding/codecs/opus/DEPS b/modules/audio_coding/codecs/opus/DEPS index c2530726ad6..ea59f626495 100644 --- a/modules/audio_coding/codecs/opus/DEPS +++ b/modules/audio_coding/codecs/opus/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - "opus_inst\.h": [ + "opus_inst\\.h": [ "+third_party/opus", ], } diff --git a/modules/audio_coding/codecs/opus/audio_coder_opus_common.h b/modules/audio_coding/codecs/opus/audio_coder_opus_common.h index 011abfb25f2..4d98cae858a 100644 --- a/modules/audio_coding/codecs/opus/audio_coder_opus_common.h +++ b/modules/audio_coding/codecs/opus/audio_coder_opus_common.h @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" #include "rtc_base/buffer.h" @@ -61,7 +61,7 @@ class OpusFrame : public AudioDecoder::EncodedAudioFrame { bool IsDtxPacket() const override { return payload_.size() <= 2; } std::optional Decode( - ArrayView decoded) const override { + std::span decoded) const override { AudioDecoder::SpeechType speech_type = AudioDecoder::kSpeech; int ret; if (is_primary_payload_) { diff --git a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc index c4fb057718d..203969f967d 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_opus.cc +++ b/modules/audio_coding/codecs/opus/audio_decoder_opus.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "api/field_trials_view.h" #include "modules/audio_coding/codecs/opus/audio_coder_opus_common.h" @@ -141,7 +141,7 @@ void AudioDecoderOpusImpl::GeneratePlc( return; } int plc_size = WebRtcOpus_PlcDuration(dec_state_) * channels_; - concealment_audio->AppendData(plc_size, [&](ArrayView decoded) { + concealment_audio->AppendData(plc_size, [&](std::span decoded) { int16_t temp_type = 1; int ret = WebRtcOpus_Decode(dec_state_, nullptr, 0, decoded.data(), &temp_type); diff --git a/modules/audio_coding/codecs/opus/audio_decoder_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_decoder_opus_unittest.cc index fb13f8ab356..b4d5df526a6 100644 --- a/modules/audio_coding/codecs/opus/audio_decoder_opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/audio_decoder_opus_unittest.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_encoder.h" @@ -82,7 +82,7 @@ class WhiteNoiseGenerator { std::numeric_limits::max())), random_generator_(42) {} - void GenerateNextFrame(ArrayView frame) { + void GenerateNextFrame(std::span frame) { for (size_t i = 0; i < frame.size(); ++i) { frame[i] = saturated_cast( random_generator_.Rand(-amplitude_, amplitude_)); @@ -94,7 +94,7 @@ class WhiteNoiseGenerator { Random random_generator_; }; -bool IsZeroedFrame(ArrayView audio) { +bool IsZeroedFrame(std::span audio) { for (const int16_t& v : audio) { if (v != 0) return false; @@ -102,7 +102,7 @@ bool IsZeroedFrame(ArrayView audio) { return true; } -bool IsTrivialStereo(ArrayView audio) { +bool IsTrivialStereo(std::span audio) { const int num_samples = CheckedDivExact(audio.size(), static_cast(2)); for (int i = 0, j = 0; i < num_samples; ++i, j += 2) { if (audio[j] != audio[j + 1]) { @@ -313,7 +313,7 @@ TEST(AudioDecoderOpusTest, ASSERT_EQ(speech_type, AudioDecoder::SpeechType::kComfortNoise); RTC_CHECK_GT(num_decoded_samples, 0); RTC_CHECK_LE(num_decoded_samples, decoded_frame.size()); - ArrayView decoded_view(decoded_frame.data(), + std::span decoded_view(decoded_frame.data(), num_decoded_samples); // Make sure that comfort noise is not a muted frame. ASSERT_FALSE(IsZeroedFrame(decoded_view)); @@ -352,7 +352,7 @@ TEST(AudioDecoderOpusTest, MonoEncoderStereoDecoderOutputsTrivialStereoPlc) { decoder.GeneratePlc(/*requested_samples_per_channel=*/kIgnored, &concealment_audio); RTC_CHECK_GT(concealment_audio.size(), 0); - ArrayView decoded_view(concealment_audio.data(), + std::span decoded_view(concealment_audio.data(), concealment_audio.size()); // Make sure that packet loss concealment is not a muted frame. ASSERT_FALSE(IsZeroedFrame(decoded_view)); @@ -450,7 +450,7 @@ TEST(AudioDecoderOpusTest, ASSERT_EQ(speech_type, AudioDecoder::SpeechType::kComfortNoise); RTC_CHECK_GT(num_decoded_samples, 0); RTC_CHECK_LE(num_decoded_samples, decoded_frame.size()); - ArrayView decoded_view(decoded_frame.data(), + std::span decoded_view(decoded_frame.data(), num_decoded_samples); // Make sure that comfort noise is not a muted frame. ASSERT_FALSE(IsZeroedFrame(decoded_view)); @@ -484,7 +484,7 @@ TEST(AudioDecoderOpusTest, decoder.GeneratePlc(/*requested_samples_per_channel=*/kIgnored, &concealment_audio); RTC_CHECK_GT(concealment_audio.size(), 0); - ArrayView decoded_view(concealment_audio.data(), + std::span decoded_view(concealment_audio.data(), concealment_audio.size()); // Make sure that packet loss concealment is not a muted frame. ASSERT_FALSE(IsZeroedFrame(decoded_view)); diff --git a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc index d5188f51a21..646cd9f5702 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.cc @@ -25,12 +25,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/match.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h" @@ -339,12 +339,12 @@ int AudioEncoderMultiChannelOpusImpl::GetTargetBitrate() const { AudioEncoder::EncodedInfo AudioEncoderMultiChannelOpusImpl::EncodeImpl( uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) { if (input_buffer_.empty()) first_timestamp_in_buffer_ = rtp_timestamp; - input_buffer_.insert(input_buffer_.end(), audio.cbegin(), audio.cend()); + input_buffer_.insert(input_buffer_.end(), audio.begin(), audio.end()); if (input_buffer_.size() < (Num10msFramesPerPacket() * SamplesPer10msFrame())) { return EncodedInfo(); @@ -355,7 +355,7 @@ AudioEncoder::EncodedInfo AudioEncoderMultiChannelOpusImpl::EncodeImpl( const size_t max_encoded_bytes = SufficientOutputBufferSize(); EncodedInfo info; info.encoded_bytes = - encoded->AppendData(max_encoded_bytes, [&](ArrayView encoded) { + encoded->AppendData(max_encoded_bytes, [&](std::span encoded) { int status = WebRtcOpus_Encode( inst_, &input_buffer_[0], CheckedDivExact(input_buffer_.size(), config_.num_channels), diff --git a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h index ccf05a8429d..b86fedcf645 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h +++ b/modules/audio_coding/codecs/opus/audio_encoder_multi_channel_opus_impl.h @@ -16,10 +16,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h" @@ -59,7 +59,7 @@ class AudioEncoderMultiChannelOpusImpl final : public AudioEncoder { protected: EncodedInfo EncodeImpl(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) override; private: diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc index ff994fba573..451fe8b9e2e 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,6 @@ #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_opus_config.h" @@ -583,14 +583,14 @@ void AudioEncoderOpusImpl::SetReceiverFrameLengthRange( AudioEncoder::EncodedInfo AudioEncoderOpusImpl::EncodeImpl( uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) { MaybeUpdateUplinkBandwidth(); if (input_buffer_.empty()) first_timestamp_in_buffer_ = rtp_timestamp; - input_buffer_.insert(input_buffer_.end(), audio.cbegin(), audio.cend()); + input_buffer_.insert(input_buffer_.end(), audio.begin(), audio.end()); if (input_buffer_.size() < (Num10msFramesPerPacket() * SamplesPer10msFrame())) { return EncodedInfo(); @@ -601,7 +601,7 @@ AudioEncoder::EncodedInfo AudioEncoderOpusImpl::EncodeImpl( const size_t max_encoded_bytes = SufficientOutputBufferSize(); EncodedInfo info; info.encoded_bytes = - encoded->AppendData(max_encoded_bytes, [&](ArrayView encoded) { + encoded->AppendData(max_encoded_bytes, [&](std::span encoded) { int status = WebRtcOpus_Encode( inst_, &input_buffer_[0], CheckedDivExact(input_buffer_.size(), config_.num_channels), diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.h b/modules/audio_coding/codecs/opus/audio_encoder_opus.h index 5b873d91863..6900e0e61e4 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.h +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_opus_config.h" @@ -102,7 +102,7 @@ class AudioEncoderOpusImpl final : public AudioEncoder { ANAStats GetANAStats() const override; std::optional > GetFrameLengthRange() const override; - ArrayView supported_frame_lengths_ms() const { + std::span supported_frame_lengths_ms() const { return config_.supported_frame_lengths_ms; } @@ -117,7 +117,7 @@ class AudioEncoderOpusImpl final : public AudioEncoder { protected: EncodedInfo EncodeImpl(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) override; private: diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc index 1fcf7a70fcd..f9ead462ebd 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_opus_config.h" @@ -512,7 +512,7 @@ TEST_P(AudioEncoderOpusTest, UpdateUplinkBandwidthInAudioNetworkAdaptor) { .WillOnce(Return(50000)); EXPECT_CALL(*states->mock_audio_network_adaptor, SetUplinkBandwidth(50000)); states->encoder->Encode( - 0, ArrayView(audio.data(), audio.size()), &encoded); + 0, std::span(audio.data(), audio.size()), &encoded); // Repeat update uplink bandwidth tests. for (int i = 0; i < 5; i++) { @@ -520,7 +520,7 @@ TEST_P(AudioEncoderOpusTest, UpdateUplinkBandwidthInAudioNetworkAdaptor) { states->fake_clock->AdvanceTime( TimeDelta::Millis(states->uplink_bandwidth_update_interval_ms - 1)); states->encoder->Encode( - 0, ArrayView(audio.data(), audio.size()), &encoded); + 0, std::span(audio.data(), audio.size()), &encoded); // Update when it is time to update. EXPECT_CALL(*states->mock_bitrate_smoother, GetAverage) @@ -528,7 +528,7 @@ TEST_P(AudioEncoderOpusTest, UpdateUplinkBandwidthInAudioNetworkAdaptor) { EXPECT_CALL(*states->mock_audio_network_adaptor, SetUplinkBandwidth(40000)); states->fake_clock->AdvanceTime(TimeDelta::Millis(1)); states->encoder->Encode( - 0, ArrayView(audio.data(), audio.size()), &encoded); + 0, std::span(audio.data(), audio.size()), &encoded); } } diff --git a/modules/audio_coding/codecs/opus/opus_unittest.cc b/modules/audio_coding/codecs/opus/opus_unittest.cc index 3d86918339d..298049aa0d9 100644 --- a/modules/audio_coding/codecs/opus/opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/opus_unittest.cc @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "modules/audio_coding/codecs/opus/opus_interface.h" #include "modules/audio_coding/neteq/tools/audio_loop.h" #include "rtc_base/checks.h" @@ -131,7 +131,7 @@ class OpusTest void PrepareSpeechData(int block_length_ms, int loop_length_ms); int EncodeDecode(WebRtcOpusEncInst* encoder, - ArrayView input_audio, + std::span input_audio, WebRtcOpusDecInst* decoder, int16_t* output_audio, int16_t* audio_type); @@ -217,7 +217,7 @@ void OpusTest::CheckAudioBounded(const int16_t* audio, } int OpusTest::EncodeDecode(WebRtcOpusEncInst* encoder, - ArrayView input_audio, + std::span input_audio, WebRtcOpusDecInst* decoder, int16_t* output_audio, int16_t* audio_type) { diff --git a/modules/audio_coding/codecs/opus/test/BUILD.gn b/modules/audio_coding/codecs/opus/test/BUILD.gn index 0b2fc17a36e..37cf935be74 100644 --- a/modules/audio_coding/codecs/opus/test/BUILD.gn +++ b/modules/audio_coding/codecs/opus/test/BUILD.gn @@ -46,9 +46,7 @@ if (rtc_include_tests) { deps = [ ":test", "../../../../../common_audio", - "../../../../../common_audio:common_audio_c", "../../../../../rtc_base:checks", - "../../../../../rtc_base:macromagic", "../../../../../test:test_support", "//testing/gtest", ] diff --git a/modules/audio_coding/codecs/opus/test/blocker.cc b/modules/audio_coding/codecs/opus/test/blocker.cc index 321a3f97708..b12943fe520 100644 --- a/modules/audio_coding/codecs/opus/test/blocker.cc +++ b/modules/audio_coding/codecs/opus/test/blocker.cc @@ -14,6 +14,8 @@ #include "rtc_base/checks.h" +namespace webrtc { + namespace { // Adds `a` and `b` frame by frame into `result` (basically matrix addition). @@ -94,8 +96,6 @@ size_t gcd(size_t a, size_t b) { } // namespace -namespace webrtc { - Blocker::Blocker(size_t chunk_size, size_t block_size, size_t num_input_channels, diff --git a/modules/audio_coding/codecs/opus/test/blocker_unittest.cc b/modules/audio_coding/codecs/opus/test/blocker_unittest.cc index 47c53fbcd03..3f03795e5b4 100644 --- a/modules/audio_coding/codecs/opus/test/blocker_unittest.cc +++ b/modules/audio_coding/codecs/opus/test/blocker_unittest.cc @@ -18,10 +18,12 @@ #include "common_audio/channel_buffer.h" #include "test/gtest.h" +namespace webrtc { + namespace { // Callback Function to add 3 to every sample in the signal. -class PlusThreeBlockerCallback : public webrtc::BlockerCallback { +class PlusThreeBlockerCallback : public BlockerCallback { public: void ProcessBlock(const float* const* input, size_t num_frames, @@ -37,7 +39,7 @@ class PlusThreeBlockerCallback : public webrtc::BlockerCallback { }; // No-op Callback Function. -class CopyBlockerCallback : public webrtc::BlockerCallback { +class CopyBlockerCallback : public BlockerCallback { public: void ProcessBlock(const float* const* input, size_t num_frames, @@ -54,8 +56,6 @@ class CopyBlockerCallback : public webrtc::BlockerCallback { } // namespace -namespace webrtc { - // Tests blocking with a window that multiplies the signal by 2, a callback // that adds 3 to each sample in the signal, and different combinations of chunk // size, block size, and shift amount. diff --git a/modules/audio_coding/codecs/opus/test/lapped_transform_unittest.cc b/modules/audio_coding/codecs/opus/test/lapped_transform_unittest.cc index 4ccba02475a..3b5abedb61f 100644 --- a/modules/audio_coding/codecs/opus/test/lapped_transform_unittest.cc +++ b/modules/audio_coding/codecs/opus/test/lapped_transform_unittest.cc @@ -18,11 +18,13 @@ #include "rtc_base/checks.h" #include "test/gtest.h" -using std::complex; +namespace webrtc { namespace { -class NoopCallback : public webrtc::LappedTransform::Callback { +using std::complex; + +class NoopCallback : public LappedTransform::Callback { public: NoopCallback() : block_num_(0) {} @@ -44,7 +46,7 @@ class NoopCallback : public webrtc::LappedTransform::Callback { size_t block_num_; }; -class FftCheckerCallback : public webrtc::LappedTransform::Callback { +class FftCheckerCallback : public LappedTransform::Callback { public: FftCheckerCallback() : block_num_(0) {} @@ -85,8 +87,6 @@ void SetFloatArray(float value, int rows, int cols, float* const* array) { } // namespace -namespace webrtc { - TEST(LappedTransformTest, Windowless) { const size_t kChannels = 3; const size_t kChunkLength = 512; diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc index b40a1918cb0..b62d5f2b212 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/call/bitrate_allocation.h" #include "api/field_trials_view.h" @@ -64,7 +64,7 @@ size_t GetMaxRedundancyFromFieldTrial(const FieldTrialsView& field_trials) { AudioEncoderCopyRed::AudioEncoderCopyRed(Config&& config, const FieldTrialsView& field_trials) : speech_encoder_(std::move(config.speech_encoder)), - primary_encoded_(0, kAudioMaxRtpPacketLen), + primary_encoded_(Buffer::CreateWithCapacity(kAudioMaxRtpPacketLen)), max_packet_length_(kAudioMaxRtpPacketLen), red_payload_type_(config.payload_type) { RTC_CHECK(speech_encoder_) << "Speech encoder not provided."; @@ -106,7 +106,7 @@ int AudioEncoderCopyRed::GetTargetBitrate() const { AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeImpl( uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) { primary_encoded_.Clear(); EncodedInfo info = @@ -160,7 +160,7 @@ AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeImpl( const uint32_t timestamp_delta = info.encoded_timestamp - it->first.encoded_timestamp; encoded->data()[header_offset] = it->first.payload_type | 0x80; - SetBE16(static_cast(encoded->data()) + header_offset + 1, + SetBE16(std::span(*encoded).subspan(header_offset + 1, 2), (timestamp_delta << 2) | (it->first.encoded_bytes >> 8)); encoded->data()[header_offset + 3] = it->first.encoded_bytes & 0xff; header_offset += kRedHeaderLength; @@ -282,9 +282,9 @@ ANAStats AudioEncoderCopyRed::GetANAStats() const { return speech_encoder_->GetANAStats(); } -ArrayView> +std::span> AudioEncoderCopyRed::ReclaimContainedEncoders() { - return ArrayView>(&speech_encoder_, 1); + return std::span>(&speech_encoder_, 1); } } // namespace webrtc diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red.h b/modules/audio_coding/codecs/red/audio_encoder_copy_red.h index 7d147802ffc..070a2428004 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.h +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.h @@ -17,10 +17,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/call/bitrate_allocation.h" #include "api/field_trials_view.h" @@ -81,11 +81,11 @@ class AudioEncoderCopyRed final : public AudioEncoder { ANAStats GetANAStats() const override; std::optional> GetFrameLengthRange() const override; - ArrayView> ReclaimContainedEncoders() override; + std::span> ReclaimContainedEncoders() override; protected: EncodedInfo EncodeImpl(uint32_t rtp_timestamp, - ArrayView audio, + std::span audio, Buffer* encoded) override; private: diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc index b1805f4459d..c7ac10f0bf7 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/field_trials.h" #include "api/units/time_delta.h" @@ -71,7 +71,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test { ASSERT_TRUE(red_.get() != nullptr); encoded_.Clear(); encoded_info_ = red_->Encode( - timestamp_, ArrayView(audio_, num_audio_samples_10ms), + timestamp_, std::span(audio_, num_audio_samples_10ms), &encoded_); timestamp_ += checked_cast(num_audio_samples_10ms); } diff --git a/modules/audio_coding/neteq/accelerate.cc b/modules/audio_coding/neteq/accelerate.cc index eda43705411..38bbdd695d6 100644 --- a/modules/audio_coding/neteq/accelerate.cc +++ b/modules/audio_coding/neteq/accelerate.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_coding/neteq/audio_multi_vector.h" #include "modules/audio_coding/neteq/time_stretch.h" #include "rtc_base/checks.h" @@ -31,7 +31,7 @@ Accelerate::ReturnCodes Accelerate::Process(const int16_t* input, input_length / num_channels_ < (2 * k15ms - 1) * fs_mult_) { // Length of input data too short to do accelerate. Simply move all data // from input to output. - output->PushBackInterleaved(ArrayView(input, input_length)); + output->PushBackInterleaved(std::span(input, input_length)); return kError; } return TimeStretch::Process(input, input_length, fast_accelerate, output, @@ -74,15 +74,15 @@ Accelerate::ReturnCodes Accelerate::CheckCriteriaAndStretch( RTC_DCHECK_GE(fs_mult_120, peak_index); // Should be handled in Process(). // Copy first part; 0 to 15 ms. output->PushBackInterleaved( - ArrayView(input, fs_mult_120 * num_channels_)); + std::span(input, fs_mult_120 * num_channels_)); // Copy the `peak_index` starting at 15 ms to `temp_vector`. AudioMultiVector temp_vector(num_channels_); - temp_vector.PushBackInterleaved(ArrayView( + temp_vector.PushBackInterleaved(std::span( &input[fs_mult_120 * num_channels_], peak_index * num_channels_)); // Cross-fade `temp_vector` onto the end of `output`. output->CrossFade(temp_vector, peak_index); // Copy the last unmodified part, 15 ms + pitch period until the end. - output->PushBackInterleaved(ArrayView( + output->PushBackInterleaved(std::span( &input[(fs_mult_120 + peak_index) * num_channels_], input_length - (fs_mult_120 + peak_index) * num_channels_)); @@ -93,7 +93,7 @@ Accelerate::ReturnCodes Accelerate::CheckCriteriaAndStretch( } } else { // Accelerate not allowed. Simply move all data from decoded to outData. - output->PushBackInterleaved(ArrayView(input, input_length)); + output->PushBackInterleaved(std::span(input, input_length)); return kNoStretch; } } diff --git a/modules/audio_coding/neteq/audio_decoder_unittest.cc b/modules/audio_coding/neteq/audio_decoder_unittest.cc index 34dd59276d6..0503dda2a63 100644 --- a/modules/audio_coding/neteq/audio_decoder_unittest.cc +++ b/modules/audio_coding/neteq/audio_decoder_unittest.cc @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/g722/audio_encoder_g722_config.h" #include "api/audio_codecs/opus/audio_encoder_opus.h" @@ -147,7 +147,7 @@ class AudioDecoderTest : public ::testing::Test { encoded_info = audio_encoder_->Encode( 0, - ArrayView(interleaved_input.get(), + std::span(interleaved_input.get(), audio_encoder_->NumChannels() * audio_encoder_->SampleRateHz() / 100), output); @@ -191,7 +191,7 @@ class AudioDecoderTest : public ::testing::Test { decoder_->ParsePayload(std::move(encoded), /*timestamp=*/0); RTC_CHECK_EQ(parse_result.size(), size_t{1}); auto decode_result = parse_result[0].frame->Decode( - ArrayView(&decoded[processed_samples * channels_], + std::span(&decoded[processed_samples * channels_], frame_size_ * channels_ * sizeof(int16_t))); RTC_CHECK(decode_result.has_value()); EXPECT_EQ(frame_size_ * channels_, decode_result->num_decoded_samples); diff --git a/modules/audio_coding/neteq/audio_multi_vector.cc b/modules/audio_coding/neteq/audio_multi_vector.cc index 6ba4bc37f67..d0d1ddc1fa6 100644 --- a/modules/audio_coding/neteq/audio_multi_vector.cc +++ b/modules/audio_coding/neteq/audio_multi_vector.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "modules/audio_coding/neteq/audio_vector.h" #include "rtc_base/checks.h" @@ -67,7 +67,7 @@ void AudioMultiVector::CopyTo(AudioMultiVector* copy_to) const { } void AudioMultiVector::PushBackInterleaved( - ArrayView append_this) { + std::span append_this) { RTC_DCHECK_EQ(append_this.size() % Channels(), 0); if (append_this.empty()) { return; diff --git a/modules/audio_coding/neteq/audio_multi_vector.h b/modules/audio_coding/neteq/audio_multi_vector.h index 486c13ab54d..12b8f94799e 100644 --- a/modules/audio_coding/neteq/audio_multi_vector.h +++ b/modules/audio_coding/neteq/audio_multi_vector.h @@ -15,9 +15,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "modules/audio_coding/neteq/audio_vector.h" @@ -55,7 +55,7 @@ class AudioMultiVector { // is assumed to be channel-interleaved. The length must be an even multiple // of this object's number of channels. The length of this object is increased // with the length of the array divided by the number of channels. - void PushBackInterleaved(ArrayView append_this); + void PushBackInterleaved(std::span append_this); // Appends the contents of AudioMultiVector `append_this` to this object. The // length of this object is increased with the length of `append_this`. diff --git a/modules/audio_coding/neteq/audio_vector.cc b/modules/audio_coding/neteq/audio_vector.cc index 096c8c483f7..088578c6bcd 100644 --- a/modules/audio_coding/neteq/audio_vector.cc +++ b/modules/audio_coding/neteq/audio_vector.cc @@ -70,12 +70,12 @@ bool AudioVector::CopyTo(size_t position, MonoView dst) const { const size_t copy_index = (begin_index_ + position) % capacity_; const size_t first_chunk_length = std::min(dst.size(), capacity_ - copy_index); - auto first_chunk = dst.subview(0, first_chunk_length); + auto first_chunk = dst.subspan(0, first_chunk_length); CopySamples(first_chunk, MonoView(&array_[copy_index], first_chunk_length)); const size_t remaining_length = dst.size() - first_chunk_length; if (remaining_length > 0) { - auto second_chunk = dst.subview(first_chunk_length, remaining_length); + auto second_chunk = dst.subspan(first_chunk_length); CopySamples(second_chunk, MonoView(&array_[0], remaining_length)); } return true; diff --git a/modules/audio_coding/neteq/background_noise.cc b/modules/audio_coding/neteq/background_noise.cc index 8c5102c77ff..ce3d3430559 100644 --- a/modules/audio_coding/neteq/background_noise.cc +++ b/modules/audio_coding/neteq/background_noise.cc @@ -13,8 +13,8 @@ #include // min, max #include #include // memcpy +#include -#include "api/array_view.h" #include "common_audio/signal_processing/dot_product_with_scale.h" #include "common_audio/signal_processing/include/signal_processing_library.h" #include "common_audio/signal_processing/include/spl_inl.h" @@ -119,7 +119,7 @@ bool BackgroundNoise::Update(const AudioMultiVector& sync_buffer) { } void BackgroundNoise::GenerateBackgroundNoise( - ArrayView random_vector, + std::span random_vector, size_t channel, int /* mute_slope */, bool /* too_many_expands */, @@ -194,7 +194,7 @@ const int16_t* BackgroundNoise::FilterState(size_t channel) const { } void BackgroundNoise::SetFilterState(size_t channel, - ArrayView input) { + std::span input) { RTC_DCHECK_LT(channel, num_channels_); size_t length = std::min(input.size(), kMaxLpcOrder); memcpy(channel_parameters_[channel].filter_state, input.data(), diff --git a/modules/audio_coding/neteq/background_noise.h b/modules/audio_coding/neteq/background_noise.h index 1a506a195b8..00b7091101b 100644 --- a/modules/audio_coding/neteq/background_noise.h +++ b/modules/audio_coding/neteq/background_noise.h @@ -15,8 +15,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -46,7 +45,7 @@ class BackgroundNoise { // Generates background noise given a random vector and writes the output to // `buffer`. - void GenerateBackgroundNoise(ArrayView random_vector, + void GenerateBackgroundNoise(std::span random_vector, size_t channel, int mute_slope, bool too_many_expands, @@ -70,7 +69,7 @@ class BackgroundNoise { // Copies `input` to the filter state. Will not copy more than `kMaxLpcOrder` // elements. - void SetFilterState(size_t channel, ArrayView input); + void SetFilterState(size_t channel, std::span input); // Returns `scale_` for `channel`. int16_t Scale(size_t channel) const; diff --git a/modules/audio_coding/neteq/comfort_noise.cc b/modules/audio_coding/neteq/comfort_noise.cc index da94881585d..1b57145b6ce 100644 --- a/modules/audio_coding/neteq/comfort_noise.cc +++ b/modules/audio_coding/neteq/comfort_noise.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_coding/codecs/cng/webrtc_cng.h" #include "modules/audio_coding/neteq/audio_multi_vector.h" #include "modules/audio_coding/neteq/audio_vector.h" @@ -67,7 +67,7 @@ int ComfortNoise::Generate(size_t requested_length, AudioMultiVector* output) { } std::unique_ptr temp(new int16_t[number_of_samples]); - if (!cng_decoder->Generate(ArrayView(temp.get(), number_of_samples), + if (!cng_decoder->Generate(std::span(temp.get(), number_of_samples), new_period)) { // Error returned. output->Zeros(requested_length); diff --git a/modules/audio_coding/neteq/merge.cc b/modules/audio_coding/neteq/merge.cc index 8abaff7dbb4..ac1f51d0b76 100644 --- a/modules/audio_coding/neteq/merge.cc +++ b/modules/audio_coding/neteq/merge.cc @@ -15,8 +15,8 @@ #include // memmove, memcpy, memset, size_t #include #include +#include -#include "api/array_view.h" #include "common_audio/signal_processing/dot_product_with_scale.h" #include "common_audio/signal_processing/include/signal_processing_library.h" #include "common_audio/signal_processing/include/spl_inl.h" @@ -66,7 +66,7 @@ size_t Merge::Process(int16_t* input, // Transfer input signal to an AudioMultiVector. AudioMultiVector input_vector(num_channels_); input_vector.PushBackInterleaved( - ArrayView(input, input_length)); + std::span(input, input_length)); size_t input_length_per_channel = input_vector.Size(); RTC_DCHECK_EQ(input_length_per_channel, input_length / num_channels_); diff --git a/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc b/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc index 632d6921fb3..116892a79dc 100644 --- a/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc +++ b/modules/audio_coding/neteq/neteq_decoder_plc_unittest.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" #include "api/make_ref_counted.h" @@ -107,9 +107,9 @@ class AudioDecoderPlc : public AudioDecoder { // An input sample generator which generates only zero-samples. class ZeroSampleGenerator : public EncodeNetEqInput::Generator { public: - ArrayView Generate(size_t num_samples) override { + std::span Generate(size_t num_samples) override { vec.resize(num_samples, 0); - ArrayView view(vec); + std::span view(vec); RTC_DCHECK_EQ(view.size(), num_samples); return view; } diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc index f0e96564f7b..81420261c9c 100644 --- a/modules/audio_coding/neteq/neteq_impl.cc +++ b/modules/audio_coding/neteq/neteq_impl.cc @@ -17,11 +17,11 @@ #include #include #include +#include #include #include #include "absl/strings/str_cat.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_view.h" #include "api/audio_codecs/audio_decoder.h" @@ -180,7 +180,7 @@ NetEqImpl::NetEqImpl(const NetEq::Config& config, NetEqImpl::~NetEqImpl() = default; int NetEqImpl::InsertPacket(const RTPHeader& rtp_header, - ArrayView payload, + std::span payload, const RtpPacketInfo& packet_info) { MsanCheckInitialized(payload); TRACE_EVENT0("webrtc", "NetEqImpl::InsertPacket"); @@ -1340,7 +1340,7 @@ int NetEqImpl::DecodeLoop(PacketList* packet_list, operation == Operation::kPreemptiveExpand); auto opt_result = packet_list->front().frame->Decode( - ArrayView(&decoded_buffer_[*decoded_length], + std::span(&decoded_buffer_[*decoded_length], decoded_buffer_length_ - *decoded_length)); if (packet_list->front().packet_info) { last_decoded_packet_infos_.push_back(*packet_list->front().packet_info); diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h index c80ad4fa412..c02282a1be7 100644 --- a/modules/audio_coding/neteq/neteq_impl.h +++ b/modules/audio_coding/neteq/neteq_impl.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_decoder_factory.h" @@ -130,7 +130,7 @@ class NetEqImpl : public NetEq { NetEqImpl& operator=(const NetEqImpl&) = delete; int InsertPacket(const RTPHeader& rtp_header, - ArrayView payload) override { + std::span payload) override { return InsertPacket( rtp_header, payload, RtpPacketInfo(rtp_header, /*receive_time=*/Timestamp::MinusInfinity())); @@ -138,7 +138,7 @@ class NetEqImpl : public NetEq { // Inserts a new packet into NetEq. Returns 0 on success, -1 on failure. int InsertPacket(const RTPHeader& rtp_header, - ArrayView payload, + std::span payload, const RtpPacketInfo& packet_info) override; int GetAudio( diff --git a/modules/audio_coding/neteq/neteq_network_stats_unittest.cc b/modules/audio_coding/neteq/neteq_network_stats_unittest.cc index fb0d8e5d4bb..79e8df06592 100644 --- a/modules/audio_coding/neteq/neteq_network_stats_unittest.cc +++ b/modules/audio_coding/neteq/neteq_network_stats_unittest.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_decoder_factory.h" @@ -76,7 +76,7 @@ class MockAudioDecoder final : public AudioDecoder { size_t Duration() const override { return kPacketDuration; } std::optional Decode( - ArrayView decoded) const override { + std::span decoded) const override { const size_t output_size = sizeof(int16_t) * kPacketDuration * num_channels_; if (decoded.size() >= output_size) { diff --git a/modules/audio_coding/neteq/neteq_stereo_unittest.cc b/modules/audio_coding/neteq/neteq_stereo_unittest.cc index bdb0cfea23e..54c81b9869e 100644 --- a/modules/audio_coding/neteq/neteq_stereo_unittest.cc +++ b/modules/audio_coding/neteq/neteq_stereo_unittest.cc @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" @@ -178,13 +178,13 @@ class NetEqStereoTest : public ::testing::TestWithParam { ASSERT_EQ(NetEq::kOK, neteq_mono_->InsertPacket( rtp_header_mono_, - ArrayView(encoded_, payload_size_bytes_), + std::span(encoded_, payload_size_bytes_), Timestamp::Millis(time_now_ms))); // Insert packet in multi-channel instance. ASSERT_EQ(NetEq::kOK, neteq_->InsertPacket( rtp_header_, - ArrayView(encoded_multi_channel_, + std::span(encoded_multi_channel_, multi_payload_size_bytes_), Timestamp::Millis(time_now_ms))); // Get next input packets (mono and multi-channel). diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc index 897532fc51a..4a25d568767 100644 --- a/modules/audio_coding/neteq/neteq_unittest.cc +++ b/modules/audio_coding/neteq/neteq_unittest.cc @@ -18,11 +18,11 @@ #include #include #include +#include #include #include #include "absl/flags/flag.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/rtp_headers.h" @@ -341,7 +341,7 @@ class NetEqBgnTest : public NetEqDecodingTest { ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, ArrayView(payload, enc_len_bytes), + rtp_info, std::span(payload, enc_len_bytes), clock_.CurrentTime())); output.Reset(); ASSERT_EQ(0, neteq_->GetAudio(&output, &muted)); @@ -464,7 +464,7 @@ TEST_F(NetEqDecodingTest, DiscardDuplicateCng) { PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); // This is the first time this CNG packet is inserted. ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, ArrayView(payload, payload_len), + rtp_info, std::span(payload, payload_len), clock_.CurrentTime())); // Pull audio once and make sure CNG is played. @@ -479,7 +479,7 @@ TEST_F(NetEqDecodingTest, DiscardDuplicateCng) { // Insert the same CNG packet again. Note that at this point it is old, since // we have already decoded the first copy of it. ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, ArrayView(payload, payload_len), + rtp_info, std::span(payload, payload_len), clock_.CurrentTime())); // Pull audio until we have played `kCngPeriodMs` of CNG. Start at 10 ms since @@ -532,7 +532,7 @@ TEST_F(NetEqDecodingTest, CngFirst) { PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); ASSERT_EQ(NetEq::kOK, neteq_->InsertPacket(rtp_info, - ArrayView(payload, payload_len), + std::span(payload, payload_len), clock_.CurrentTime())); ++seq_no; timestamp += kCngPeriodSamples; @@ -585,7 +585,7 @@ class NetEqDecodingTestWithMutedState : public NetEqDecodingTest { PopulateCng(0, rtp_timestamp, &rtp_info, payload, &payload_len); EXPECT_EQ(NetEq::kOK, neteq_->InsertPacket( - rtp_info, ArrayView(payload, payload_len), + rtp_info, std::span(payload, payload_len), clock_.CurrentTime())); } diff --git a/modules/audio_coding/neteq/normal.cc b/modules/audio_coding/neteq/normal.cc index 909e955684d..be727fb7990 100644 --- a/modules/audio_coding/neteq/normal.cc +++ b/modules/audio_coding/neteq/normal.cc @@ -14,20 +14,40 @@ #include #include // memset, memcpy #include +#include -#include "api/array_view.h" #include "api/neteq/neteq.h" #include "common_audio/signal_processing/dot_product_with_scale.h" #include "common_audio/signal_processing/include/signal_processing_library.h" #include "common_audio/signal_processing/include/spl_inl.h" #include "modules/audio_coding/codecs/cng/webrtc_cng.h" #include "modules/audio_coding/neteq/audio_multi_vector.h" +#include "modules/audio_coding/neteq/audio_vector.h" #include "modules/audio_coding/neteq/background_noise.h" #include "modules/audio_coding/neteq/decoder_database.h" #include "modules/audio_coding/neteq/expand.h" #include "rtc_base/checks.h" namespace webrtc { +namespace { + +void Crossfade(const AudioVector& from, AudioVector& to, size_t win_length) { + const size_t win_length_clamped = + std::min({win_length, from.Size(), to.Size()}); + if (win_length_clamped == 0) { + return; + } + int16_t win_slope_Q14 = (1 << 14) / static_cast(win_length_clamped); + int16_t win_up_Q14 = 0; + for (size_t i = 0; i < win_length_clamped; i++) { + win_up_Q14 += win_slope_Q14; + to[i] = + (win_up_Q14 * to[i] + ((1 << 14) - win_up_Q14) * from[i] + (1 << 13)) >> + 14; + } +} + +} // namespace int Normal::Process(const int16_t* input, size_t length, @@ -46,7 +66,7 @@ int Normal::Process(const int16_t* input, output->Clear(); return 0; } - output->PushBackInterleaved(ArrayView(input, length)); + output->PushBackInterleaved(std::span(input, length)); const int fs_mult = fs_hz_ / 8000; RTC_DCHECK_GT(fs_mult, 0); @@ -138,23 +158,7 @@ int Normal::Process(const int16_t* input, // Interpolate the expanded data into the new vector. // (NB/WB/SWB32/SWB48 8/16/32/48 samples.) - size_t win_length = samples_per_ms_; - int16_t win_slope_Q14 = default_win_slope_Q14_; - RTC_DCHECK_LT(channel_ix, output->Channels()); - if (win_length > output->Size()) { - win_length = output->Size(); - win_slope_Q14 = (1 << 14) / static_cast(win_length); - } - int16_t win_up_Q14 = 0; - for (size_t i = 0; i < win_length; i++) { - win_up_Q14 += win_slope_Q14; - (*output)[channel_ix][i] = - (win_up_Q14 * (*output)[channel_ix][i] + - ((1 << 14) - win_up_Q14) * expanded[channel_ix][i] + (1 << 13)) >> - 14; - } - RTC_DCHECK_GT(win_up_Q14, - (1 << 14) - 32); // Worst case rouding is a length of 34 + Crossfade(expanded[channel_ix], (*output)[channel_ix], samples_per_ms_); } } else if (last_mode == NetEq::Mode::kRfc3389Cng) { RTC_DCHECK_EQ(output->Channels(), 1); // Not adapted for multi-channel yet. @@ -176,22 +180,9 @@ int Normal::Process(const int16_t* input, } // Interpolate the CNG into the new vector. // (NB/WB/SWB32/SWB48 8/16/32/48 samples.) - size_t win_length = samples_per_ms_; - int16_t win_slope_Q14 = default_win_slope_Q14_; - if (win_length > kCngLength) { - win_length = kCngLength; - win_slope_Q14 = (1 << 14) / static_cast(win_length); - } - int16_t win_up_Q14 = 0; - for (size_t i = 0; i < win_length; i++) { - win_up_Q14 += win_slope_Q14; - (*output)[0][i] = - (win_up_Q14 * (*output)[0][i] + - ((1 << 14) - win_up_Q14) * cng_output[i] + (1 << 13)) >> - 14; - } - RTC_DCHECK_GT(win_up_Q14, - (1 << 14) - 32); // Worst case rouding is a length of 34 + AudioVector temp_vector(kCngLength); + temp_vector.OverwriteAt(cng_output, kCngLength, 0); + Crossfade(temp_vector, (*output)[0], samples_per_ms_); } return static_cast(length); diff --git a/modules/audio_coding/neteq/normal.h b/modules/audio_coding/neteq/normal.h index e6c918764fe..d0dfc2f6cbb 100644 --- a/modules/audio_coding/neteq/normal.h +++ b/modules/audio_coding/neteq/normal.h @@ -17,7 +17,6 @@ #include "api/neteq/neteq.h" #include "modules/audio_coding/neteq/statistics_calculator.h" #include "rtc_base/checks.h" -#include "rtc_base/numerics/safe_conversions.h" namespace webrtc { @@ -42,8 +41,6 @@ class Normal { background_noise_(background_noise), expand_(expand), samples_per_ms_(CheckedDivExact(fs_hz_, 1000)), - default_win_slope_Q14_( - dchecked_cast((1 << 14) / samples_per_ms_)), statistics_(statistics) {} virtual ~Normal() {} @@ -68,7 +65,6 @@ class Normal { const BackgroundNoise& background_noise_; Expand* expand_; const size_t samples_per_ms_; - const int16_t default_win_slope_Q14_; StatisticsCalculator* const statistics_; }; diff --git a/modules/audio_coding/neteq/normal_unittest.cc b/modules/audio_coding/neteq/normal_unittest.cc index 272869d701b..c29a2c3314a 100644 --- a/modules/audio_coding/neteq/normal_unittest.cc +++ b/modules/audio_coding/neteq/normal_unittest.cc @@ -14,6 +14,7 @@ #include #include +#include #include "api/neteq/neteq.h" #include "api/neteq/tick_timer.h" @@ -147,6 +148,20 @@ TEST(Normal, LastModeExpand120msPacket) { EXPECT_CALL(expand, Die()); // Called when `expand` goes out of scope. } +TEST(Normal, LastModeRfc3389CngSmallInput) { + constexpr size_t kChannels = 1; + constexpr size_t kInputFrames = 10; + MockDecoderDatabase db; + Normal normal(/*fs_hz=*/48000, /*decoder_database=*/&db, + /*background_noise=*/BackgroundNoise(kChannels), + /*expand=*/nullptr, /*statistics=*/nullptr); + AudioMultiVector output(kChannels); + std::vector input(kChannels * kInputFrames, 0); + EXPECT_EQ(normal.Process(input.data(), input.size(), NetEq::Mode::kRfc3389Cng, + &output), + static_cast(input.size())); +} + // TODO(hlundin): Write more tests. } // namespace webrtc diff --git a/modules/audio_coding/neteq/packet_buffer_unittest.cc b/modules/audio_coding/neteq/packet_buffer_unittest.cc index c3869fb4282..5851e4a0525 100644 --- a/modules/audio_coding/neteq/packet_buffer_unittest.cc +++ b/modules/audio_coding/neteq/packet_buffer_unittest.cc @@ -17,10 +17,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "api/neteq/tick_timer.h" #include "modules/audio_coding/neteq/mock/mock_decoder_database.h" @@ -30,6 +30,8 @@ #include "test/gmock.h" #include "test/gtest.h" +namespace webrtc { + using ::testing::_; using ::testing::InSequence; using ::testing::MockFunction; @@ -37,7 +39,7 @@ using ::testing::Return; using ::testing::StrictMock; namespace { -class MockEncodedAudioFrame : public webrtc::AudioDecoder::EncodedAudioFrame { +class MockEncodedAudioFrame : public AudioDecoder::EncodedAudioFrame { public: MOCK_METHOD(size_t, Duration, (), (const, override)); @@ -45,7 +47,7 @@ class MockEncodedAudioFrame : public webrtc::AudioDecoder::EncodedAudioFrame { MOCK_METHOD(std::optional, Decode, - (webrtc::ArrayView decoded), + (std::span decoded), (const, override)); }; @@ -55,9 +57,9 @@ class PacketGenerator { PacketGenerator(uint16_t seq_no, uint32_t ts, uint8_t pt, int frame_size); virtual ~PacketGenerator() {} void Reset(uint16_t seq_no, uint32_t ts, uint8_t pt, int frame_size); - webrtc::Packet NextPacket( + Packet NextPacket( int payload_size_bytes, - std::unique_ptr audio_frame); + std::unique_ptr audio_frame); uint16_t seq_no_; uint32_t ts_; @@ -82,10 +84,10 @@ void PacketGenerator::Reset(uint16_t seq_no, frame_size_ = frame_size; } -webrtc::Packet PacketGenerator::NextPacket( +Packet PacketGenerator::NextPacket( int payload_size_bytes, - std::unique_ptr audio_frame) { - webrtc::Packet packet; + std::unique_ptr audio_frame) { + Packet packet; packet.sequence_number = seq_no_; packet.timestamp = ts_; packet.payload_type = pt_; @@ -109,8 +111,6 @@ struct PacketsToInsert { } // namespace -namespace webrtc { - // Start of test definitions. TEST(PacketBuffer, CreateAndDestroy) { diff --git a/modules/audio_coding/neteq/preemptive_expand.cc b/modules/audio_coding/neteq/preemptive_expand.cc index a183c3dd96f..1e3976ebf48 100644 --- a/modules/audio_coding/neteq/preemptive_expand.cc +++ b/modules/audio_coding/neteq/preemptive_expand.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_coding/neteq/audio_multi_vector.h" #include "modules/audio_coding/neteq/time_stretch.h" @@ -35,7 +35,7 @@ PreemptiveExpand::ReturnCodes PreemptiveExpand::Process( old_data_length >= input_length / num_channels_ - overlap_samples_) { // Length of input data too short to do preemptive expand. Simply move all // data from input to output. - output->PushBackInterleaved(ArrayView(input, input_length)); + output->PushBackInterleaved(std::span(input, input_length)); return kError; } const bool kFastMode = false; // Fast mode is not available for PE Expand. @@ -79,17 +79,17 @@ PreemptiveExpand::ReturnCodes PreemptiveExpand::CheckCriteriaAndStretch( size_t unmodified_length = std::max(old_data_length_per_channel_, fs_mult_120); // Copy first part, including cross-fade region. - output->PushBackInterleaved(ArrayView( + output->PushBackInterleaved(std::span( input, (unmodified_length + peak_index) * num_channels_)); // Copy the last `peak_index` samples up to 15 ms to `temp_vector`. AudioMultiVector temp_vector(num_channels_); - temp_vector.PushBackInterleaved(ArrayView( + temp_vector.PushBackInterleaved(std::span( &input[(unmodified_length - peak_index) * num_channels_], peak_index * num_channels_)); // Cross-fade `temp_vector` onto the end of `output`. output->CrossFade(temp_vector, peak_index); // Copy the last unmodified part, 15 ms + pitch period until the end. - output->PushBackInterleaved(ArrayView( + output->PushBackInterleaved(std::span( &input[unmodified_length * num_channels_], input_length - unmodified_length * num_channels_)); @@ -100,7 +100,7 @@ PreemptiveExpand::ReturnCodes PreemptiveExpand::CheckCriteriaAndStretch( } } else { // Accelerate not allowed. Simply move all data from decoded to outData. - output->PushBackInterleaved(ArrayView(input, input_length)); + output->PushBackInterleaved(std::span(input, input_length)); return kNoStretch; } } diff --git a/modules/audio_coding/neteq/test/neteq_decoding_test.cc b/modules/audio_coding/neteq/test/neteq_decoding_test.cc index f266081479b..45c3d6ffc9d 100644 --- a/modules/audio_coding/neteq/test/neteq_decoding_test.cc +++ b/modules/audio_coding/neteq/test/neteq_decoding_test.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" @@ -320,7 +320,7 @@ void NetEqDecodingTest::LongCngWithClockDrift(double drift_factor, RTPHeader rtp_info; PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, ArrayView(payload, payload_len), + rtp_info, std::span(payload, payload_len), Timestamp::Millis(t_ms))); ++seq_no; timestamp += kCngPeriodSamples; @@ -363,7 +363,7 @@ void NetEqDecodingTest::LongCngWithClockDrift(double drift_factor, RTPHeader rtp_info; PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len); ASSERT_EQ(0, neteq_->InsertPacket( - rtp_info, ArrayView(payload, payload_len), + rtp_info, std::span(payload, payload_len), Timestamp::Millis(t_ms))); ++seq_no; timestamp += kCngPeriodSamples; diff --git a/modules/audio_coding/neteq/test/neteq_opus_quality_test.cc b/modules/audio_coding/neteq/test/neteq_opus_quality_test.cc index ecf1a219373..e1c7e419dcb 100644 --- a/modules/audio_coding/neteq/test/neteq_opus_quality_test.cc +++ b/modules/audio_coding/neteq/test/neteq_opus_quality_test.cc @@ -11,9 +11,9 @@ #include #include #include +#include #include "absl/flags/flag.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/rtp_parameters.h" #include "modules/audio_coding/codecs/opus/opus_inst.h" @@ -161,7 +161,7 @@ int NetEqOpusQualityTest::EncodeBlock(int16_t* in_data, int value; opus_repacketizer_init(repacketizer_); for (int idx = 0; idx < sub_packets_; idx++) { - payload->AppendData(max_bytes, [&](ArrayView payload) { + payload->AppendData(max_bytes, [&](std::span payload) { value = WebRtcOpus_Encode(opus_encoder_, pointer, sub_block_size_samples_, max_bytes, payload.data()); diff --git a/modules/audio_coding/neteq/test/neteq_pcm16b_quality_test.cc b/modules/audio_coding/neteq/test/neteq_pcm16b_quality_test.cc index d1f6cf7907d..b7091397135 100644 --- a/modules/audio_coding/neteq/test/neteq_pcm16b_quality_test.cc +++ b/modules/audio_coding/neteq/test/neteq_pcm16b_quality_test.cc @@ -11,9 +11,9 @@ #include #include #include +#include #include "absl/flags/flag.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h" @@ -67,7 +67,7 @@ class NetEqPcm16bQualityTest : public NetEqQualityTest { AudioEncoder::EncodedInfo info; do { info = encoder_->Encode(dummy_timestamp, - ArrayView( + std::span( in_data + encoded_samples, kFrameSizeSamples), payload); encoded_samples += kFrameSizeSamples; diff --git a/modules/audio_coding/neteq/test/neteq_pcmu_quality_test.cc b/modules/audio_coding/neteq/test/neteq_pcmu_quality_test.cc index a4a624d3daf..b060a923813 100644 --- a/modules/audio_coding/neteq/test/neteq_pcmu_quality_test.cc +++ b/modules/audio_coding/neteq/test/neteq_pcmu_quality_test.cc @@ -11,9 +11,9 @@ #include #include #include +#include #include "absl/flags/flag.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "modules/audio_coding/codecs/g711/audio_encoder_pcm.h" @@ -66,7 +66,7 @@ class NetEqPcmuQualityTest : public NetEqQualityTest { AudioEncoder::EncodedInfo info; do { info = encoder_->Encode(dummy_timestamp, - ArrayView( + std::span( in_data + encoded_samples, kFrameSizeSamples), payload); encoded_samples += kFrameSizeSamples; diff --git a/modules/audio_coding/neteq/tools/audio_checksum.h b/modules/audio_coding/neteq/tools/audio_checksum.h index f83255ee428..de1c4395221 100644 --- a/modules/audio_coding/neteq/tools/audio_checksum.h +++ b/modules/audio_coding/neteq/tools/audio_checksum.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "modules/audio_coding/neteq/tools/audio_sink.h" @@ -29,8 +30,7 @@ class AudioChecksum : public AudioSink { public: AudioChecksum() : checksum_(MessageDigestFactory::Create(DIGEST_MD5)), - checksum_result_( - Buffer::CreateUninitializedWithSize(checksum_->Size())), + checksum_result_(Buffer::CreateWithCapacity(checksum_->Size())), finished_(false) {} AudioChecksum(const AudioChecksum&) = delete; @@ -51,7 +51,11 @@ class AudioChecksum : public AudioSink { std::string Finish() { if (!finished_) { finished_ = true; - checksum_->Finish(checksum_result_.data(), checksum_result_.size()); + checksum_result_.AppendData(checksum_->Size(), + [&](std::span view) { + checksum_->Finish(view.data(), view.size()); + return view.size(); + }); } return hex_encode(checksum_result_); } diff --git a/modules/audio_coding/neteq/tools/audio_loop.cc b/modules/audio_coding/neteq/tools/audio_loop.cc index ba67a667095..a7353b3100c 100644 --- a/modules/audio_coding/neteq/tools/audio_loop.cc +++ b/modules/audio_coding/neteq/tools/audio_loop.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" namespace webrtc { namespace test { @@ -50,14 +50,14 @@ bool AudioLoop::Init(absl::string_view file_name, return true; } -ArrayView AudioLoop::GetNextBlock() { +std::span AudioLoop::GetNextBlock() { // Check that the AudioLoop is initialized. if (block_length_samples_ == 0) - return ArrayView(); + return std::span(); const int16_t* output_ptr = &audio_array_[next_index_]; next_index_ = (next_index_ + block_length_samples_) % loop_length_samples_; - return ArrayView(output_ptr, block_length_samples_); + return std::span(output_ptr, block_length_samples_); } } // namespace test diff --git a/modules/audio_coding/neteq/tools/audio_loop.h b/modules/audio_coding/neteq/tools/audio_loop.h index d5c561d6230..b00d50a5039 100644 --- a/modules/audio_coding/neteq/tools/audio_loop.h +++ b/modules/audio_coding/neteq/tools/audio_loop.h @@ -14,9 +14,9 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" namespace webrtc { namespace test { @@ -44,7 +44,7 @@ class AudioLoop { // Returns a (pointer,size) pair for the next block of audio. The size is // equal to the `block_length_samples` Init() argument. - ArrayView GetNextBlock(); + std::span GetNextBlock(); private: size_t next_index_; diff --git a/modules/audio_coding/neteq/tools/encode_neteq_input.h b/modules/audio_coding/neteq/tools/encode_neteq_input.h index 66927d2f159..d9b1cd37330 100644 --- a/modules/audio_coding/neteq/tools/encode_neteq_input.h +++ b/modules/audio_coding/neteq/tools/encode_neteq_input.h @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "modules/audio_coding/neteq/tools/neteq_input.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" @@ -33,7 +33,7 @@ class EncodeNetEqInput : public NetEqInput { public: virtual ~Generator() = default; // Returns the next num_samples values from the signal generator. - virtual ArrayView Generate(size_t num_samples) = 0; + virtual std::span Generate(size_t num_samples) = 0; }; // The source will end after the given input duration. diff --git a/modules/audio_coding/neteq/tools/fake_decode_from_file.cc b/modules/audio_coding/neteq/tools/fake_decode_from_file.cc index c2a9c7752f4..e94c4596f56 100644 --- a/modules/audio_coding/neteq/tools/fake_decode_from_file.cc +++ b/modules/audio_coding/neteq/tools/fake_decode_from_file.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "modules/audio_coding/neteq/tools/input_audio_file.h" #include "modules/rtp_rtcp/source/byte_io.h" @@ -44,7 +44,7 @@ class FakeEncodedFrame : public AudioDecoder::EncodedAudioFrame { size_t Duration() const override { return duration_; } std::optional Decode( - ArrayView decoded) const override { + std::span decoded) const override { if (is_dtx_) { std::fill_n(decoded.data(), duration_, 0); return DecodeResult{.num_decoded_samples = duration_, @@ -105,7 +105,7 @@ int FakeDecodeFromFile::DecodeInternal(const uint8_t* encoded, void FakeDecodeFromFile::PrepareEncoded(uint32_t timestamp, size_t samples, size_t original_payload_size_bytes, - ArrayView encoded) { + std::span encoded) { RTC_CHECK_GE(encoded.size(), 12); ByteWriter::WriteLittleEndian(&encoded[0], timestamp); ByteWriter::WriteLittleEndian(&encoded[4], diff --git a/modules/audio_coding/neteq/tools/fake_decode_from_file.h b/modules/audio_coding/neteq/tools/fake_decode_from_file.h index 39e4a5eb744..01c73c45aaf 100644 --- a/modules/audio_coding/neteq/tools/fake_decode_from_file.h +++ b/modules/audio_coding/neteq/tools/fake_decode_from_file.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder.h" #include "modules/audio_coding/neteq/tools/input_audio_file.h" #include "rtc_base/buffer.h" @@ -68,7 +68,7 @@ class FakeDecodeFromFile : public AudioDecoder { static void PrepareEncoded(uint32_t timestamp, size_t samples, size_t original_payload_size_bytes, - ArrayView encoded); + std::span encoded); private: std::unique_ptr input_; diff --git a/modules/audio_coding/neteq/tools/neteq_event_log_input.cc b/modules/audio_coding/neteq/tools/neteq_event_log_input.cc index 66494d57412..f7834c6d679 100644 --- a/modules/audio_coding/neteq/tools/neteq_event_log_input.cc +++ b/modules/audio_coding/neteq/tools/neteq_event_log_input.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/events/rtc_event_audio_playout.h" @@ -130,7 +130,7 @@ class NetEqEventLogInput : public NetEqInput { packet_data->SetTimestamp(logged.header.timestamp); packet_data->SetSsrc(logged.header.ssrc); packet_data->SetCsrcs( - MakeArrayView(logged.header.arrOfCSRCs, logged.header.numCSRCs)); + std::span(logged.header.arrOfCSRCs, logged.header.numCSRCs)); packet_data->set_arrival_time(logged.log_time()); // This is a header-only "dummy" packet. Set the payload to all zeros, with diff --git a/modules/audio_coding/neteq/tools/neteq_quality_test.cc b/modules/audio_coding/neteq/tools/neteq_quality_test.cc index 68e4fac1dcc..666d2e993e3 100644 --- a/modules/audio_coding/neteq/tools/neteq_quality_test.cc +++ b/modules/audio_coding/neteq/tools/neteq_quality_test.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,6 @@ #include "absl/flags/flag.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_format.h" #include "api/environment/environment_factory.h" @@ -434,7 +434,7 @@ int NetEqQualityTest::Transmit() { if (!PacketLost()) { int ret = neteq_->InsertPacket( rtp_header_, - ArrayView(payload_.data(), payload_size_bytes_), + std::span(payload_.data(), payload_size_bytes_), Timestamp::Millis(packet_input_time_ms)); if (ret != NetEq::kOK) return -1; diff --git a/modules/audio_coding/neteq/tools/neteq_test.cc b/modules/audio_coding/neteq/tools/neteq_test.cc index 57782b1826a..adf882025a6 100644 --- a/modules/audio_coding/neteq/tools/neteq_test.cc +++ b/modules/audio_coding/neteq/tools/neteq_test.cc @@ -19,14 +19,12 @@ #include #include -#include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_format.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" -#include "api/field_trials.h" +#include "api/field_trials_view.h" #include "api/neteq/default_neteq_factory.h" #include "api/neteq/neteq.h" #include "api/neteq/neteq_factory.h" @@ -91,11 +89,12 @@ NetEqTest::NetEqTest(const NetEq::Config& config, std::unique_ptr input, std::unique_ptr output, Callbacks callbacks, - absl::string_view field_trials) + const FieldTrialsView* field_trials) : input_(std::move(input)), clock_(Timestamp::Millis(input_->NextEventTime().value_or(0))), - env_(CreateEnvironment(&clock_, - std::make_unique(field_trials))), + env_(CreateEnvironment( + &clock_, + field_trials ? field_trials->CreateCopy() : nullptr)), neteq_( neteq_factory ? neteq_factory->Create(env_, config, std::move(decoder_factory)) diff --git a/modules/audio_coding/neteq/tools/neteq_test.h b/modules/audio_coding/neteq/tools/neteq_test.h index e0cf777fd11..0fddb2a25df 100644 --- a/modules/audio_coding/neteq/tools/neteq_test.h +++ b/modules/audio_coding/neteq/tools/neteq_test.h @@ -17,10 +17,10 @@ #include #include -#include "absl/strings/string_view.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_format.h" #include "api/environment/environment.h" +#include "api/field_trials_view.h" #include "api/neteq/neteq.h" #include "api/neteq/neteq_factory.h" #include "api/scoped_refptr.h" @@ -92,7 +92,7 @@ class NetEqTest : public NetEqSimulator { std::unique_ptr input, std::unique_ptr output, Callbacks callbacks, - absl::string_view field_trials = ""); + const FieldTrialsView* field_trials = nullptr); ~NetEqTest() override; diff --git a/modules/audio_coding/neteq/tools/neteq_test_factory.cc b/modules/audio_coding/neteq/tools/neteq_test_factory.cc index 8b6f14a5e31..ced6030f4da 100644 --- a/modules/audio_coding/neteq/tools/neteq_test_factory.cc +++ b/modules/audio_coding/neteq/tools/neteq_test_factory.cc @@ -28,6 +28,7 @@ #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/environment/environment.h" +#include "api/field_trials.h" #include "api/make_ref_counted.h" #include "api/neteq/neteq.h" #include "api/neteq/neteq_factory.h" @@ -347,10 +348,10 @@ std::unique_ptr NetEqTestFactory::InitializeTest( neteq_config.sample_rate_hz = *sample_rate_hz; neteq_config.max_packets_in_buffer = config.max_nr_packets_in_buffer; neteq_config.enable_fast_accelerate = config.enable_fast_accelerate; - return std::make_unique(neteq_config, decoder_factory, codecs, - std::move(text_log), factory, - std::move(input), std::move(output), - callbacks, config.field_trial_string); + FieldTrials field_trials_view(config.field_trial_string); + return std::make_unique( + neteq_config, decoder_factory, codecs, std::move(text_log), factory, + std::move(input), std::move(output), callbacks, &field_trials_view); } } // namespace test diff --git a/modules/audio_coding/neteq/tools/rtp_jitter.cc b/modules/audio_coding/neteq/tools/rtp_jitter.cc index acf27445baa..03b346c645e 100644 --- a/modules/audio_coding/neteq/tools/rtp_jitter.cc +++ b/modules/audio_coding/neteq/tools/rtp_jitter.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -32,7 +32,7 @@ constexpr size_t kRtpDumpHeaderLength = 8; Buffer ReadNextPacket(FILE* file) { // Read the rtpdump header for the next packet. Buffer buffer; - buffer.SetData(kRtpDumpHeaderLength, [&](ArrayView x) { + buffer.SetData(kRtpDumpHeaderLength, [&](std::span x) { return fread(x.data(), 1, x.size(), file); }); if (buffer.size() != kRtpDumpHeaderLength) { @@ -45,7 +45,7 @@ Buffer ReadNextPacket(FILE* file) { RTC_CHECK_GE(len, kRtpDumpHeaderLength); // Read remaining data from file directly into buffer. - buffer.AppendData(len - kRtpDumpHeaderLength, [&](ArrayView x) { + buffer.AppendData(len - kRtpDumpHeaderLength, [&](std::span x) { return fread(x.data(), 1, x.size(), file); }); if (buffer.size() != len) { diff --git a/modules/audio_coding/test/Channel.cc b/modules/audio_coding/test/Channel.cc index 5d3c2192d3f..e2ebb90074a 100644 --- a/modules/audio_coding/test/Channel.cc +++ b/modules/audio_coding/test/Channel.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/neteq/neteq.h" #include "api/rtp_headers.h" #include "api/units/timestamp.h" @@ -92,7 +92,7 @@ int32_t Channel::SendData(AudioFrameType frameType, } status = _neteq->InsertPacket( - rtp_header, ArrayView(_payloadData, payloadDataSize), + rtp_header, std::span(_payloadData, payloadDataSize), /*receive_time=*/Timestamp::MinusInfinity()); return status; diff --git a/modules/audio_coding/test/EncodeDecodeTest.cc b/modules/audio_coding/test/EncodeDecodeTest.cc index 848e833f429..e61d584eec2 100644 --- a/modules/audio_coding/test/EncodeDecodeTest.cc +++ b/modules/audio_coding/test/EncodeDecodeTest.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" @@ -182,7 +182,7 @@ bool Receiver::IncomingPacket() { EXPECT_GE( 0, _neteq->InsertPacket(_rtpHeader, - ArrayView(_incomingPayload, + std::span(_incomingPayload, _realPayloadSizeBytes), /*receive_time=*/Timestamp::Millis(_nextTime))); _realPayloadSizeBytes = _rtpStream->Read(&_rtpHeader, _incomingPayload, diff --git a/modules/audio_coding/test/PacketLossTest.cc b/modules/audio_coding/test/PacketLossTest.cc index 59b5309c806..129eebec404 100644 --- a/modules/audio_coding/test/PacketLossTest.cc +++ b/modules/audio_coding/test/PacketLossTest.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" @@ -73,7 +73,7 @@ bool ReceiverWithPacketLoss::IncomingPacket() { if (!PacketLost()) { _neteq->InsertPacket( _rtpHeader, - ArrayView(_incomingPayload, _realPayloadSizeBytes), + std::span(_incomingPayload, _realPayloadSizeBytes), Timestamp::Millis(_nextTime)); } packet_counter_++; diff --git a/modules/audio_coding/test/TestAllCodecs.cc b/modules/audio_coding/test/TestAllCodecs.cc index 0914f0acfeb..689c25868da 100644 --- a/modules/audio_coding/test/TestAllCodecs.cc +++ b/modules/audio_coding/test/TestAllCodecs.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include "absl/strings/match.h" #include "absl/strings/str_cat.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" @@ -48,12 +48,12 @@ EXPECT_GE(f, 0) << "Error Calling API"; \ } while (0) +namespace webrtc { + namespace { const size_t kVariableSize = std::numeric_limits::max(); } // namespace -namespace webrtc { - // Class for simulating packet handling. TestPack::TestPack() : neteq_(nullptr), @@ -93,7 +93,7 @@ int32_t TestPack::SendData(AudioFrameType frame_type, memcpy(payload_data_, payload_data, payload_size); status = neteq_->InsertPacket( - rtp_header, ArrayView(payload_data_, payload_size), + rtp_header, std::span(payload_data_, payload_size), /*receive_time=*/Timestamp::MinusInfinity()); payload_size_ = payload_size; diff --git a/modules/audio_coding/test/TestStereo.cc b/modules/audio_coding/test/TestStereo.cc index 78b0d8ca74b..f0913f72ecb 100644 --- a/modules/audio_coding/test/TestStereo.cc +++ b/modules/audio_coding/test/TestStereo.cc @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include "absl/strings/match.h" #include "absl/strings/str_cat.h" -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" @@ -78,7 +78,7 @@ int32_t TestPackStereo::SendData(const AudioFrameType frame_type, if (lost_packet_ == false) { status = neteq_->InsertPacket( - rtp_header, ArrayView(payload_data, payload_size), + rtp_header, std::span(payload_data, payload_size), /*receive_time=*/Timestamp::MinusInfinity()); if (frame_type != AudioFrameType::kAudioFrameCN) { diff --git a/modules/audio_coding/test/target_delay_unittest.cc b/modules/audio_coding/test/target_delay_unittest.cc index 96130b49d6d..bf72bcd8a67 100644 --- a/modules/audio_coding/test/target_delay_unittest.cc +++ b/modules/audio_coding/test/target_delay_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" @@ -95,7 +95,7 @@ class TargetDelayTest : public ::testing::Test { rtp_header_.sequenceNumber++; ASSERT_EQ(0, neteq_->InsertPacket( rtp_header_, - ArrayView(payload_, kFrameSizeSamples * 2), + std::span(payload_, kFrameSizeSamples * 2), Timestamp::MinusInfinity())); } diff --git a/modules/audio_device/BUILD.gn b/modules/audio_device/BUILD.gn index cfce21967e5..631924fbdd0 100644 --- a/modules/audio_device/BUILD.gn +++ b/modules/audio_device/BUILD.gn @@ -58,7 +58,6 @@ rtc_library("audio_device_buffer") { "fine_audio_buffer.h", ] deps = [ - "../../api:array_view", "../../api:sequence_checker", "../../api/audio:audio_device", "../../api/environment", @@ -75,7 +74,6 @@ rtc_library("audio_device_buffer") { "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:no_unique_address", - "../../system_wrappers", "../../system_wrappers:metrics", ] } @@ -88,7 +86,6 @@ rtc_library("audio_device_generic") { deps = [ ":audio_device_buffer", "../../api/audio:audio_device", - "../../rtc_base:logging", ] } @@ -149,7 +146,6 @@ rtc_library("audio_device_module_from_input_and_output") { ":audio_device_buffer", ":audio_device_name", ":windows_core_audio_utility", - "../../api:array_view", "../../api:make_ref_counted", "../../api:scoped_refptr", "../../api:sequence_checker", @@ -186,7 +182,6 @@ if (!build_with_chromium) { ":audio_device_default", ":audio_device_generic", ":audio_device_impl", - "../../api:array_view", "../../api:make_ref_counted", "../../api:scoped_refptr", "../../api/audio:audio_device", @@ -196,11 +191,8 @@ if (!build_with_chromium) { "../../common_audio", "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:logging", "../../rtc_base:macromagic", - "../../rtc_base:platform_thread", "../../rtc_base:random", - "../../rtc_base:rtc_event", "../../rtc_base:safe_conversions", "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", @@ -247,7 +239,6 @@ if (!build_with_chromium) { "../../rtc_base:platform_thread", "../../rtc_base:stringutils", "../../rtc_base:threading", - "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:file_wrapper", "//third_party/abseil-cpp/absl/strings:string_view", @@ -264,7 +255,6 @@ rtc_library("audio_device_impl") { ":audio_device_default", ":audio_device_dummy", ":audio_device_generic", - "../../api:array_view", "../../api:make_ref_counted", "../../api:ref_count", "../../api:refcountedbase", @@ -468,21 +458,17 @@ if (rtc_include_tests && !build_with_chromium) { "test_audio_device_impl_test.cc", ] deps = [ - ":audio_device", ":audio_device_buffer", ":audio_device_generic", ":audio_device_impl", ":mock_audio_device", ":test_audio_device_module", - "../../api:array_view", "../../api:scoped_refptr", "../../api:sequence_checker", "../../api/audio:audio_device", "../../api/audio:create_audio_device_module", "../../api/environment", "../../api/environment:environment_factory", - "../../api/task_queue", - "../../api/task_queue:default_task_queue_factory", "../../api/units:time_delta", "../../api/units:timestamp", "../../common_audio", @@ -496,7 +482,6 @@ if (rtc_include_tests && !build_with_chromium) { "../../rtc_base:task_queue_for_test", "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", - "../../system_wrappers", "../../test:create_test_environment", "../../test:fileutils", "../../test:test_support", diff --git a/modules/audio_device/DEPS b/modules/audio_device/DEPS index b0571deb0ea..380f3fd9286 100644 --- a/modules/audio_device/DEPS +++ b/modules/audio_device/DEPS @@ -4,10 +4,10 @@ include_rules = [ ] specific_include_rules = { - "ensure_initialized\.cc": [ + "ensure_initialized\\.cc": [ "+sdk/android", ], - "audio_device_impl\.cc": [ + "audio_device_impl\\.cc": [ "+sdk/objc", "+sdk/android", ], diff --git a/modules/audio_device/audio_device_buffer.cc b/modules/audio_device/audio_device_buffer.cc index b687ee9be86..4e9e350a098 100644 --- a/modules/audio_device/audio_device_buffer.cc +++ b/modules/audio_device/audio_device_buffer.cc @@ -10,6 +10,7 @@ #include "modules/audio_device/audio_device_buffer.h" +#include #include #include #include @@ -53,7 +54,7 @@ AudioDeviceBuffer::AudioDeviceBuffer(const Environment& env, : env_(env), task_queue_(env_.task_queue_factory().CreateTaskQueue( kTimerQueueName, - TaskQueueFactory::Priority::NORMAL)), + TaskQueueFactory::Priority::kNormal)), audio_transport_cb_(nullptr), rec_sample_rate_(0), play_sample_rate_(0), @@ -522,9 +523,8 @@ void AudioDeviceBuffer::LogStats(LogState state) { } last_stats_ = stats; - int64_t time_to_wait_ms = - next_callback_time - env_.clock().TimeInMilliseconds(); - RTC_DCHECK_GT(time_to_wait_ms, 0) << "Invalid timer interval"; + int64_t time_to_wait_ms = std::max( + next_callback_time - env_.clock().TimeInMilliseconds(), 0); // Keep posting new (delayed) tasks until state is changed to kLogStop. task_queue_->PostDelayedTask( diff --git a/modules/audio_device/audio_device_unittest.cc b/modules/audio_device/audio_device_unittest.cc index ef93f7bc4c9..9dc10ea6489 100644 --- a/modules/audio_device/audio_device_unittest.cc +++ b/modules/audio_device/audio_device_unittest.cc @@ -18,9 +18,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_device_defines.h" #include "api/audio/create_audio_device_module.h" #include "api/environment/environment.h" @@ -113,8 +113,8 @@ enum class TransportType { // measurements. class AudioStream { public: - virtual void Write(ArrayView source) = 0; - virtual void Read(ArrayView destination) = 0; + virtual void Write(std::span source) = 0; + virtual void Read(std::span destination) = 0; virtual ~AudioStream() = default; }; @@ -141,7 +141,7 @@ int IndexToMilliseconds(size_t index, size_t frames_per_10ms_buffer) { // change over time and that both sides will in most cases use the same size. class FifoAudioStream : public AudioStream { public: - void Write(ArrayView source) override { + void Write(std::span source) override { RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); const size_t size = [&] { MutexLock lock(&lock_); @@ -158,7 +158,7 @@ class FifoAudioStream : public AudioStream { written_elements_ += size; } - void Read(ArrayView destination) override { + void Read(std::span destination) override { MutexLock lock(&lock_); if (fifo_.empty()) { std::fill(destination.begin(), destination.end(), 0); @@ -227,7 +227,7 @@ class LatencyAudioStream : public AudioStream { } // Insert periodic impulses in first two samples of `destination`. - void Read(ArrayView destination) override { + void Read(std::span destination) override { RTC_DCHECK_RUN_ON(&read_thread_checker_); if (read_count_ == 0) { PRINT("["); @@ -249,7 +249,7 @@ class LatencyAudioStream : public AudioStream { // Detect received impulses in `source`, derive time between transmission and // detection and add the calculated delay to list of latencies. - void Write(ArrayView source) override { + void Write(std::span source) override { RTC_DCHECK_RUN_ON(&write_thread_checker_); RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); MutexLock lock(&lock_); @@ -402,9 +402,8 @@ class MockAudioTransport : public test::MockAudioTransport { } // Write audio data to audio stream object if one has been injected. if (audio_stream_) { - audio_stream_->Write( - MakeArrayView(static_cast(audio_buffer), - samples_per_channel * channels)); + audio_stream_->Write(std::span(static_cast(audio_buffer), + samples_per_channel * channels)); } // Signal the event after given amount of callbacks. if (event_ && ReceivedEnoughCallbacks()) { @@ -443,8 +442,8 @@ class MockAudioTransport : public test::MockAudioTransport { samples_out = samples_per_channel * channels; // Read audio data from audio stream object if one has been injected. if (audio_stream_) { - audio_stream_->Read(MakeArrayView(static_cast(audio_buffer), - samples_per_channel * channels)); + audio_stream_->Read(std::span(static_cast(audio_buffer), + samples_per_channel * channels)); } else { // Fill the audio buffer with zeros to avoid disturbing audio. const size_t num_bytes = samples_per_channel * bytes_per_frame; diff --git a/modules/audio_device/audio_engine_device.h b/modules/audio_device/audio_engine_device.h index b9a8c26e542..f5ceab9c6cc 100644 --- a/modules/audio_device/audio_engine_device.h +++ b/modules/audio_device/audio_engine_device.h @@ -122,6 +122,11 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool output_enabled = false; bool output_running = false; + // Default true: the engine may activate on init. + // Set false to gate start/stop (e.g. CallKit). + bool output_available = true; + bool input_available = true; + // Output will be enabled when input is enabled bool input_follow_mode = true; bool input_enabled_persistent_mode = false; @@ -151,6 +156,7 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool operator==(const EngineState& rhs) const { return input_enabled == rhs.input_enabled && input_running == rhs.input_running && output_enabled == rhs.output_enabled && output_running == rhs.output_running && + input_available == rhs.input_available && output_available == rhs.output_available && input_follow_mode == rhs.input_follow_mode && input_enabled_persistent_mode == rhs.input_enabled_persistent_mode && input_muted == rhs.input_muted && is_interrupted == rhs.is_interrupted && @@ -172,32 +178,31 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool IsOutputInputLinked() const { return input_follow_mode && voice_processing_enabled; } bool IsOutputEnabled() const { - return IsOutputInputLinked() ? (IsInputEnabled() || output_enabled) : output_enabled; + bool result = IsOutputInputLinked() ? (IsInputEnabled() || output_enabled) : output_enabled; + return output_available && result; } bool IsOutputRunning() const { - return IsOutputInputLinked() ? (IsInputRunning() || output_running) : output_running; + bool result = IsOutputInputLinked() ? (IsInputRunning() || output_running) : output_running; + return output_available && result; } bool IsInputEnabled() const { - return !(mute_mode == MuteMode::RestartEngine && input_muted) && - (input_enabled || input_enabled_persistent_mode); + bool result = !(mute_mode == MuteMode::RestartEngine && input_muted) && + (input_enabled || input_enabled_persistent_mode); + return input_available && result; } bool IsInputRunning() const { - return !(mute_mode == MuteMode::RestartEngine && input_muted) && input_running; + bool result = !(mute_mode == MuteMode::RestartEngine && input_muted) && input_running; + return input_available && result; } bool IsAnyEnabled() const { return IsInputEnabled() || IsOutputEnabled(); } bool IsAnyRunning() const { return IsInputRunning() || IsOutputRunning(); } - bool IsAllEnabled() const { - return IsOutputInputLinked() ? IsInputEnabled() : IsInputEnabled() && output_enabled; - } - - bool IsAllRunning() const { - return IsOutputInputLinked() ? input_running : input_running && output_running; - } + bool IsAllEnabled() const { return IsInputEnabled() && IsOutputEnabled(); } + bool IsAllRunning() const { return IsInputRunning() && IsOutputRunning(); } bool IsOutputDefaultDevice() const { #if TARGET_OS_OSX @@ -318,6 +323,9 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver int32_t SetEngineState(EngineState enable); int32_t GetEngineState(EngineState* enabled); + int32_t SetEngineAvailability(bool input_available, bool output_available); + int32_t EngineAvailability(bool* input_available, bool* output_available); + int32_t SetObserver(AudioDeviceObserver* observer) override; bool IsAudioEngineDevice() const override { return true; } @@ -440,6 +448,7 @@ class AudioEngineDevice : public AudioDeviceModule, public AudioSessionObserver bool IsMicrophonePermissionGranted(); void ResetEngineState(); int32_t ModifyEngineState(std::function state_transform); + int32_t ApplyDeviceEngineState(EngineStateUpdate& state); int32_t ApplyManualEngineState(EngineStateUpdate& state); diff --git a/modules/audio_device/audio_engine_device.mm b/modules/audio_device/audio_engine_device.mm index 18b417404f2..270e030d9df 100644 --- a/modules/audio_device/audio_engine_device.mm +++ b/modules/audio_device/audio_engine_device.mm @@ -1260,6 +1260,33 @@ return result; } +int32_t AudioEngineDevice::SetEngineAvailability(bool input_available, bool output_available) { + RTC_DCHECK_RUN_ON(thread_); + LOGI() << "SetEngineAvailability: " << input_available << " " << output_available; + + int32_t result = + ModifyEngineState([input_available, output_available](EngineState state) -> EngineState { + state.input_available = input_available; + state.output_available = output_available; + return state; + }); + + return result; +} + +int32_t AudioEngineDevice::EngineAvailability(bool* input_available, bool* output_available) { + RTC_DCHECK_RUN_ON(thread_); + + if (input_available == nullptr || output_available == nullptr) { + return -1; + } + + *input_available = engine_state_.input_available; + *output_available = engine_state_.output_available; + + return 0; +} + int32_t AudioEngineDevice::ManualRenderingMode(bool* enabled) { LOGI() << "ManualRenderingMode"; RTC_DCHECK_RUN_ON(thread_); @@ -2816,6 +2843,8 @@ << ", IsAnyRunning: " << state.IsAnyRunning() << ", IsAllEnabled: " << state.IsAllEnabled() << ", IsAllRunning: " << state.IsAllRunning() + << ", InputAvailable: " << state.input_available + << ", OutputAvailable: " << state.output_available << ", AdvancedDucking: " << state.advanced_ducking << ", DuckingLevel: " << state.ducking_level << ", MuteMode: " << static_cast(state.mute_mode) diff --git a/modules/audio_device/fine_audio_buffer.cc b/modules/audio_device/fine_audio_buffer.cc index 0ab7023448a..04cf6713a67 100644 --- a/modules/audio_device/fine_audio_buffer.cc +++ b/modules/audio_device/fine_audio_buffer.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_device/audio_device_buffer.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -64,7 +64,7 @@ bool FineAudioBuffer::IsReadyForRecord() const { return record_samples_per_channel_10ms_ > 0 && record_channels_ > 0; } -void FineAudioBuffer::GetPlayoutData(ArrayView audio_buffer, +void FineAudioBuffer::GetPlayoutData(std::span audio_buffer, int playout_delay_ms) { RTC_DCHECK(IsReadyForPlayout()); // Ask WebRTC for new data in chunks of 10ms until we have enough to @@ -81,7 +81,7 @@ void FineAudioBuffer::GetPlayoutData(ArrayView audio_buffer, const size_t num_elements_10ms = playout_channels_ * playout_samples_per_channel_10ms_; const size_t written_elements = playout_buffer_.AppendData( - num_elements_10ms, [&](ArrayView buf) { + num_elements_10ms, [&](std::span buf) { const size_t samples_per_channel_10ms = audio_device_buffer_->GetPlayoutData(buf.data()); return playout_channels_ * samples_per_channel_10ms; @@ -108,7 +108,7 @@ void FineAudioBuffer::GetPlayoutData(ArrayView audio_buffer, } void FineAudioBuffer::DeliverRecordedData( - ArrayView audio_buffer, + std::span audio_buffer, int record_delay_ms, std::optional capture_time_ns) { RTC_DCHECK(IsReadyForRecord()); diff --git a/modules/audio_device/fine_audio_buffer.h b/modules/audio_device/fine_audio_buffer.h index dd4d456a686..a2cb3c2a327 100644 --- a/modules/audio_device/fine_audio_buffer.h +++ b/modules/audio_device/fine_audio_buffer.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/buffer.h" namespace webrtc { @@ -52,7 +52,7 @@ class FineAudioBuffer { // silence instead. The provided delay estimate in `playout_delay_ms` should // contain an estimate of the latency between when an audio frame is read from // WebRTC and when it is played out on the speaker. - void GetPlayoutData(ArrayView audio_buffer, int playout_delay_ms); + void GetPlayoutData(std::span audio_buffer, int playout_delay_ms); // Consumes the audio data in `audio_buffer` and sends it to the WebRTC layer // in chunks of 10ms. The sum of the provided delay estimate in @@ -63,11 +63,11 @@ class FineAudioBuffer { // Example: buffer size is 5ms => call #1 stores 5ms of data, call #2 stores // 5ms of data and sends a total of 10ms to WebRTC and clears the internal // cache. Call #3 restarts the scheme above. - void DeliverRecordedData(ArrayView audio_buffer, + void DeliverRecordedData(std::span audio_buffer, int record_delay_ms) { DeliverRecordedData(audio_buffer, record_delay_ms, std::nullopt); } - void DeliverRecordedData(ArrayView audio_buffer, + void DeliverRecordedData(std::span audio_buffer, int record_delay_ms, std::optional capture_time_ns); diff --git a/modules/audio_device/fine_audio_buffer_unittest.cc b/modules/audio_device/fine_audio_buffer_unittest.cc index 2ec0a537960..2516b5f0c34 100644 --- a/modules/audio_device/fine_audio_buffer_unittest.cc +++ b/modules/audio_device/fine_audio_buffer_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_device/mock_audio_device_buffer.h" #include "test/create_test_environment.h" #include "test/gmock.h" @@ -131,12 +131,12 @@ void RunFineBufferTest(int frame_size_in_samples) { for (int i = 0; i < kNumberOfFrames; ++i) { fine_buffer.GetPlayoutData( - ArrayView(out_buffer.get(), kChannels * kFrameSizeSamples), 0); + std::span(out_buffer.get(), kChannels * kFrameSizeSamples), 0); EXPECT_TRUE( VerifyBuffer(out_buffer.get(), i, kChannels * kFrameSizeSamples)); UpdateInputBuffer(in_buffer.get(), i, kChannels * kFrameSizeSamples); fine_buffer.DeliverRecordedData( - ArrayView(in_buffer.get(), + std::span(in_buffer.get(), kChannels * kFrameSizeSamples), 0); } diff --git a/modules/audio_device/include/test_audio_device.cc b/modules/audio_device/include/test_audio_device.cc index 06c43cca922..0e74c722a65 100644 --- a/modules/audio_device/include/test_audio_device.cc +++ b/modules/audio_device/include/test_audio_device.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,8 @@ #include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/audio/audio_device.h" +#include "api/environment/environment.h" #include "api/make_ref_counted.h" #include "api/task_queue/task_queue_factory.h" #include "common_audio/wav_file.h" @@ -434,7 +437,7 @@ class PulsedNoiseCapturerImpl final buffer->SetData( TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz_) * num_channels_, - [&](ArrayView data) { + [&](std::span data) { if (fill_with_zero_) { std::fill(data.begin(), data.end(), 0); } else { @@ -480,13 +483,13 @@ class WavFileReader final : public TestAudioDeviceModule::Capturer { buffer->SetData( TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz_) * num_channels_, - [&](ArrayView data) { + [&](std::span data) { size_t read = wav_reader_->ReadSamples(data.size(), data.data()); if (read < data.size() && repeat_) { do { wav_reader_->Reset(); size_t delta = wav_reader_->ReadSamples( - data.size() - read, data.subview(read).data()); + data.size() - read, data.subspan(read).data()); RTC_CHECK_GT(delta, 0) << "No new data read from file"; read += delta; } while (read < data.size()); @@ -530,7 +533,7 @@ class WavFileWriter final : public TestAudioDeviceModule::Renderer { int NumChannels() const override { return num_channels_; } - bool Render(ArrayView data) override { + bool Render(std::span data) override { wav_writer_->WriteSamples(data.data(), data.size()); return true; } @@ -567,11 +570,11 @@ class BoundedWavFileWriter : public TestAudioDeviceModule::Renderer { int NumChannels() const override { return num_channels_; } - bool Render(ArrayView data) override { + bool Render(std::span data) override { const int16_t kAmplitudeThreshold = 5; - const int16_t* begin = data.begin(); - const int16_t* end = data.end(); + const int16_t* begin = data.data(); + const int16_t* end = data.data() + data.size(); if (!started_writing_) { // Cut off silence at the beginning. while (begin < end) { @@ -602,7 +605,7 @@ class BoundedWavFileWriter : public TestAudioDeviceModule::Renderer { wav_writer_.WriteSamples(begin, end - begin); } // Save the number of zeros we skipped in case this needs to be restored. - trailing_zeros_ += data.end() - end; + trailing_zeros_ += (data.data() + data.size()) - end; } return true; } @@ -626,7 +629,7 @@ class DiscardRenderer final : public TestAudioDeviceModule::Renderer { int NumChannels() const override { return num_channels_; } - bool Render(ArrayView /* data */) override { return true; } + bool Render(std::span /* data */) override { return true; } private: int sampling_frequency_in_hz_; @@ -662,15 +665,15 @@ class RawFileReader final : public TestAudioDeviceModule::Capturer { buffer->SetData( TestAudioDeviceModule::SamplesPerFrame(SamplingFrequency()) * NumChannels(), - [&](ArrayView data) { - ArrayView read_buffer_view = ReadBufferView(); + [&](std::span data) { + std::span read_buffer_view = ReadBufferView(); size_t size = data.size() * 2; size_t read = input_file_.Read(read_buffer_view.data(), size); if (read < size && repeat_) { do { input_file_.Rewind(); size_t delta = input_file_.Read( - read_buffer_view.subview(read).data(), size - read); + read_buffer_view.subspan(read).data(), size - read); RTC_CHECK_GT(delta, 0) << "No new data to read from file"; read += delta; } while (read < size); @@ -682,7 +685,7 @@ class RawFileReader final : public TestAudioDeviceModule::Capturer { } private: - ArrayView ReadBufferView() { return read_buffer_; } + std::span ReadBufferView() { return read_buffer_; } const std::string input_file_name_; const int sampling_frequency_in_hz_; @@ -720,11 +723,11 @@ class RawFileWriter : public TestAudioDeviceModule::Renderer { int NumChannels() const override { return num_channels_; } - bool Render(ArrayView data) override { + bool Render(std::span data) override { const int16_t kAmplitudeThreshold = 5; - const int16_t* begin = data.begin(); - const int16_t* end = data.end(); + const int16_t* begin = data.data(); + const int16_t* end = data.data() + data.size(); if (!started_writing_) { // Cut off silence at the beginning. while (begin < end) { @@ -755,7 +758,7 @@ class RawFileWriter : public TestAudioDeviceModule::Renderer { WriteInt16(begin, end); } // Save the number of zeros we skipped in case this needs to be restored. - trailing_zeros_ += data.end() - end; + trailing_zeros_ += (data.data() + data.size()) - end; } return true; } diff --git a/modules/audio_device/include/test_audio_device.h b/modules/audio_device/include/test_audio_device.h index 79694846a68..e460d25d159 100644 --- a/modules/audio_device/include/test_audio_device.h +++ b/modules/audio_device/include/test_audio_device.h @@ -13,9 +13,9 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/environment/environment.h" #include "api/scoped_refptr.h" @@ -59,7 +59,7 @@ class TestAudioDeviceModule { virtual int NumChannels() const = 0; // Renders the passed audio data and returns true if the renderer wants // to keep receiving data, or false otherwise. - virtual bool Render(ArrayView data) = 0; + virtual bool Render(std::span data) = 0; }; // A fake capturer that generates pulses with random samples between diff --git a/modules/audio_device/include/test_audio_device_unittest.cc b/modules/audio_device/include/test_audio_device_unittest.cc index 95eb74eb625..1ad4b9dbd87 100644 --- a/modules/audio_device/include/test_audio_device_unittest.cc +++ b/modules/audio_device/include/test_audio_device_unittest.cc @@ -20,11 +20,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/audio/audio_device_defines.h" #include "api/environment/environment.h" @@ -65,7 +65,7 @@ void RunWavTest(const std::vector& input_samples, TestAudioDeviceModule::CreateBoundedWavFileWriter(output_filename, 800); for (size_t i = 0; i < input_samples.size(); i += kSamplesPerFrame) { - EXPECT_TRUE(writer->Render(ArrayView( + EXPECT_TRUE(writer->Render(std::span( &input_samples[i], std::min(kSamplesPerFrame, input_samples.size() - i)))); } @@ -162,7 +162,7 @@ TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) { TestAudioDeviceModule::CreateWavFileWriter(output_filename, 800); for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) { - EXPECT_TRUE(writer->Render(ArrayView( + EXPECT_TRUE(writer->Render(std::span( &kInputSamples[i], std::min(kSamplesPerFrame, kInputSamples.size() - i)))); } @@ -203,7 +203,7 @@ void RunRawTestNoRepeat(const std::vector& input_samples, output_filename, /*sampling_frequency_in_hz=*/800); for (size_t i = 0; i < input_samples.size(); i += kSamplesPerFrame) { - EXPECT_TRUE(writer->Render(ArrayView( + EXPECT_TRUE(writer->Render(std::span( &input_samples[i], std::min(kSamplesPerFrame, input_samples.size() - i)))); } @@ -311,7 +311,7 @@ TEST(RawFileWriterTest, Repeat) { output_filename, /*sampling_frequency_in_hz=*/800); for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) { - EXPECT_TRUE(writer->Render(ArrayView( + EXPECT_TRUE(writer->Render(std::span( &kInputSamples[i], std::min(kSamplesPerFrame, kInputSamples.size() - i)))); } diff --git a/modules/audio_device/linux/audio_device_alsa_linux.cc b/modules/audio_device/linux/audio_device_alsa_linux.cc index 4e504f354d0..731d5b161d9 100644 --- a/modules/audio_device/linux/audio_device_alsa_linux.cc +++ b/modules/audio_device/linux/audio_device_alsa_linux.cc @@ -34,7 +34,11 @@ #endif WebRTCAlsaSymbolTable* GetAlsaSymbolTable() { - static WebRTCAlsaSymbolTable* alsa_symbol_table = new WebRTCAlsaSymbolTable(); + static WebRTCAlsaSymbolTable* alsa_symbol_table = []() { + auto* table = new WebRTCAlsaSymbolTable(); + table->Load(); + return table; + }(); return alsa_symbol_table; } @@ -161,7 +165,7 @@ AudioDeviceGeneric::InitStatus AudioDeviceLinuxALSA::Init() { MutexLock lock(&mutex_); // Load libasound - if (!GetAlsaSymbolTable()->Load()) { + if (!GetAlsaSymbolTable()->IsLoaded()) { // Alsa is not installed on this system RTC_LOG(LS_ERROR) << "failed to load symbol table"; return InitStatus::OTHER_ERROR; diff --git a/modules/audio_device/linux/audio_device_pulse_linux.cc b/modules/audio_device/linux/audio_device_pulse_linux.cc index a098e908ff9..f1121c1a9aa 100644 --- a/modules/audio_device/linux/audio_device_pulse_linux.cc +++ b/modules/audio_device/linux/audio_device_pulse_linux.cc @@ -31,8 +31,11 @@ #endif WebRTCPulseSymbolTable* GetPulseSymbolTable() { - static WebRTCPulseSymbolTable* pulse_symbol_table = - new WebRTCPulseSymbolTable(); + static WebRTCPulseSymbolTable* pulse_symbol_table = []() { + auto* table = new WebRTCPulseSymbolTable(); + table->Load(); + return table; + }(); return pulse_symbol_table; } @@ -1555,7 +1558,7 @@ int32_t AudioDeviceLinuxPulse::InitPulseAudio() { int retVal = 0; // Load libpulse - if (!GetPulseSymbolTable()->Load()) { + if (!GetPulseSymbolTable()->IsLoaded()) { // Most likely the Pulse library and sound server are not installed on // this system RTC_LOG(LS_ERROR) << "failed to load symbol table"; diff --git a/modules/audio_device/mac/audio_device_mac.cc b/modules/audio_device/mac/audio_device_mac.cc index d175fa9bf88..5ff407c33cf 100644 --- a/modules/audio_device/mac/audio_device_mac.cc +++ b/modules/audio_device/mac/audio_device_mac.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include "modules/third_party/portaudio/pa_ringbuffer.h" @@ -847,8 +848,8 @@ int32_t AudioDeviceMac::PlayoutDeviceName(uint16_t index, memset(guid, 0, kAdmMaxGuidSize); return GetDeviceName(kAudioDevicePropertyScopeOutput, index, - webrtc::ArrayView(name, kAdmMaxDeviceNameSize), - webrtc::ArrayView(guid, kAdmMaxGuidSize)); + std::span(name, kAdmMaxDeviceNameSize), + std::span(guid, kAdmMaxGuidSize)); } int32_t AudioDeviceMac::RecordingDeviceName(uint16_t index, @@ -867,8 +868,8 @@ int32_t AudioDeviceMac::RecordingDeviceName(uint16_t index, } return GetDeviceName(kAudioDevicePropertyScopeInput, index, - webrtc::ArrayView(name, kAdmMaxDeviceNameSize), - webrtc::ArrayView(guid, kAdmMaxGuidSize)); + std::span(name, kAdmMaxDeviceNameSize), + std::span(guid, kAdmMaxGuidSize)); } int16_t AudioDeviceMac::RecordingDevices() { @@ -1646,8 +1647,8 @@ int32_t AudioDeviceMac::GetNumberDevices(const AudioObjectPropertyScope scope, int32_t AudioDeviceMac::GetDeviceName(const AudioObjectPropertyScope scope, const uint16_t index, - webrtc::ArrayView name, - webrtc::ArrayView guid) { + std::span name, + std::span guid) { OSStatus err = noErr; AudioDeviceID deviceIds[MaxNumberDevices]; diff --git a/modules/audio_device/mac/audio_device_mac.h b/modules/audio_device/mac/audio_device_mac.h index d5553e9cb46..1d413f8e114 100644 --- a/modules/audio_device/mac/audio_device_mac.h +++ b/modules/audio_device/mac/audio_device_mac.h @@ -17,6 +17,7 @@ #include #include +#include #include "absl/strings/string_view.h" #include "modules/audio_device/audio_device_generic.h" @@ -187,8 +188,8 @@ class AudioDeviceMac : public AudioDeviceGeneric { int32_t GetDeviceName(AudioObjectPropertyScope scope, uint16_t index, - webrtc::ArrayView name, - webrtc::ArrayView guid); + std::span name, + std::span guid); int32_t InitDevice(uint16_t userDeviceIndex, AudioDeviceID& deviceId, diff --git a/modules/audio_device/test_audio_device_impl.cc b/modules/audio_device/test_audio_device_impl.cc index 80402d3ea32..33f59a8539c 100644 --- a/modules/audio_device/test_audio_device_impl.cc +++ b/modules/audio_device/test_audio_device_impl.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/task_queue/task_queue_factory.h" #include "api/units/time_delta.h" @@ -64,7 +64,7 @@ TestAudioDevice::TestAudioDevice( AudioDeviceGeneric::InitStatus TestAudioDevice::Init() { task_queue_ = env_.task_queue_factory().CreateTaskQueue( - "TestAudioDeviceModuleImpl", TaskQueueFactory::Priority::NORMAL); + "TestAudioDeviceModuleImpl", TaskQueueFactory::Priority::kNormal); RepeatingTaskHandle::Start(task_queue_.get(), [this]() { ProcessAudio(); @@ -189,7 +189,7 @@ void TestAudioDevice::ProcessAudio() { size_t samples_out = samples_per_channel * renderer_->NumChannels(); RTC_CHECK_LE(samples_out, playout_buffer_.size()); const bool keep_rendering = renderer_->Render( - ArrayView(playout_buffer_.data(), samples_out)); + std::span(playout_buffer_.data(), samples_out)); if (!keep_rendering) { rendering_ = false; } diff --git a/modules/audio_device/win/core_audio_input_win.cc b/modules/audio_device/win/core_audio_input_win.cc index f6139aefb39..3b92e586f45 100644 --- a/modules/audio_device/win/core_audio_input_win.cc +++ b/modules/audio_device/win/core_audio_input_win.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/environment/environment.h" #include "api/sequence_checker.h" @@ -368,8 +368,8 @@ bool CoreAudioInput::OnDataCallback(uint64_t device_frequency) { // Copy recorded audio in `audio_data` to the WebRTC sink using the // FineAudioBuffer object. fine_audio_buffer_->DeliverRecordedData( - webrtc::MakeArrayView(reinterpret_cast(audio_data), - format_.Format.nChannels * num_frames_to_read), + std::span(reinterpret_cast(audio_data), + format_.Format.nChannels * num_frames_to_read), latency_ms_); } diff --git a/modules/audio_device/win/core_audio_output_win.cc b/modules/audio_device/win/core_audio_output_win.cc index decc0ee6538..f373c40bd1e 100644 --- a/modules/audio_device/win/core_audio_output_win.cc +++ b/modules/audio_device/win/core_audio_output_win.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/environment/environment.h" #include "api/sequence_checker.h" @@ -346,8 +346,8 @@ bool CoreAudioOutput::OnDataCallback(uint64_t device_frequency) { // Get audio data from WebRTC and write it to the allocated buffer in // `audio_data`. The playout latency is not updated for each callback. fine_audio_buffer_->GetPlayoutData( - webrtc::MakeArrayView(reinterpret_cast(audio_data), - num_requested_frames * format_.Format.nChannels), + std::span(reinterpret_cast(audio_data), + num_requested_frames * format_.Format.nChannels), latency_ms_); // Release the buffer space acquired in IAudioRenderClient::GetBuffer. diff --git a/modules/audio_mixer/BUILD.gn b/modules/audio_mixer/BUILD.gn index d2fa8dbaec2..49daca411a2 100644 --- a/modules/audio_mixer/BUILD.gn +++ b/modules/audio_mixer/BUILD.gn @@ -39,28 +39,20 @@ rtc_library("audio_mixer_impl") { deps = [ ":audio_frame_manipulator", - "../../api:array_view", "../../api:make_ref_counted", "../../api:rtp_packet_info", "../../api:scoped_refptr", "../../api/audio:audio_frame_api", "../../api/audio:audio_mixer_api", "../../api/audio:audio_processing", - "../../api/audio:audio_processing", - "../../audio/utility:audio_frame_operations", "../../common_audio", "../../rtc_base:checks", "../../rtc_base:event_tracer", "../../rtc_base:logging", "../../rtc_base:macromagic", - "../../rtc_base:race_checker", - "../../rtc_base:refcount", - "../../rtc_base:safe_conversions", "../../rtc_base/synchronization:mutex", - "../../system_wrappers", "../../system_wrappers:metrics", "../audio_processing:apm_logging", - "../audio_processing:audio_frame_view", "../audio_processing/agc2:fixed_digital", ] } @@ -95,9 +87,6 @@ if (rtc_include_tests) { ] deps = [ - ":audio_frame_manipulator", - ":audio_mixer_impl", - "../../api:array_view", "../../api/audio:audio_frame_api", "../../rtc_base:checks", "../../rtc_base:safe_conversions", @@ -116,7 +105,6 @@ if (rtc_include_tests) { ":audio_frame_manipulator", ":audio_mixer_impl", ":audio_mixer_test_utils", - "../../api:array_view", "../../api:rtp_packet_info", "../../api:scoped_refptr", "../../api/audio:audio_frame_api", diff --git a/modules/audio_mixer/OWNERS b/modules/audio_mixer/OWNERS index 5edc304ab38..6e7454380ea 100644 --- a/modules/audio_mixer/OWNERS +++ b/modules/audio_mixer/OWNERS @@ -1,2 +1 @@ -alessiob@webrtc.org henrik.lundin@webrtc.org diff --git a/modules/audio_mixer/audio_mixer_impl.cc b/modules/audio_mixer/audio_mixer_impl.cc index edf68284e48..e9612790934 100644 --- a/modules/audio_mixer/audio_mixer_impl.cc +++ b/modules/audio_mixer/audio_mixer_impl.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/make_ref_counted.h" #include "api/scoped_refptr.h" @@ -101,7 +101,7 @@ void AudioMixerImpl::Mix(size_t number_of_channels, }); int output_frequency = output_rate_calculator_->CalculateOutputRateFromRange( - ArrayView(helper_containers_->preferred_rates.data(), + std::span(helper_containers_->preferred_rates.data(), number_of_streams)); frame_combiner_.Combine(GetAudioFromSources(output_frequency), @@ -129,7 +129,7 @@ void AudioMixerImpl::RemoveSource(Source* audio_source) { audio_source_list_.erase(iter); } -ArrayView AudioMixerImpl::GetAudioFromSources( +std::span AudioMixerImpl::GetAudioFromSources( int output_frequency) { int audio_to_mix_count = 0; for (auto& source_and_status : audio_source_list_) { @@ -148,7 +148,7 @@ ArrayView AudioMixerImpl::GetAudioFromSources( &source_and_status->audio_frame; } } - return ArrayView(helper_containers_->audio_to_mix.data(), + return std::span(helper_containers_->audio_to_mix.data(), audio_to_mix_count); } diff --git a/modules/audio_mixer/audio_mixer_impl.h b/modules/audio_mixer/audio_mixer_impl.h index 104f1304b7e..c70d6c9ca86 100644 --- a/modules/audio_mixer/audio_mixer_impl.h +++ b/modules/audio_mixer/audio_mixer_impl.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/scoped_refptr.h" @@ -63,7 +63,7 @@ class AudioMixerImpl : public AudioMixer { void UpdateSourceCountStats() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); // Fetches audio frames to mix from sources. - ArrayView GetAudioFromSources(int output_frequency) + std::span GetAudioFromSources(int output_frequency) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); // The critical section lock guards audio source insertion and diff --git a/modules/audio_mixer/audio_mixer_impl_unittest.cc b/modules/audio_mixer/audio_mixer_impl_unittest.cc index 9cd6b424be3..1e240cf09f0 100644 --- a/modules/audio_mixer/audio_mixer_impl_unittest.cc +++ b/modules/audio_mixer/audio_mixer_impl_unittest.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_mixer.h" #include "api/rtp_packet_info.h" @@ -122,7 +122,7 @@ class CustomRateCalculator : public OutputRateCalculator { public: explicit CustomRateCalculator(int rate) : rate_(rate) {} int CalculateOutputRateFromRange( - ArrayView /* preferred_rates */) override { + std::span /* preferred_rates */) override { return rate_; } @@ -484,7 +484,7 @@ class HighOutputRateCalculator : public OutputRateCalculator { public: static const int kDefaultFrequency = 76000; int CalculateOutputRateFromRange( - ArrayView /* preferred_sample_rates */) override { + std::span /* preferred_sample_rates */) override { return kDefaultFrequency; } ~HighOutputRateCalculator() override {} diff --git a/modules/audio_mixer/default_output_rate_calculator.cc b/modules/audio_mixer/default_output_rate_calculator.cc index 6885875caec..8eb6a05f726 100644 --- a/modules/audio_mixer/default_output_rate_calculator.cc +++ b/modules/audio_mixer/default_output_rate_calculator.cc @@ -12,21 +12,21 @@ #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "rtc_base/checks.h" namespace webrtc { int DefaultOutputRateCalculator::CalculateOutputRateFromRange( - ArrayView preferred_sample_rates) { + std::span preferred_sample_rates) { if (preferred_sample_rates.empty()) { return DefaultOutputRateCalculator::kDefaultFrequency; } using NativeRate = AudioProcessing::NativeRate; const int maximal_frequency = *std::max_element( - preferred_sample_rates.cbegin(), preferred_sample_rates.cend()); + preferred_sample_rates.begin(), preferred_sample_rates.end()); RTC_DCHECK_LE(NativeRate::kSampleRate8kHz, maximal_frequency); RTC_DCHECK_GE(NativeRate::kSampleRate48kHz, maximal_frequency); diff --git a/modules/audio_mixer/default_output_rate_calculator.h b/modules/audio_mixer/default_output_rate_calculator.h index acae77fe0a9..e91a2d884de 100644 --- a/modules/audio_mixer/default_output_rate_calculator.h +++ b/modules/audio_mixer/default_output_rate_calculator.h @@ -11,7 +11,8 @@ #ifndef MODULES_AUDIO_MIXER_DEFAULT_OUTPUT_RATE_CALCULATOR_H_ #define MODULES_AUDIO_MIXER_DEFAULT_OUTPUT_RATE_CALCULATOR_H_ -#include "api/array_view.h" +#include + #include "modules/audio_mixer/output_rate_calculator.h" namespace webrtc { @@ -25,7 +26,7 @@ class DefaultOutputRateCalculator : public OutputRateCalculator { // AudioProcessing::NativeRate. If `preferred_sample_rates` is // empty, returns `kDefaultFrequency`. int CalculateOutputRateFromRange( - ArrayView preferred_sample_rates) override; + std::span preferred_sample_rates) override; ~DefaultOutputRateCalculator() override {} }; diff --git a/modules/audio_mixer/frame_combiner.cc b/modules/audio_mixer/frame_combiner.cc index 6f5e1b785a7..7fd5caf377c 100644 --- a/modules/audio_mixer/frame_combiner.cc +++ b/modules/audio_mixer/frame_combiner.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_view.h" #include "api/rtp_packet_info.h" @@ -32,7 +32,7 @@ namespace webrtc { namespace { -void SetAudioFrameFields(ArrayView mix_list, +void SetAudioFrameFields(std::span mix_list, size_t number_of_channels, int sample_rate, size_t /* number_of_streams */, @@ -70,7 +70,7 @@ void SetAudioFrameFields(ArrayView mix_list, } } -void MixFewFramesWithNoLimiter(ArrayView mix_list, +void MixFewFramesWithNoLimiter(std::span mix_list, AudioFrame* audio_frame_for_mixing) { if (mix_list.empty()) { audio_frame_for_mixing->Mute(); @@ -82,7 +82,7 @@ void MixFewFramesWithNoLimiter(ArrayView mix_list, CopySamples(dst, mix_list[0]->data_view()); } -void MixToFloatFrame(ArrayView mix_list, +void MixToFloatFrame(std::span mix_list, DeinterleavedView& mixing_buffer) { const size_t number_of_channels = NumChannels(mixing_buffer); // Clear the mixing buffer. @@ -133,7 +133,7 @@ FrameCombiner::FrameCombiner(bool use_limiter) FrameCombiner::~FrameCombiner() = default; -void FrameCombiner::Combine(ArrayView mix_list, +void FrameCombiner::Combine(std::span mix_list, size_t number_of_channels, int sample_rate, size_t number_of_streams, diff --git a/modules/audio_mixer/frame_combiner.h b/modules/audio_mixer/frame_combiner.h index 74c4547cc8b..c6ee55c8ad5 100644 --- a/modules/audio_mixer/frame_combiner.h +++ b/modules/audio_mixer/frame_combiner.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "modules/audio_processing/agc2/limiter.h" @@ -33,7 +33,7 @@ class FrameCombiner { // because 'mix_list' can be empty. The parameter // 'number_of_streams' is used for determining whether to pass the // data through a limiter. - void Combine(ArrayView mix_list, + void Combine(std::span mix_list, size_t number_of_channels, int sample_rate, size_t number_of_streams, diff --git a/modules/audio_mixer/frame_combiner_unittest.cc b/modules/audio_mixer/frame_combiner_unittest.cc index df01237e07d..e2afc47443e 100644 --- a/modules/audio_mixer/frame_combiner_unittest.cc +++ b/modules/audio_mixer/frame_combiner_unittest.cc @@ -14,12 +14,11 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" -#include "api/audio/audio_view.h" #include "api/audio/channel_layout.h" #include "api/rtp_packet_info.h" #include "api/rtp_packet_infos.h" @@ -146,15 +145,12 @@ TEST(FrameCombiner, ContainsAllRtpPacketInfos) { TEST(FrameCombinerDeathTest, BuildCrashesWithManyChannels) { FrameCombiner combiner(true); for (const int rate : {8000, 18000, 34000, 48000}) { - for (const int number_of_channels : {10, 20, 21}) { - RTC_DCHECK_LE(number_of_channels, kMaxNumberOfAudioChannels); + for (const int number_of_channels : {10, 15, 17}) { if (static_cast(rate / 100 * number_of_channels) > AudioFrame::kMaxDataSizeSamples) { continue; } const std::vector all_frames = {&frame1, &frame2}; - SetUpFrames(rate, number_of_channels); - const int number_of_frames = 2; SCOPED_TRACE( ProduceDebugText(rate, number_of_channels, number_of_frames)); @@ -162,8 +158,11 @@ TEST(FrameCombinerDeathTest, BuildCrashesWithManyChannels) { all_frames.begin(), all_frames.begin() + number_of_frames); AudioFrame audio_frame_for_mixing; EXPECT_DEATH( - combiner.Combine(frames_to_combine, number_of_channels, rate, - frames_to_combine.size(), &audio_frame_for_mixing), + { + SetUpFrames(rate, number_of_channels); + combiner.Combine(frames_to_combine, number_of_channels, rate, + frames_to_combine.size(), &audio_frame_for_mixing); + }, ""); } } @@ -341,8 +340,8 @@ TEST(FrameCombiner, GainCurveIsSmoothForAlternatingNumberOfStreams) { config.sample_rate_hz, number_of_streams, &audio_frame_for_mixing); cumulative_change += change_calculator.CalculateGainChange( - ArrayView(frame1.data(), number_of_samples), - ArrayView(audio_frame_for_mixing.data(), + std::span(frame1.data(), number_of_samples), + std::span(audio_frame_for_mixing.data(), number_of_samples)); } diff --git a/modules/audio_mixer/gain_change_calculator.cc b/modules/audio_mixer/gain_change_calculator.cc index ff14baaa52c..6420468f400 100644 --- a/modules/audio_mixer/gain_change_calculator.cc +++ b/modules/audio_mixer/gain_change_calculator.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -24,8 +24,8 @@ namespace { constexpr int16_t kReliabilityThreshold = 100; } // namespace -float GainChangeCalculator::CalculateGainChange(ArrayView in, - ArrayView out) { +float GainChangeCalculator::CalculateGainChange(std::span in, + std::span out) { RTC_DCHECK_EQ(in.size(), out.size()); std::vector gain(in.size()); @@ -37,9 +37,9 @@ float GainChangeCalculator::LatestGain() const { return last_reliable_gain_; } -void GainChangeCalculator::CalculateGain(ArrayView in, - ArrayView out, - ArrayView gain) { +void GainChangeCalculator::CalculateGain(std::span in, + std::span out, + std::span gain) { RTC_DCHECK_EQ(in.size(), out.size()); RTC_DCHECK_EQ(in.size(), gain.size()); @@ -52,7 +52,7 @@ void GainChangeCalculator::CalculateGain(ArrayView in, } float GainChangeCalculator::CalculateDifferences( - ArrayView values) { + std::span values) { float res = 0; for (float f : values) { res += fabs(f - last_value_); diff --git a/modules/audio_mixer/gain_change_calculator.h b/modules/audio_mixer/gain_change_calculator.h index b17db3b197e..2b5013a78c4 100644 --- a/modules/audio_mixer/gain_change_calculator.h +++ b/modules/audio_mixer/gain_change_calculator.h @@ -13,7 +13,7 @@ #include -#include "api/array_view.h" +#include namespace webrtc { @@ -22,17 +22,17 @@ class GainChangeCalculator { // The 'out' signal is assumed to be produced from 'in' by applying // a smoothly varying gain. This method computes variations of the // gain and handles special cases when the samples are small. - float CalculateGainChange(ArrayView in, - ArrayView out); + float CalculateGainChange(std::span in, + std::span out); float LatestGain() const; private: - void CalculateGain(ArrayView in, - ArrayView out, - ArrayView gain); + void CalculateGain(std::span in, + std::span out, + std::span gain); - float CalculateDifferences(ArrayView values); + float CalculateDifferences(std::span values); float last_value_ = 0.f; float last_reliable_gain_ = 1.0f; }; diff --git a/modules/audio_mixer/output_rate_calculator.h b/modules/audio_mixer/output_rate_calculator.h index 755da3285f8..e9938a08f3d 100644 --- a/modules/audio_mixer/output_rate_calculator.h +++ b/modules/audio_mixer/output_rate_calculator.h @@ -11,7 +11,7 @@ #ifndef MODULES_AUDIO_MIXER_OUTPUT_RATE_CALCULATOR_H_ #define MODULES_AUDIO_MIXER_OUTPUT_RATE_CALCULATOR_H_ -#include "api/array_view.h" +#include namespace webrtc { @@ -20,7 +20,7 @@ namespace webrtc { class OutputRateCalculator { public: virtual int CalculateOutputRateFromRange( - ArrayView preferred_sample_rates) = 0; + std::span preferred_sample_rates) = 0; virtual ~OutputRateCalculator() {} }; diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index 57af2d3d852..f1330d634d6 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -32,7 +32,6 @@ rtc_library("audio_frame_proxies") { "include/audio_frame_proxies.h", ] deps = [ - ":audio_frame_view", "../../api/audio:audio_frame_api", "../../api/audio:audio_processing", "../../api/audio:audio_processing_statistics", @@ -57,13 +56,13 @@ rtc_library("audio_buffer") { defines = [] deps = [ - "../../api:array_view", "../../api/audio:audio_frame_api", "../../api/audio:audio_processing", "../../common_audio", "../../common_audio:common_audio_c", "../../rtc_base:checks", "../../rtc_base:gtest_prod", + "capture_mixer", ] } @@ -79,7 +78,6 @@ rtc_library("high_pass_filter") { deps = [ ":audio_buffer", - "../../api:array_view", "../../rtc_base:checks", "utility:cascaded_biquad_filter", ] @@ -97,7 +95,6 @@ rtc_library("post_filter") { deps = [ ":audio_buffer", - "../../api:array_view", "../../rtc_base:checks", "utility:cascaded_biquad_filter", ] @@ -137,7 +134,6 @@ rtc_library("gain_controller2") { "../../common_audio", "../../rtc_base:checks", "../../rtc_base:logging", - "../../rtc_base:stringutils", "agc2:adaptive_digital_gain_controller", "agc2:common", "agc2:cpu_features", @@ -157,8 +153,6 @@ rtc_library("audio_processing") { sources = [ "audio_processing_impl.cc", "audio_processing_impl.h", - "echo_control_mobile_impl.cc", - "echo_control_mobile_impl.h", "gain_control_impl.cc", "gain_control_impl.h", "render_queue_item_verifier.h", @@ -170,15 +164,11 @@ rtc_library("audio_processing") { ":apm_logging", ":audio_buffer", ":audio_frame_proxies", - ":audio_frame_view", ":gain_controller2", ":high_pass_filter", ":post_filter", ":rms_level", - "../../api:array_view", "../../api:field_trials_view", - "../../api:function_view", - "../../api:make_ref_counted", "../../api:scoped_refptr", "../../api/audio:aec3_config", "../../api/audio:audio_frame_api", @@ -188,29 +178,19 @@ rtc_library("audio_processing") { "../../api/audio:neural_residual_echo_estimator_api", "../../api/environment", "../../api/task_queue", - "../../audio/utility:audio_frame_operations", "../../common_audio", - "../../common_audio:common_audio_c", - "../../common_audio:fir_filter", - "../../common_audio:fir_filter_factory", - "../../common_audio/third_party/ooura:fft_size_256", "../../rtc_base:checks", "../../rtc_base:denormal_disabler", "../../rtc_base:event_tracer", "../../rtc_base:gtest_prod", "../../rtc_base:logging", "../../rtc_base:macromagic", - "../../rtc_base:safe_minmax", - "../../rtc_base:sanitizer", "../../rtc_base:swap_queue", "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", - "../../rtc_base/system:rtc_export", - "../../system_wrappers", "../../system_wrappers:metrics", "aec3", "aec_dump", - "aecm:aecm_core", "agc", "agc:gain_control_interface", "agc:legacy_agc", @@ -247,7 +227,6 @@ rtc_library("residual_echo_detector") { ] deps = [ ":apm_logging", - "../../api:array_view", "../../api/audio:audio_processing", "../../rtc_base:checks", "../../rtc_base:logging", @@ -261,10 +240,7 @@ rtc_library("rms_level") { "rms_level.cc", "rms_level.h", ] - deps = [ - "../../api:array_view", - "../../rtc_base:checks", - ] + deps = [ "../../rtc_base:checks" ] } rtc_library("audio_processing_statistics") { @@ -296,7 +272,6 @@ rtc_library("apm_logging") { "logging/apm_data_dumper.h", ] deps = [ - "../../api:array_view", "../../common_audio", "../../rtc_base:checks", "../../rtc_base:stringutils", @@ -311,9 +286,6 @@ if (rtc_include_tests) { sources = [ "include/mock_audio_processing.h" ] deps = [ ":aec_dump_interface", - ":audio_buffer", - ":audio_processing", - "../../api:array_view", "../../api:scoped_refptr", "../../api/audio:audio_processing", "../../api/audio:audio_processing_statistics", @@ -347,7 +319,6 @@ if (rtc_include_tests) { sources = [ "audio_buffer_unittest.cc", "audio_frame_view_unittest.cc", - "echo_control_mobile_unittest.cc", "gain_controller2_unittest.cc", "splitting_filter_unittest.cc", "test/echo_canceller3_config_json_unittest.cc", @@ -366,12 +337,10 @@ if (rtc_include_tests) { ":high_pass_filter", ":mocks", ":post_filter", - "../../api:array_view", "../../api:make_ref_counted", "../../api:ref_count", "../../api:scoped_refptr", "../../api/audio:aec3_config", - "../../api/audio:aec3_factory", "../../api/audio:audio_frame_api", "../../api/audio:audio_processing", "../../api/audio:audio_processing_statistics", @@ -381,11 +350,8 @@ if (rtc_include_tests) { "../../api/environment:environment_factory", "../../api/units:time_delta", "../../common_audio", - "../../common_audio:common_audio_c", "../../rtc_base:checks", "../../rtc_base:cpu_info", - "../../rtc_base:denormal_disabler", - "../../rtc_base:gtest_prod", "../../rtc_base:macromagic", "../../rtc_base:platform_thread", "../../rtc_base:protobuf_utils", @@ -399,7 +365,6 @@ if (rtc_include_tests) { "../../rtc_base:task_queue_for_test", "../../rtc_base:threading", "../../rtc_base/synchronization:mutex", - "../../rtc_base/system:arch", "../../rtc_base/system:file_wrapper", "../../test:fileutils", "../../test:rtc_expect_death", @@ -410,7 +375,6 @@ if (rtc_include_tests) { "agc:gain_control_interface", "agc2:adaptive_digital_gain_controller_unittest", "agc2:biquad_filter_unittests", - "agc2:cpu_features", "agc2:fixed_digital_unittests", "agc2:gain_applier_unittest", "agc2:input_volume_controller", @@ -420,10 +384,8 @@ if (rtc_include_tests) { "agc2:saturation_protector_unittest", "agc2:speech_level_estimator_unittest", "agc2:test_utils", - "agc2:vad_wrapper", "agc2:vad_wrapper_unittests", "agc2/rnn_vad:unittests", - "capture_levels_adjuster", "capture_levels_adjuster:capture_levels_adjuster_unittests", "capture_mixer:capture_mixer_unittests", "test/conversational_speech:unittest", @@ -437,12 +399,6 @@ if (rtc_include_tests) { defines = [] - if (rtc_prefer_fixed_point) { - defines += [ "WEBRTC_AUDIOPROC_FIXED_PROFILE" ] - } else { - defines += [ "WEBRTC_AUDIOPROC_FLOAT_PROFILE" ] - } - if (rtc_enable_protobuf) { defines += [ "WEBRTC_AUDIOPROC_DEBUG_DUMP" ] deps += [ @@ -465,7 +421,6 @@ if (rtc_include_tests) { "audio_processing_impl_locking_unittest.cc", "audio_processing_impl_unittest.cc", "audio_processing_unittest.cc", - "echo_control_mobile_bit_exact_unittest.cc", "echo_detector/circular_buffer_unittest.cc", "echo_detector/mean_variance_estimator_unittest.cc", "echo_detector/moving_max_unittest.cc", @@ -495,7 +450,6 @@ if (rtc_include_tests) { deps = [ ":audio_processing", ":audioproc_test_utils", - "../../api:array_view", "../../api:scoped_refptr", "../../api/audio:audio_processing", "../../api/audio:builtin_audio_processing_builder", @@ -506,10 +460,8 @@ if (rtc_include_tests) { "../../api/units:time_delta", "../../rtc_base:checks", "../../rtc_base:platform_thread", - "../../rtc_base:protobuf_utils", "../../rtc_base:random", "../../rtc_base:rtc_event", - "../../rtc_base:safe_conversions", "../../system_wrappers", "../../test:test_support", "//third_party/abseil-cpp/absl/strings:string_view", @@ -522,8 +474,6 @@ if (rtc_include_tests) { "test/fake_recording_device.h", ] deps = [ - "../../api:array_view", - "../../api/audio:audio_frame_api", "../../common_audio", "../../rtc_base:checks", "../../rtc_base:logging", @@ -557,14 +507,12 @@ if (rtc_include_tests) { ":aec3_config_json", ":analog_mic_simulation", ":apm_logging", - ":audio_processing", ":audioproc_debug_proto", ":audioproc_protobuf_utils", ":audioproc_test_utils", "../../api:field_trials", "../../api:scoped_refptr", "../../api/audio:aec3_config", - "../../api/audio:aec3_factory", "../../api/audio:audio_processing", "../../api/audio:builtin_audio_processing_builder", "../../api/audio:echo_detector_creator", @@ -605,7 +553,6 @@ if (rtc_include_tests) { deps = [ ":audioproc_debug_proto", - "../../rtc_base:checks", "../../rtc_base:protobuf_utils", "../../rtc_base/system:arch", ] @@ -648,14 +595,11 @@ rtc_library("audioproc_test_utils") { deps = [ ":audio_buffer", - ":audio_processing", - "../../api:array_view", "../../api/audio:audio_frame_api", "../../api/audio:audio_processing", "../../common_audio", "../../rtc_base:checks", "../../rtc_base:random", - "../../rtc_base/system:arch", "../../system_wrappers", "../../test:fileutils", "../../test:test_support", @@ -678,7 +622,6 @@ rtc_library("aec3_config_json") { "../../rtc_base:logging", "../../rtc_base:rtc_json", "../../rtc_base:stringutils", - "../../rtc_base/system:rtc_export", "//third_party/abseil-cpp/absl/strings:string_view", ] } diff --git a/modules/audio_processing/OWNERS b/modules/audio_processing/OWNERS index f5dc59ea352..cc5ef820974 100644 --- a/modules/audio_processing/OWNERS +++ b/modules/audio_processing/OWNERS @@ -1,8 +1,6 @@ -alessiob@webrtc.org devicentepena@webrtc.org gustaf@webrtc.org henrik.lundin@webrtc.org -ivoc@webrtc.org lionelk@webrtc.org peah@webrtc.org saza@webrtc.org diff --git a/modules/audio_processing/aec3/BUILD.gn b/modules/audio_processing/aec3/BUILD.gn index 1f5a2717c97..f52017d9357 100644 --- a/modules/audio_processing/aec3/BUILD.gn +++ b/modules/audio_processing/aec3/BUILD.gn @@ -75,8 +75,8 @@ rtc_library("aec3") { "matched_filter.cc", "matched_filter_lag_aggregator.cc", "matched_filter_lag_aggregator.h", - "moving_average.cc", - "moving_average.h", + "moving_average_spectrum.cc", + "moving_average_spectrum.h", "multi_channel_content_detector.cc", "multi_channel_content_detector.h", "nearend_detector.h", @@ -143,13 +143,11 @@ rtc_library("aec3") { "..:apm_logging", "..:audio_buffer", "..:high_pass_filter", - "../../../api:array_view", "../../../api:field_trials_view", "../../../api/audio:aec3_config", "../../../api/audio:echo_control", "../../../api/audio:neural_residual_echo_estimator_api", "../../../api/environment", - "../../../common_audio:common_audio_c", "../../../rtc_base:checks", "../../../rtc_base:cpu_info", "../../../rtc_base:gtest_prod", @@ -176,10 +174,7 @@ rtc_source_set("aec3_common") { rtc_source_set("aec3_block") { sources = [ "block.h" ] - deps = [ - ":aec3_common", - "../../../api:array_view", - ] + deps = [ ":aec3_common" ] } rtc_source_set("aec3_fft") { @@ -187,10 +182,8 @@ rtc_source_set("aec3_fft") { deps = [ ":aec3_common", ":fft_data", - "../../../api:array_view", "../../../common_audio/third_party/ooura:fft_size_128", "../../../rtc_base:checks", - "../../../rtc_base/system:arch", ] } @@ -205,9 +198,7 @@ rtc_source_set("render_buffer") { ":aec3_block", ":aec3_common", ":fft_data", - "../../../api:array_view", "../../../rtc_base:checks", - "../../../rtc_base/system:arch", ] } @@ -219,7 +210,6 @@ rtc_source_set("adaptive_fir_filter") { ":fft_data", ":render_buffer", "..:apm_logging", - "../../../api:array_view", "../../../rtc_base/system:arch", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -229,7 +219,6 @@ rtc_source_set("adaptive_fir_filter_erl") { sources = [ "adaptive_fir_filter_erl.h" ] deps = [ ":aec3_common", - "../../../api:array_view", "../../../rtc_base/system:arch", ] } @@ -238,8 +227,6 @@ rtc_source_set("matched_filter") { sources = [ "matched_filter.h" ] deps = [ ":aec3_common", - "../../../api:array_view", - "../../../rtc_base:gtest_prod", "../../../rtc_base/system:arch", ] } @@ -248,7 +235,6 @@ rtc_source_set("vector_math") { sources = [ "vector_math.h" ] deps = [ ":aec3_common", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base/system:arch", ] @@ -258,7 +244,6 @@ rtc_source_set("fft_data") { sources = [ "fft_data.h" ] deps = [ ":aec3_common", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base/system:arch", ] @@ -292,7 +277,6 @@ if (current_cpu == "x86" || current_cpu == "x64") { ":matched_filter", ":render_buffer", ":vector_math", - "../../../api:array_view", "../../../rtc_base:checks", ] } @@ -327,9 +311,7 @@ if (rtc_include_tests) { ":vector_math", "..:apm_logging", "..:audio_buffer", - "..:audio_processing", "..:high_pass_filter", - "../../../api:array_view", "../../../api:field_trials", "../../../api/audio:aec3_config", "../../../api/audio:echo_control", @@ -338,14 +320,12 @@ if (rtc_include_tests) { "../../../api/environment:environment_factory", "../../../rtc_base:checks", "../../../rtc_base:cpu_info", - "../../../rtc_base:gunit_helpers", "../../../rtc_base:random", "../../../rtc_base:safe_minmax", "../../../rtc_base:stringutils", "../../../rtc_base/system:arch", "../../../system_wrappers:metrics", "../../../test:create_test_field_trials", - "../../../test:fileutils", "../../../test:test_support", "../utility:cascaded_biquad_filter", ] @@ -382,7 +362,7 @@ if (rtc_include_tests) { "frame_blocker_unittest.cc", "matched_filter_lag_aggregator_unittest.cc", "matched_filter_unittest.cc", - "moving_average_unittest.cc", + "moving_average_spectrum_unittest.cc", "multi_channel_content_detector_unittest.cc", "refined_filter_update_gain_unittest.cc", "render_buffer_unittest.cc", diff --git a/modules/audio_processing/aec3/adaptive_fir_filter.cc b/modules/audio_processing/aec3/adaptive_fir_filter.cc index 6a2e3ba712e..142f786f675 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" #include "modules/audio_processing/aec3/render_buffer.h" @@ -34,10 +34,8 @@ namespace webrtc { -namespace aec3 { - // Computes and stores the frequency response of the filter. -void ComputeFrequencyResponse( +void ComputeFrequencyResponse_C( size_t num_partitions, const std::vector>& H, std::vector>* H2) { @@ -134,7 +132,7 @@ void AdaptPartitions(const RenderBuffer& render_buffer, const FftData& G, size_t num_partitions, std::vector>* H) { - ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); size_t index = render_buffer.Position(); const size_t num_render_channels = render_buffer_data[index].size(); @@ -157,7 +155,7 @@ void AdaptPartitions_Neon(const RenderBuffer& render_buffer, const FftData& G, size_t num_partitions, std::vector>* H) { - webrtc::ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); const size_t num_render_channels = render_buffer_data[0].size(); const size_t lim1 = std::min( @@ -223,7 +221,7 @@ void AdaptPartitions_Sse2(const RenderBuffer& render_buffer, const FftData& G, size_t num_partitions, std::vector>* H) { - ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); const size_t num_render_channels = render_buffer_data[0].size(); const size_t lim1 = std::min( @@ -294,7 +292,7 @@ void ApplyFilter(const RenderBuffer& render_buffer, S->re.fill(0.f); S->im.fill(0.f); - ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); size_t index = render_buffer.Position(); const size_t num_render_channels = render_buffer_data[index].size(); @@ -319,12 +317,12 @@ void ApplyFilter_Neon(const RenderBuffer& render_buffer, const std::vector>& H, FftData* S) { // const RenderBuffer& render_buffer, - // webrtc::ArrayView H, + // std::span H, // FftData* S) { RTC_DCHECK_GE(H.size(), H.size() - 1); S->Clear(); - webrtc::ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); const size_t num_render_channels = render_buffer_data[0].size(); const size_t lim1 = std::min( @@ -389,13 +387,13 @@ void ApplyFilter_Sse2(const RenderBuffer& render_buffer, const std::vector>& H, FftData* S) { // const RenderBuffer& render_buffer, - // webrtc::ArrayView H, + // std::span H, // FftData* S) { RTC_DCHECK_GE(H.size(), H.size() - 1); S->re.fill(0.f); S->im.fill(0.f); - ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); const size_t num_render_channels = render_buffer_data[0].size(); const size_t lim1 = std::min( @@ -455,8 +453,6 @@ void ApplyFilter_Sse2(const RenderBuffer& render_buffer, } #endif -} // namespace aec3 - namespace { // Ensures that the newly added filter partitions after a size increase are set @@ -563,19 +559,19 @@ void AdaptiveFirFilter::Filter(const RenderBuffer& render_buffer, switch (optimization_) { #if defined(WEBRTC_ARCH_X86_FAMILY) case Aec3Optimization::kSse2: - aec3::ApplyFilter_Sse2(render_buffer, current_size_partitions_, H_, S); + ApplyFilter_Sse2(render_buffer, current_size_partitions_, H_, S); break; case Aec3Optimization::kAvx2: - aec3::ApplyFilter_Avx2(render_buffer, current_size_partitions_, H_, S); + ApplyFilter_Avx2(render_buffer, current_size_partitions_, H_, S); break; #endif #if defined(WEBRTC_HAS_NEON) case Aec3Optimization::kNeon: - aec3::ApplyFilter_Neon(render_buffer, current_size_partitions_, H_, S); + ApplyFilter_Neon(render_buffer, current_size_partitions_, H_, S); break; #endif default: - aec3::ApplyFilter(render_buffer, current_size_partitions_, H_, S); + ApplyFilter(render_buffer, current_size_partitions_, H_, S); } } @@ -607,19 +603,19 @@ void AdaptiveFirFilter::ComputeFrequencyResponse( switch (optimization_) { #if defined(WEBRTC_ARCH_X86_FAMILY) case Aec3Optimization::kSse2: - aec3::ComputeFrequencyResponse_Sse2(current_size_partitions_, H_, H2); + ComputeFrequencyResponse_Sse2(current_size_partitions_, H_, H2); break; case Aec3Optimization::kAvx2: - aec3::ComputeFrequencyResponse_Avx2(current_size_partitions_, H_, H2); + ComputeFrequencyResponse_Avx2(current_size_partitions_, H_, H2); break; #endif #if defined(WEBRTC_HAS_NEON) case Aec3Optimization::kNeon: - aec3::ComputeFrequencyResponse_Neon(current_size_partitions_, H_, H2); + ComputeFrequencyResponse_Neon(current_size_partitions_, H_, H2); break; #endif default: - aec3::ComputeFrequencyResponse(current_size_partitions_, H_, H2); + ComputeFrequencyResponse_C(current_size_partitions_, H_, H2); } } @@ -632,22 +628,19 @@ void AdaptiveFirFilter::AdaptAndUpdateSize(const RenderBuffer& render_buffer, switch (optimization_) { #if defined(WEBRTC_ARCH_X86_FAMILY) case Aec3Optimization::kSse2: - aec3::AdaptPartitions_Sse2(render_buffer, G, current_size_partitions_, - &H_); + AdaptPartitions_Sse2(render_buffer, G, current_size_partitions_, &H_); break; case Aec3Optimization::kAvx2: - aec3::AdaptPartitions_Avx2(render_buffer, G, current_size_partitions_, - &H_); + AdaptPartitions_Avx2(render_buffer, G, current_size_partitions_, &H_); break; #endif #if defined(WEBRTC_HAS_NEON) case Aec3Optimization::kNeon: - aec3::AdaptPartitions_Neon(render_buffer, G, current_size_partitions_, - &H_); + AdaptPartitions_Neon(render_buffer, G, current_size_partitions_, &H_); break; #endif default: - aec3::AdaptPartitions(render_buffer, G, current_size_partitions_, &H_); + AdaptPartitions(render_buffer, G, current_size_partitions_, &H_); } } diff --git a/modules/audio_processing/aec3/adaptive_fir_filter.h b/modules/audio_processing/aec3/adaptive_fir_filter.h index 34c06f43675..d960a2aa03e 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter.h +++ b/modules/audio_processing/aec3/adaptive_fir_filter.h @@ -17,7 +17,6 @@ #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_fft.h" #include "modules/audio_processing/aec3/fft_data.h" @@ -26,9 +25,8 @@ #include "rtc_base/system/arch.h" namespace webrtc { -namespace aec3 { // Computes and stores the frequency response of the filter. -void ComputeFrequencyResponse( +void ComputeFrequencyResponse_C( size_t num_partitions, const std::vector>& H, std::vector>* H2); @@ -96,8 +94,6 @@ void ApplyFilter_Avx2(const RenderBuffer& render_buffer, FftData* S); #endif -} // namespace aec3 - // Provides a frequency domain adaptive filter functionality. class AdaptiveFirFilter { public: diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc b/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc index f7ba1fda86e..327f0fb16d5 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_avx2.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/adaptive_fir_filter.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" @@ -24,8 +24,6 @@ namespace webrtc { -namespace aec3 { - // Computes and stores the frequency response of the filter. void ComputeFrequencyResponse_Avx2( size_t num_partitions, @@ -63,7 +61,7 @@ void AdaptPartitions_Avx2(const RenderBuffer& render_buffer, const FftData& G, size_t num_partitions, std::vector>* H) { - ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); const size_t num_render_channels = render_buffer_data[0].size(); const size_t lim1 = std::min( @@ -134,7 +132,7 @@ void ApplyFilter_Avx2(const RenderBuffer& render_buffer, S->re.fill(0.f); S->im.fill(0.f); - ArrayView> render_buffer_data = + std::span> render_buffer_data = render_buffer.GetFftBuffer(); const size_t num_render_channels = render_buffer_data[0].size(); const size_t lim1 = std::min( @@ -193,5 +191,4 @@ void ApplyFilter_Avx2(const RenderBuffer& render_buffer, } while (p < lim2); } -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc b/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc index 11f83af7fe6..19ffe86ef8b 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_erl.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" @@ -31,12 +31,10 @@ namespace webrtc { -namespace aec3 { - // Computes and stores the echo return loss estimate of the filter, which is the // sum of the partition frequency responses. void ErlComputer(const std::vector>& H2, - ArrayView erl) { + std::span erl) { std::fill(erl.begin(), erl.end(), 0.f); for (auto& H2_j : H2) { std::transform(H2_j.begin(), H2_j.end(), erl.begin(), erl.begin(), @@ -49,7 +47,7 @@ void ErlComputer(const std::vector>& H2, // sum of the partition frequency responses. void ErlComputer_NEON( const std::vector>& H2, - webrtc::ArrayView erl) { + std::span erl) { std::fill(erl.begin(), erl.end(), 0.f); for (auto& H2_j : H2) { for (size_t k = 0; k < kFftLengthBy2; k += 4) { @@ -68,7 +66,7 @@ void ErlComputer_NEON( // sum of the partition frequency responses. void ErlComputer_SSE2( const std::vector>& H2, - ArrayView erl) { + std::span erl) { std::fill(erl.begin(), erl.end(), 0.f); for (auto& H2_j : H2) { for (size_t k = 0; k < kFftLengthBy2; k += 4) { @@ -82,29 +80,27 @@ void ErlComputer_SSE2( } #endif -} // namespace aec3 - void ComputeErl(const Aec3Optimization& optimization, const std::vector>& H2, - ArrayView erl) { + std::span erl) { RTC_DCHECK_EQ(kFftLengthBy2Plus1, erl.size()); // Update the frequency response and echo return loss for the filter. switch (optimization) { #if defined(WEBRTC_ARCH_X86_FAMILY) case Aec3Optimization::kSse2: - aec3::ErlComputer_SSE2(H2, erl); + ErlComputer_SSE2(H2, erl); break; case Aec3Optimization::kAvx2: - aec3::ErlComputer_AVX2(H2, erl); + ErlComputer_AVX2(H2, erl); break; #endif #if defined(WEBRTC_HAS_NEON) case Aec3Optimization::kNeon: - aec3::ErlComputer_NEON(H2, erl); + ErlComputer_NEON(H2, erl); break; #endif default: - aec3::ErlComputer(H2, erl); + ErlComputer(H2, erl); } } diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_erl.h b/modules/audio_processing/aec3/adaptive_fir_filter_erl.h index 68da2d2d661..60a5c1e678e 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_erl.h +++ b/modules/audio_processing/aec3/adaptive_fir_filter_erl.h @@ -14,40 +14,37 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/system/arch.h" namespace webrtc { -namespace aec3 { // Computes and stores the echo return loss estimate of the filter, which is the // sum of the partition frequency responses. void ErlComputer(const std::vector>& H2, - ArrayView erl); + std::span erl); #if defined(WEBRTC_HAS_NEON) void ErlComputer_NEON( const std::vector>& H2, - webrtc::ArrayView erl); + std::span erl); #endif #if defined(WEBRTC_ARCH_X86_FAMILY) void ErlComputer_SSE2( const std::vector>& H2, - ArrayView erl); + std::span erl); void ErlComputer_AVX2( const std::vector>& H2, - ArrayView erl); + std::span erl); #endif -} // namespace aec3 - // Computes the echo return loss based on a frequency response. void ComputeErl(const Aec3Optimization& optimization, const std::vector>& H2, - ArrayView erl); + std::span erl); } // namespace webrtc diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc b/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc index 6abbcaec5a4..3b67f02247a 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_erl_avx2.cc @@ -13,21 +13,19 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" #include "modules/audio_processing/aec3/aec3_common.h" namespace webrtc { -namespace aec3 { - // Computes and stores the echo return loss estimate of the filter, which is the // sum of the partition frequency responses. void ErlComputer_AVX2( const std::vector>& H2, - ArrayView erl) { + std::span erl) { std::fill(erl.begin(), erl.end(), 0.f); for (auto& H2_j : H2) { for (size_t k = 0; k < kFftLengthBy2; k += 8) { @@ -40,5 +38,4 @@ void ErlComputer_AVX2( } } -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_erl_unittest.cc b/modules/audio_processing/aec3/adaptive_fir_filter_erl_unittest.cc index d95aad1a244..64d4f04b0be 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_erl_unittest.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_erl_unittest.cc @@ -25,7 +25,6 @@ #endif namespace webrtc { -namespace aec3 { #if defined(WEBRTC_HAS_NEON) // Verifies that the optimized method for echo return loss computation is @@ -105,5 +104,4 @@ TEST(AdaptiveFirFilter, UpdateErlAvx2Optimization) { #endif -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc index 2e600be46d9..e4c54812048 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment_factory.h" #include "modules/audio_processing/aec3/adaptive_fir_filter_erl.h" @@ -52,7 +52,6 @@ #endif namespace webrtc { -namespace aec3 { namespace { std::string ProduceDebugText(size_t num_render_channels, size_t delay) { @@ -168,7 +167,7 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels, } } - ComputeFrequencyResponse(num_partitions, H, &H2); + ComputeFrequencyResponse_C(num_partitions, H, &H2); ComputeFrequencyResponse_Neon(num_partitions, H, &H2_Neon); for (size_t p = 0; p < num_partitions; ++p) { @@ -348,7 +347,7 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels, } } - ComputeFrequencyResponse(num_partitions, H, &H2); + ComputeFrequencyResponse_C(num_partitions, H, &H2); ComputeFrequencyResponse_Sse2(num_partitions, H, &H2_Sse2); for (size_t p = 0; p < num_partitions; ++p) { @@ -383,7 +382,7 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels, } } - ComputeFrequencyResponse(num_partitions, H, &H2); + ComputeFrequencyResponse_C(num_partitions, H, &H2); ComputeFrequencyResponse_Avx2(num_partitions, H, &H2_Avx2); for (size_t p = 0; p < num_partitions; ++p) { @@ -546,11 +545,11 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) { num_render_channels); for (size_t ch = 0; ch < num_render_channels; ++ch) { x_hp_filter[ch] = std::make_unique( - ArrayView( + std::span( kHighPassFilterCoefficients)); } CascadedBiQuadFilter y_hp_filter( - (ArrayView( + (std::span( kHighPassFilterCoefficients))); SCOPED_TRACE(ProduceDebugText(num_render_channels, delay_samples)); @@ -620,5 +619,4 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) { } } -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/aec3_fft.cc b/modules/audio_processing/aec3/aec3_fft.cc index 7652e306a03..360590e358f 100644 --- a/modules/audio_processing/aec3/aec3_fft.cc +++ b/modules/audio_processing/aec3/aec3_fft.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" #include "rtc_base/checks.h" @@ -88,7 +88,7 @@ bool IsSse2Available() { Aec3Fft::Aec3Fft() : ooura_fft_(IsSse2Available()) {} // TODO(peah): Change x to be std::array once the rest of the code allows this. -void Aec3Fft::ZeroPaddedFft(ArrayView x, +void Aec3Fft::ZeroPaddedFft(std::span x, Window window, FftData* X) const { RTC_DCHECK(X); @@ -114,8 +114,8 @@ void Aec3Fft::ZeroPaddedFft(ArrayView x, Fft(&fft, X); } -void Aec3Fft::PaddedFft(ArrayView x, - ArrayView x_old, +void Aec3Fft::PaddedFft(std::span x, + std::span x_old, Window window, FftData* X) const { RTC_DCHECK(X); diff --git a/modules/audio_processing/aec3/aec3_fft.h b/modules/audio_processing/aec3/aec3_fft.h index 83d2a2e9199..2362a51df02 100644 --- a/modules/audio_processing/aec3/aec3_fft.h +++ b/modules/audio_processing/aec3/aec3_fft.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_AEC3_AEC3_FFT_H_ #include +#include -#include "api/array_view.h" #include "common_audio/third_party/ooura/fft_size_128/ooura_fft.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" @@ -48,19 +48,19 @@ class Aec3Fft { // Windows the input using a Hanning window, and then adds padding of // kFftLengthBy2 initial zeros before computing the Fft. - void ZeroPaddedFft(ArrayView x, Window window, FftData* X) const; + void ZeroPaddedFft(std::span x, Window window, FftData* X) const; // Concatenates the kFftLengthBy2 values long x and x_old before computing the // Fft. After that, x is copied to x_old. - void PaddedFft(ArrayView x, - ArrayView x_old, + void PaddedFft(std::span x, + std::span x_old, FftData* X) const { PaddedFft(x, x_old, Window::kRectangular, X); } // Padded Fft using a time-domain window. - void PaddedFft(ArrayView x, - ArrayView x_old, + void PaddedFft(std::span x, + std::span x_old, Window window, FftData* X) const; diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc index abc2ccb0d6e..d97fda3b9c4 100644 --- a/modules/audio_processing/aec3/aec_state.cc +++ b/modules/audio_processing/aec3/aec_state.cc @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" @@ -59,7 +59,7 @@ void ComputeAvgRenderReverb( int delay_blocks, float reverb_decay, ReverbModel* reverb_model, - ArrayView reverb_power_spectrum) { + std::span reverb_power_spectrum) { RTC_DCHECK(reverb_model); const size_t num_render_channels = spectrum_buffer.buffer[0].size(); int idx_at_delay = @@ -67,13 +67,13 @@ void ComputeAvgRenderReverb( int idx_past = spectrum_buffer.IncIndex(idx_at_delay); std::array X2_data; - ArrayView X2; + std::span X2; if (num_render_channels > 1) { auto average_channels = [](size_t num_render_channels, - ArrayView> + std::span> spectrum_band_0, - ArrayView render_power) { + std::span render_power) { std::fill(render_power.begin(), render_power.end(), 0.f); for (size_t ch = 0; ch < num_render_channels; ++ch) { for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { @@ -101,7 +101,7 @@ void ComputeAvgRenderReverb( X2 = spectrum_buffer.buffer[idx_at_delay][/*channel=*/0]; } - ArrayView reverb_power = + std::span reverb_power = reverb_model->reverb(); for (size_t k = 0; k < X2.size(); ++k) { reverb_power_spectrum[k] = X2[k] + reverb_power[k]; @@ -112,7 +112,7 @@ void ComputeAvgRenderReverb( std::atomic AecState::instance_count_(0); -void AecState::GetResidualEchoScaling(ArrayView residual_scaling) const { +void AecState::GetResidualEchoScaling(std::span residual_scaling) const { bool filter_has_had_time_to_converge; if (config_.filter.conservative_initial_phase) { filter_has_had_time_to_converge = @@ -189,13 +189,13 @@ void AecState::HandleEchoPathChange( void AecState::Update( const std::optional& external_delay, - ArrayView>> + std::span>> adaptive_filter_frequency_responses, - ArrayView> adaptive_filter_impulse_responses, + std::span> adaptive_filter_impulse_responses, const RenderBuffer& render_buffer, - ArrayView> E2_refined, - ArrayView> Y2, - ArrayView subtractor_output) { + std::span> E2_refined, + std::span> Y2, + std::span subtractor_output) { RTC_DCHECK_EQ(num_capture_channels_, Y2.size()); RTC_DCHECK_EQ(num_capture_channels_, subtractor_output.size()); RTC_DCHECK_EQ(num_capture_channels_, @@ -376,12 +376,11 @@ AecState::FilterDelay::FilterDelay(const EchoCanceller3Config& config, min_filter_delay_(delay_headroom_blocks_) {} void AecState::FilterDelay::Update( - ArrayView analyzer_filter_delay_estimates_blocks, + std::span analyzer_filter_delay_estimates_blocks, const std::optional& external_delay, size_t blocks_with_proper_filter_adaptation) { // Update the delay based on the external delay. - if (external_delay && - (!external_delay_ || external_delay_->delay != external_delay->delay)) { + if (external_delay) { external_delay_ = external_delay; } @@ -465,7 +464,7 @@ void AecState::SaturationDetector::Update( const Block& x, bool saturated_capture, bool usable_linear_estimate, - ArrayView subtractor_output, + std::span subtractor_output, float echo_path_gain) { saturated_echo_ = false; if (!saturated_capture) { @@ -483,7 +482,7 @@ void AecState::SaturationDetector::Update( } else { float max_sample = 0.f; for (int ch = 0; ch < x.NumChannels(); ++ch) { - ArrayView x_ch = x.View(/*band=*/0, ch); + std::span x_ch = x.View(/*band=*/0, ch); for (float sample : x_ch) { max_sample = std::max(max_sample, fabsf(sample)); } diff --git a/modules/audio_processing/aec3/aec_state.h b/modules/audio_processing/aec3/aec_state.h index 9c5adc7d3c7..df5bc65b9b0 100644 --- a/modules/audio_processing/aec3/aec_state.h +++ b/modules/audio_processing/aec3/aec_state.h @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -67,7 +67,7 @@ class AecState { // Returns the appropriate scaling of the residual echo to match the // audibility. - void GetResidualEchoScaling(ArrayView residual_scaling) const; + void GetResidualEchoScaling(std::span residual_scaling) const; // Returns whether the stationary properties of the signals are used in the // aec. @@ -76,13 +76,13 @@ class AecState { } // Returns the ERLE. - ArrayView> Erle( + std::span> Erle( bool onset_compensated) const { return erle_estimator_.Erle(onset_compensated); } // Returns the non-capped ERLE. - ArrayView> ErleUnbounded() const { + std::span> ErleUnbounded() const { return erle_estimator_.ErleUnbounded(); } @@ -129,7 +129,7 @@ class AecState { } // Return the frequency response of the reverberant echo. - ArrayView GetReverbFrequencyResponse() const { + std::span GetReverbFrequencyResponse() const { return reverb_model_estimator_.GetReverbFrequencyResponse(); } @@ -143,13 +143,13 @@ class AecState { // TODO(bugs.webrtc.org/10913): Compute multi-channel ERL. void Update( const std::optional& external_delay, - ArrayView>> + std::span>> adaptive_filter_frequency_responses, - ArrayView> adaptive_filter_impulse_responses, + std::span> adaptive_filter_impulse_responses, const RenderBuffer& render_buffer, - ArrayView> E2_refined, - ArrayView> Y2, - ArrayView subtractor_output); + std::span> E2_refined, + std::span> Y2, + std::span subtractor_output); // Returns filter length in blocks. int FilterLengthBlocks() const { @@ -215,7 +215,7 @@ class AecState { // Returns the delay in blocks relative to the beginning of the filter that // corresponds to the direct path of the echo. - ArrayView DirectPathFilterDelays() const { + std::span DirectPathFilterDelays() const { return filter_delays_blocks_; } @@ -224,7 +224,7 @@ class AecState { int MinDirectPathFilterDelay() const { return min_filter_delay_; } // Updates the delay estimates based on new data. - void Update(ArrayView analyzer_filter_delay_estimates_blocks, + void Update(std::span analyzer_filter_delay_estimates_blocks, const std::optional& external_delay, size_t blocks_with_proper_filter_adaptation); @@ -287,7 +287,7 @@ class AecState { void Update(const Block& x, bool saturated_capture, bool usable_linear_estimate, - ArrayView subtractor_output, + std::span subtractor_output, float echo_path_gain); private: diff --git a/modules/audio_processing/aec3/alignment_mixer.cc b/modules/audio_processing/aec3/alignment_mixer.cc index 0370aebcaf4..c3e36b22b7e 100644 --- a/modules/audio_processing/aec3/alignment_mixer.cc +++ b/modules/audio_processing/aec3/alignment_mixer.cc @@ -12,8 +12,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" @@ -70,7 +70,7 @@ AlignmentMixer::AlignmentMixer(size_t num_channels, } void AlignmentMixer::ProduceOutput(const Block& x, - ArrayView y) { + std::span y) { RTC_DCHECK_EQ(x.NumChannels(), num_channels_); if (selection_variant_ == MixingVariant::kDownmix) { @@ -85,7 +85,7 @@ void AlignmentMixer::ProduceOutput(const Block& x, } void AlignmentMixer::Downmix(const Block& x, - ArrayView y) const { + std::span y) const { RTC_DCHECK_EQ(x.NumChannels(), num_channels_); RTC_DCHECK_GE(num_channels_, 2); std::memcpy(&y[0], x.View(/*band=*/0, /*channel=*/0).data(), @@ -122,7 +122,7 @@ int AlignmentMixer::SelectChannel(const Block& x) { for (int ch = 0; ch < num_ch_to_analyze; ++ch) { float x2_sum = 0.f; - ArrayView x_ch = x.View(/*band=*/0, ch); + std::span x_ch = x.View(/*band=*/0, ch); for (size_t i = 0; i < kBlockSize; ++i) { x2_sum += x_ch[i] * x_ch[i]; } diff --git a/modules/audio_processing/aec3/alignment_mixer.h b/modules/audio_processing/aec3/alignment_mixer.h index 29bf14b6755..ecc015d7876 100644 --- a/modules/audio_processing/aec3/alignment_mixer.h +++ b/modules/audio_processing/aec3/alignment_mixer.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" @@ -36,7 +36,7 @@ class AlignmentMixer { float excitation_limit, bool prefer_first_two_channels); - void ProduceOutput(const Block& x, ArrayView y); + void ProduceOutput(const Block& x, std::span y); enum class MixingVariant { kDownmix, kAdaptive, kFixed }; @@ -51,7 +51,7 @@ class AlignmentMixer { int selected_channel_ = 0; size_t block_counter_ = 0; - void Downmix(const Block& x, ArrayView y) const; + void Downmix(const Block& x, std::span y) const; int SelectChannel(const Block& x); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/alignment_mixer_unittest.cc b/modules/audio_processing/aec3/alignment_mixer_unittest.cc index 72c2649a48c..e21e2118e84 100644 --- a/modules/audio_processing/aec3/alignment_mixer_unittest.cc +++ b/modules/audio_processing/aec3/alignment_mixer_unittest.cc @@ -15,7 +15,6 @@ #include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "rtc_base/checks.h" diff --git a/modules/audio_processing/aec3/block.h b/modules/audio_processing/aec3/block.h index 75f032d0f29..164ba1824a3 100644 --- a/modules/audio_processing/aec3/block.h +++ b/modules/audio_processing/aec3/block.h @@ -12,10 +12,10 @@ #define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_H_ #include +#include #include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" namespace webrtc { @@ -58,14 +58,14 @@ class Block { return begin(band, channel) + kBlockSize; } - // Access data via ArrayView. - ArrayView View(int band, int channel) { - return ArrayView(&data_[GetIndex(band, channel)], + // Access data via std::span. + std::span View(int band, int channel) { + return std::span(&data_[GetIndex(band, channel)], kBlockSize); } - ArrayView View(int band, int channel) const { - return ArrayView(&data_[GetIndex(band, channel)], + std::span View(int band, int channel) const { + return std::span(&data_[GetIndex(band, channel)], kBlockSize); } diff --git a/modules/audio_processing/aec3/block_delay_buffer.cc b/modules/audio_processing/aec3/block_delay_buffer.cc index 3d67aae6cba..84df4e50c3e 100644 --- a/modules/audio_processing/aec3/block_delay_buffer.cc +++ b/modules/audio_processing/aec3/block_delay_buffer.cc @@ -10,9 +10,9 @@ #include "modules/audio_processing/aec3/block_delay_buffer.h" #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/audio_buffer.h" #include "rtc_base/checks.h" @@ -44,7 +44,7 @@ void BlockDelayBuffer::DelaySignal(AudioBuffer* frame) { for (size_t ch = 0; ch < num_channels; ++ch) { RTC_DCHECK_EQ(buf_[ch].size(), frame->num_bands()); RTC_DCHECK_EQ(buf_[ch].size(), num_bands); - ArrayView frame_ch(frame->split_bands(ch), num_bands); + std::span frame_ch(frame->split_bands(ch), num_bands); const size_t delay = delay_; for (size_t band = 0; band < num_bands; ++band) { diff --git a/modules/audio_processing/aec3/block_framer.cc b/modules/audio_processing/aec3/block_framer.cc index 58d1effba70..f9cadb9a8ae 100644 --- a/modules/audio_processing/aec3/block_framer.cc +++ b/modules/audio_processing/aec3/block_framer.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "rtc_base/checks.h" @@ -54,7 +54,7 @@ void BlockFramer::InsertBlock(const Block& block) { void BlockFramer::InsertBlockAndExtractSubFrame( const Block& block, - std::vector>>* sub_frame) { + std::vector>>* sub_frame) { RTC_DCHECK(sub_frame); RTC_DCHECK_EQ(num_bands_, block.NumBands()); RTC_DCHECK_EQ(num_channels_, block.NumChannels()); diff --git a/modules/audio_processing/aec3/block_framer.h b/modules/audio_processing/aec3/block_framer.h index c7ddc4987e7..896bc6b5ddd 100644 --- a/modules/audio_processing/aec3/block_framer.h +++ b/modules/audio_processing/aec3/block_framer.h @@ -12,9 +12,9 @@ #define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_FRAMER_H_ #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/block.h" namespace webrtc { @@ -37,7 +37,7 @@ class BlockFramer { // Adds a 64 sample block and extracts an 80 sample subframe. void InsertBlockAndExtractSubFrame( const Block& block, - std::vector>>* sub_frame); + std::vector>>* sub_frame); private: const size_t num_bands_; diff --git a/modules/audio_processing/aec3/block_framer_unittest.cc b/modules/audio_processing/aec3/block_framer_unittest.cc index b898f32db61..fa9aaff15f5 100644 --- a/modules/audio_processing/aec3/block_framer_unittest.cc +++ b/modules/audio_processing/aec3/block_framer_unittest.cc @@ -11,10 +11,10 @@ #include "modules/audio_processing/aec3/block_framer.h" #include +#include #include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "rtc_base/checks.h" @@ -26,12 +26,12 @@ namespace { void SetupSubFrameView( std::vector>>* sub_frame, - std::vector>>* sub_frame_view) { + std::vector>>* sub_frame_view) { for (size_t band = 0; band < sub_frame_view->size(); ++band) { for (size_t channel = 0; channel < (*sub_frame_view)[band].size(); ++channel) { (*sub_frame_view)[band][channel] = - ArrayView((*sub_frame)[band][channel].data(), + std::span((*sub_frame)[band][channel].data(), (*sub_frame)[band][channel].size()); } } @@ -52,7 +52,7 @@ float ComputeSampleValue(size_t chunk_counter, bool VerifySubFrame( size_t sub_frame_counter, int offset, - const std::vector>>& sub_frame_view) { + const std::vector>>& sub_frame_view) { for (size_t band = 0; band < sub_frame_view.size(); ++band) { for (size_t channel = 0; channel < sub_frame_view[band].size(); ++channel) { for (size_t sample = 0; sample < sub_frame_view[band][channel].size(); @@ -89,8 +89,8 @@ void RunFramerTest(int sample_rate_hz, size_t num_channels) { std::vector>> output_sub_frame( num_bands, std::vector>( num_channels, std::vector(kSubFrameLength, 0.f))); - std::vector>> output_sub_frame_view( - num_bands, std::vector>(num_channels)); + std::vector>> output_sub_frame_view( + num_bands, std::vector>(num_channels)); SetupSubFrameView(&output_sub_frame, &output_sub_frame_view); BlockFramer framer(num_bands, num_channels); @@ -128,9 +128,9 @@ void RunWronglySizedInsertAndExtractParametersTest( num_sub_frame_bands, std::vector>( num_sub_frame_channels, std::vector(sub_frame_length, 0.f))); - std::vector>> output_sub_frame_view( + std::vector>> output_sub_frame_view( output_sub_frame.size(), - std::vector>(num_sub_frame_channels)); + std::vector>(num_sub_frame_channels)); SetupSubFrameView(&output_sub_frame, &output_sub_frame_view); BlockFramer framer(correct_num_bands, correct_num_channels); EXPECT_DEATH( @@ -151,9 +151,9 @@ void RunWronglySizedInsertParameterTest(int sample_rate_hz, correct_num_bands, std::vector>( correct_num_channels, std::vector(kSubFrameLength, 0.f))); - std::vector>> output_sub_frame_view( + std::vector>> output_sub_frame_view( output_sub_frame.size(), - std::vector>(correct_num_channels)); + std::vector>(correct_num_channels)); SetupSubFrameView(&output_sub_frame, &output_sub_frame_view); BlockFramer framer(correct_num_bands, correct_num_channels); framer.InsertBlockAndExtractSubFrame(correct_block, &output_sub_frame_view); @@ -178,8 +178,8 @@ void RunWronglyInsertOrderTest(int sample_rate_hz, correct_num_bands, std::vector>( num_channels, std::vector(kSubFrameLength, 0.f))); - std::vector>> output_sub_frame_view( - output_sub_frame.size(), std::vector>(num_channels)); + std::vector>> output_sub_frame_view( + output_sub_frame.size(), std::vector>(num_channels)); SetupSubFrameView(&output_sub_frame, &output_sub_frame_view); BlockFramer framer(correct_num_bands, num_channels); for (size_t k = 0; k < num_preceeding_api_calls; ++k) { diff --git a/modules/audio_processing/aec3/block_processor_unittest.cc b/modules/audio_processing/aec3/block_processor_unittest.cc index fe9596d4c49..bc085841d26 100644 --- a/modules/audio_processing/aec3/block_processor_unittest.cc +++ b/modules/audio_processing/aec3/block_processor_unittest.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -118,7 +118,7 @@ std::string ProduceDebugText(int sample_rate_hz) { return ss.Release(); } -void FillSampleVector(int call_counter, int delay, ArrayView samples) { +void FillSampleVector(int call_counter, int delay, std::span samples) { for (size_t i = 0; i < samples.size(); ++i) { samples[i] = (call_counter - delay) * 10000.0f + i; } diff --git a/modules/audio_processing/aec3/comfort_noise_generator.cc b/modules/audio_processing/aec3/comfort_noise_generator.cc index ce500a57d5c..58b2323d25b 100644 --- a/modules/audio_processing/aec3/comfort_noise_generator.cc +++ b/modules/audio_processing/aec3/comfort_noise_generator.cc @@ -18,13 +18,14 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" #include "modules/audio_processing/aec3/vector_math.h" +#include "rtc_base/checks.h" // Defines WEBRTC_ARCH_X86_FAMILY, used below. #include "rtc_base/system/arch.h" @@ -47,7 +48,7 @@ float GetNoiseFloorFactor(float noise_floor_dbfs) { // Table of sqrt(2) * sin(2*pi*i/32). // clang-format off -constexpr float kSqrt2Sin[32] = { +constexpr std::array kSqrt2Sin = { +0.0000000f, +0.2758994f, +0.5411961f, +0.7856950f, +1.0000000f, +1.1758756f, +1.3065630f, +1.3870398f, +sqrt2_v, +1.3870398f, +1.3065630f, +1.1758756f, +1.0000000f, +0.7856950f, +0.5411961f, @@ -57,18 +58,43 @@ constexpr float kSqrt2Sin[32] = { -0.5411961f, -0.2758994f}; // clang-format on -void GenerateComfortNoise(Aec3Optimization optimization, - const std::array& N2, - uint32_t* seed, - FftData* lower_band_noise, - FftData* upper_band_noise) { +void GenerateRandomSinTableIndices( + uint32_t& seed, + std::span re_sin_table_indices, + std::span im_sin_table_indices) { + RTC_DCHECK_EQ(re_sin_table_indices.size(), im_sin_table_indices.size()); + + constexpr int32_t kSeedMask = 0x80000000 - 1; + constexpr int16_t kImIndexMask = 32 - 1; + for (size_t k = 0; k < re_sin_table_indices.size(); ++k) { + // Generate a random 31-bit integer. + seed = (seed * 69069 + 1) & kSeedMask; + + // Convert to 5-bit indices. + int16_t index = seed >> 26; + RTC_DCHECK_LT(index, kSqrt2Sin.size()); + re_sin_table_indices[k] = index; + + index = (re_sin_table_indices[k] + 8) & kImIndexMask; + RTC_DCHECK_LT(index, kSqrt2Sin.size()); + im_sin_table_indices[k] = index; + } +} + +void GenerateComfortNoise( + Aec3Optimization optimization, + const std::array& N2, + std::span re_sin_table_indices, + std::span im_sin_table_indices, + FftData* lower_band_noise, + FftData* upper_band_noise) { FftData* N_low = lower_band_noise; FftData* N_high = upper_band_noise; // Compute square root spectrum. std::array N; std::copy(N2.begin(), N2.end(), N.begin()); - aec3::VectorMath(optimization).Sqrt(N); + VectorMath(optimization).Sqrt(N); // Compute the noise level for the upper bands. constexpr float kOneByNumBands = 1.f / (kFftLengthBy2Plus1 / 2 + 1); @@ -85,16 +111,10 @@ void GenerateComfortNoise(Aec3Optimization optimization, N_low->re[0] = N_low->re[kFftLengthBy2] = N_high->re[0] = N_high->re[kFftLengthBy2] = 0.f; for (size_t k = 1; k < kFftLengthBy2; k++) { - constexpr int kIndexMask = 32 - 1; - // Generate a random 31-bit integer. - seed[0] = (seed[0] * 69069 + 1) & (0x80000000 - 1); - // Convert to a 5-bit index. - int i = seed[0] >> 26; - // y = sqrt(2) * sin(a) - const float x = kSqrt2Sin[i]; + const float& x = kSqrt2Sin[re_sin_table_indices[k - 1]]; // x = sqrt(2) * cos(a) = sqrt(2) * sin(a + pi/2) - const float y = kSqrt2Sin[(i + 8) & kIndexMask]; + const float& y = kSqrt2Sin[im_sin_table_indices[k - 1]]; // Form low-frequency noise via spectral shaping. N_low->re[k] = N[k] * x; @@ -131,9 +151,9 @@ ComfortNoiseGenerator::~ComfortNoiseGenerator() = default; void ComfortNoiseGenerator::Compute( bool saturated_capture, - ArrayView> capture_spectrum, - ArrayView lower_band_noise, - ArrayView upper_band_noise) { + std::span> capture_spectrum, + std::span lower_band_noise, + std::span upper_band_noise) { const auto& Y2 = capture_spectrum; if (!saturated_capture) { @@ -185,8 +205,14 @@ void ComfortNoiseGenerator::Compute( // Choose N2 estimate to use. const auto& N2 = N2_initial_ ? (*N2_initial_) : N2_; + std::array re_sin_table_indices; + std::array im_sin_table_indices; + GenerateRandomSinTableIndices(seed_, re_sin_table_indices, + im_sin_table_indices); + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { - GenerateComfortNoise(optimization_, N2[ch], &seed_, &lower_band_noise[ch], + GenerateComfortNoise(optimization_, N2[ch], re_sin_table_indices, + im_sin_table_indices, &lower_band_noise[ch], &upper_band_noise[ch]); } } diff --git a/modules/audio_processing/aec3/comfort_noise_generator.h b/modules/audio_processing/aec3/comfort_noise_generator.h index 3672185f99d..05cd43e79a9 100644 --- a/modules/audio_processing/aec3/comfort_noise_generator.h +++ b/modules/audio_processing/aec3/comfort_noise_generator.h @@ -16,16 +16,15 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" #include "rtc_base/system/arch.h" namespace webrtc { -namespace aec3 { #if defined(WEBRTC_ARCH_X86_FAMILY) void EstimateComfortNoise_SSE2(const std::array& N2, @@ -38,8 +37,6 @@ void EstimateComfortNoise(const std::array& N2, FftData* lower_band_noise, FftData* upper_band_noise); -} // namespace aec3 - // Generates the comfort noise. class ComfortNoiseGenerator { public: @@ -53,12 +50,12 @@ class ComfortNoiseGenerator { // Computes the comfort noise. void Compute( bool saturated_capture, - ArrayView> capture_spectrum, - ArrayView lower_band_noise, - ArrayView upper_band_noise); + std::span> capture_spectrum, + std::span lower_band_noise, + std::span upper_band_noise); // Returns the estimate of the background noise spectrum. - ArrayView> NoiseSpectrum() const { + std::span> NoiseSpectrum() const { return N2_; } diff --git a/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc b/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc index 17ce0477d72..e9850231093 100644 --- a/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc +++ b/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc @@ -23,7 +23,6 @@ #include "test/gtest.h" namespace webrtc { -namespace aec3 { namespace { float Power(const FftData& N) { @@ -70,5 +69,4 @@ TEST(ComfortNoiseGenerator, CorrectLevel) { } } -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/decimator.cc b/modules/audio_processing/aec3/decimator.cc index d0f0e7c82c2..473273d2c1b 100644 --- a/modules/audio_processing/aec3/decimator.cc +++ b/modules/audio_processing/aec3/decimator.cc @@ -11,8 +11,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/utility/cascaded_biquad_filter.h" #include "rtc_base/checks.h" @@ -59,20 +59,20 @@ Decimator::Decimator(size_t down_sampling_factor) : down_sampling_factor_(down_sampling_factor), anti_aliasing_filter_( down_sampling_factor_ == 4 - ? ArrayView( + ? std::span( kLowPassFilterDs4) - : ArrayView( + : std::span( kBandPassFilterDs8)), noise_reduction_filter_( down_sampling_factor_ == 8 - ? (ArrayView( + ? (std::span( kPassThroughFilter)) - : (ArrayView( + : (std::span( kHighPassFilter))) { RTC_DCHECK(down_sampling_factor_ == 4 || down_sampling_factor_ == 8); } -void Decimator::Decimate(ArrayView in, ArrayView out) { +void Decimator::Decimate(std::span in, std::span out) { RTC_DCHECK_EQ(kBlockSize, in.size()); RTC_DCHECK_EQ(kBlockSize / down_sampling_factor_, out.size()); std::array x; diff --git a/modules/audio_processing/aec3/decimator.h b/modules/audio_processing/aec3/decimator.h index 805616948f7..51d64895154 100644 --- a/modules/audio_processing/aec3/decimator.h +++ b/modules/audio_processing/aec3/decimator.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/utility/cascaded_biquad_filter.h" namespace webrtc { @@ -27,7 +27,7 @@ class Decimator { Decimator& operator=(const Decimator&) = delete; // Downsamples the signal. - void Decimate(ArrayView in, ArrayView out); + void Decimate(std::span in, std::span out); private: const size_t down_sampling_factor_; diff --git a/modules/audio_processing/aec3/decimator_unittest.cc b/modules/audio_processing/aec3/decimator_unittest.cc index 2adb46f64ce..d0eb010b685 100644 --- a/modules/audio_processing/aec3/decimator_unittest.cc +++ b/modules/audio_processing/aec3/decimator_unittest.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" #include "rtc_base/strings/string_builder.h" @@ -60,17 +60,17 @@ void ProduceDecimatedSinusoidalOutputPower(int sample_rate_hz, for (size_t k = 0; k < kNumBlocks; ++k) { std::vector sub_block(sub_block_size); decimator.Decimate( - ArrayView(&input[k * kBlockSize], kBlockSize), sub_block); + std::span(&input[k * kBlockSize], kBlockSize), sub_block); std::copy(sub_block.begin(), sub_block.end(), output.begin() + k * sub_block_size); } ASSERT_GT(kNumBlocks, kNumStartupBlocks); - ArrayView input_to_evaluate( + std::span input_to_evaluate( &input[kNumStartupBlocks * kBlockSize], (kNumBlocks - kNumStartupBlocks) * kBlockSize); - ArrayView output_to_evaluate( + std::span output_to_evaluate( &output[kNumStartupBlocks * sub_block_size], (kNumBlocks - kNumStartupBlocks) * sub_block_size); *input_power = @@ -114,7 +114,7 @@ TEST(DecimatorDeathTest, WrongInputSize) { TEST(DecimatorDeathTest, NullOutput) { Decimator decimator(4); std::vector x(kBlockSize, 0.f); - EXPECT_DEATH(decimator.Decimate(x, nullptr), ""); + EXPECT_DEATH(decimator.Decimate(x, {}), ""); } // Verifies the check for the output size. diff --git a/modules/audio_processing/aec3/delay_estimate.h b/modules/audio_processing/aec3/delay_estimate.h index 7838a0c2554..2ba03fd98b8 100644 --- a/modules/audio_processing/aec3/delay_estimate.h +++ b/modules/audio_processing/aec3/delay_estimate.h @@ -24,8 +24,6 @@ struct DelayEstimate { Quality quality; size_t delay; - size_t blocks_since_last_change = 0; - size_t blocks_since_last_update = 0; }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/dominant_nearend_detector.cc b/modules/audio_processing/aec3/dominant_nearend_detector.cc index c37fa9a4bad..d7e01fbaeb5 100644 --- a/modules/audio_processing/aec3/dominant_nearend_detector.cc +++ b/modules/audio_processing/aec3/dominant_nearend_detector.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" @@ -24,26 +24,21 @@ namespace webrtc { DominantNearendDetector::DominantNearendDetector( const EchoCanceller3Config::Suppressor::DominantNearendDetection& config, size_t num_capture_channels) - : enr_threshold_(config.enr_threshold), - enr_exit_threshold_(config.enr_exit_threshold), - snr_threshold_(config.snr_threshold), - hold_duration_(config.hold_duration), - trigger_threshold_(config.trigger_threshold), - use_during_initial_phase_(config.use_during_initial_phase), + : config_(config), num_capture_channels_(num_capture_channels), trigger_counters_(num_capture_channels_), hold_counters_(num_capture_channels_) {} void DominantNearendDetector::Update( - ArrayView> nearend_spectrum, - ArrayView> + std::span> nearend_spectrum, + std::span> residual_echo_spectrum, - ArrayView> + std::span> comfort_noise_spectrum, bool initial_state) { nearend_state_ = false; - auto low_frequency_energy = [](ArrayView spectrum) { + auto low_frequency_energy = [](std::span spectrum) { RTC_DCHECK_LE(16, spectrum.size()); return std::accumulate(spectrum.begin() + 1, spectrum.begin() + 16, 0.f); }; @@ -55,13 +50,13 @@ void DominantNearendDetector::Update( // Detect strong active nearend if the nearend is sufficiently stronger than // the echo and the nearend noise. - if ((!initial_state || use_during_initial_phase_) && - echo_sum < enr_threshold_ * ne_sum && - ne_sum > snr_threshold_ * noise_sum) { - if (++trigger_counters_[ch] >= trigger_threshold_) { + if ((!initial_state || config_.use_during_initial_phase) && + echo_sum < config_.enr_threshold * ne_sum && + ne_sum > config_.snr_threshold * noise_sum) { + if (++trigger_counters_[ch] >= config_.trigger_threshold) { // After a period of strong active nearend activity, flag nearend mode. - hold_counters_[ch] = hold_duration_; - trigger_counters_[ch] = trigger_threshold_; + hold_counters_[ch] = config_.hold_duration; + trigger_counters_[ch] = config_.trigger_threshold; } } else { // Forget previously detected strong active nearend activity. @@ -69,8 +64,8 @@ void DominantNearendDetector::Update( } // Exit nearend-state early at strong echo. - if (echo_sum > enr_exit_threshold_ * ne_sum && - echo_sum > snr_threshold_ * noise_sum) { + if (echo_sum > config_.enr_exit_threshold * ne_sum && + echo_sum > config_.snr_threshold * noise_sum) { hold_counters_[ch] = 0; } @@ -79,4 +74,10 @@ void DominantNearendDetector::Update( nearend_state_ = nearend_state_ || hold_counters_[ch] > 0; } } + +void DominantNearendDetector::SetConfig( + const EchoCanceller3Config::Suppressor& config) { + config_ = config.dominant_nearend_detection; +} + } // namespace webrtc diff --git a/modules/audio_processing/aec3/dominant_nearend_detector.h b/modules/audio_processing/aec3/dominant_nearend_detector.h index afd15160424..877697c2e4f 100644 --- a/modules/audio_processing/aec3/dominant_nearend_detector.h +++ b/modules/audio_processing/aec3/dominant_nearend_detector.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/nearend_detector.h" @@ -33,20 +33,18 @@ class DominantNearendDetector : public NearendDetector { // Updates the state selection based on latest spectral estimates. void Update( - ArrayView> nearend_spectrum, - ArrayView> + std::span> nearend_spectrum, + std::span> residual_echo_spectrum, - ArrayView> + std::span> comfort_noise_spectrum, bool initial_state) override; + // Sets the configuration. + void SetConfig(const EchoCanceller3Config::Suppressor& config) override; + private: - const float enr_threshold_; - const float enr_exit_threshold_; - const float snr_threshold_; - const int hold_duration_; - const int trigger_threshold_; - const bool use_during_initial_phase_; + EchoCanceller3Config::Suppressor::DominantNearendDetection config_; const size_t num_capture_channels_; bool nearend_state_ = false; diff --git a/modules/audio_processing/aec3/echo_audibility.cc b/modules/audio_processing/aec3/echo_audibility.cc index 6853f511267..55191ef4ade 100644 --- a/modules/audio_processing/aec3/echo_audibility.cc +++ b/modules/audio_processing/aec3/echo_audibility.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block_buffer.h" #include "modules/audio_processing/aec3/render_buffer.h" @@ -33,7 +33,7 @@ EchoAudibility::EchoAudibility(bool use_render_stationarity_at_init) EchoAudibility::~EchoAudibility() = default; void EchoAudibility::Update(const RenderBuffer& render_buffer, - ArrayView average_reverb, + std::span average_reverb, int delay_blocks, bool external_delay_seen) { UpdateRenderNoiseEstimator(render_buffer.GetSpectrumBuffer(), @@ -53,7 +53,7 @@ void EchoAudibility::Reset() { void EchoAudibility::UpdateRenderStationarityFlags( const RenderBuffer& render_buffer, - ArrayView average_reverb, + std::span average_reverb, int min_channel_delay_blocks) { const SpectrumBuffer& spectrum_buffer = render_buffer.GetSpectrumBuffer(); int idx_at_delay = spectrum_buffer.OffsetIndex(spectrum_buffer.read, @@ -101,9 +101,9 @@ bool EchoAudibility::IsRenderTooLow(const BlockBuffer& block_buffer) { idx = block_buffer.IncIndex(idx)) { float max_abs_over_channels = 0.f; for (int ch = 0; ch < num_render_channels; ++ch) { - ArrayView block = + std::span block = block_buffer.buffer[idx].View(/*band=*/0, /*channel=*/ch); - auto r = std::minmax_element(block.cbegin(), block.cend()); + auto r = std::minmax_element(block.begin(), block.end()); float max_abs_channel = std::max(std::fabs(*r.first), std::fabs(*r.second)); max_abs_over_channels = diff --git a/modules/audio_processing/aec3/echo_audibility.h b/modules/audio_processing/aec3/echo_audibility.h index 44df9266c14..35edb5d5920 100644 --- a/modules/audio_processing/aec3/echo_audibility.h +++ b/modules/audio_processing/aec3/echo_audibility.h @@ -14,8 +14,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/block_buffer.h" #include "modules/audio_processing/aec3/render_buffer.h" #include "modules/audio_processing/aec3/spectrum_buffer.h" @@ -33,12 +33,12 @@ class EchoAudibility { // Feed new render data to the echo audibility estimator. void Update(const RenderBuffer& render_buffer, - ArrayView average_reverb, + std::span average_reverb, int min_channel_delay_blocks, bool external_delay_seen); // Get the residual echo scaling. void GetResidualEchoScaling(bool filter_has_had_time_to_converge, - ArrayView residual_scaling) const { + std::span residual_scaling) const { for (size_t band = 0; band < residual_scaling.size(); ++band) { if (render_stationarity_.IsBandStationary(band) && (filter_has_had_time_to_converge || @@ -61,7 +61,7 @@ class EchoAudibility { // Updates the render stationarity flags for the current frame. void UpdateRenderStationarityFlags(const RenderBuffer& render_buffer, - ArrayView average_reverb, + std::span average_reverb, int delay_blocks); // Updates the noise estimator with the new render data since the previous diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc index 7bf2aa3856e..5e99a5b3bc3 100644 --- a/modules/audio_processing/aec3/echo_canceller3.cc +++ b/modules/audio_processing/aec3/echo_canceller3.cc @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/echo_control.h" #include "api/audio/neural_residual_echo_estimator.h" @@ -45,7 +45,7 @@ namespace { enum class EchoCanceller3ApiCall { kCapture, kRender }; -bool DetectSaturation(ArrayView y) { +bool DetectSaturation(std::span y) { for (size_t k = 0; k < y.size(); ++k) { if (y[k] >= 32700.0f || y[k] <= -32700.0f) { return true; @@ -102,14 +102,14 @@ void RetrieveFieldTrialValue(const FieldTrialsView& field_trials, void FillSubFrameView( AudioBuffer* frame, size_t sub_frame_index, - std::vector>>* sub_frame_view) { + std::vector>>* sub_frame_view) { RTC_DCHECK_GE(1, sub_frame_index); RTC_DCHECK_LE(0, sub_frame_index); RTC_DCHECK_EQ(frame->num_bands(), sub_frame_view->size()); RTC_DCHECK_EQ(frame->num_channels(), (*sub_frame_view)[0].size()); for (size_t band = 0; band < sub_frame_view->size(); ++band) { for (size_t channel = 0; channel < (*sub_frame_view)[0].size(); ++channel) { - (*sub_frame_view)[band][channel] = ArrayView( + (*sub_frame_view)[band][channel] = std::span( &frame->split_bands(channel)[band][sub_frame_index * kSubFrameLength], kSubFrameLength); } @@ -120,7 +120,7 @@ void FillSubFrameView( bool proper_downmix_needed, std::vector>>* frame, size_t sub_frame_index, - std::vector>>* sub_frame_view) { + std::vector>>* sub_frame_view) { RTC_DCHECK_GE(1, sub_frame_index); RTC_DCHECK_EQ(frame->size(), sub_frame_view->size()); const size_t frame_num_channels = (*frame)[0].size(); @@ -148,7 +148,7 @@ void FillSubFrameView( } } for (size_t band = 0; band < frame->size(); ++band) { - (*sub_frame_view)[band][/*channel=*/0] = ArrayView( + (*sub_frame_view)[band][/*channel=*/0] = std::span( &(*frame)[band][/*channel=*/0][sub_frame_index * kSubFrameLength], kSubFrameLength); } @@ -156,7 +156,7 @@ void FillSubFrameView( RTC_DCHECK_EQ(frame_num_channels, sub_frame_num_channels); for (size_t band = 0; band < frame->size(); ++band) { for (size_t channel = 0; channel < (*frame)[band].size(); ++channel) { - (*sub_frame_view)[band][channel] = ArrayView( + (*sub_frame_view)[band][channel] = std::span( &(*frame)[band][channel][sub_frame_index * kSubFrameLength], kSubFrameLength); } @@ -176,9 +176,9 @@ void ProcessCaptureFrameContent( BlockFramer* output_framer, BlockProcessor* block_processor, Block* linear_output_block, - std::vector>>* linear_output_sub_frame_view, + std::vector>>* linear_output_sub_frame_view, Block* capture_block, - std::vector>>* capture_sub_frame_view) { + std::vector>>* capture_sub_frame_view) { FillSubFrameView(capture, sub_frame_index, capture_sub_frame_view); if (linear_output) { @@ -238,7 +238,7 @@ void BufferRenderFrameContent( FrameBlocker* render_blocker, BlockProcessor* block_processor, Block* block, - std::vector>>* sub_frame_view) { + std::vector>>* sub_frame_view) { FillSubFrameView(proper_downmix_needed, render_frame, sub_frame_index, sub_frame_view); render_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block); @@ -264,7 +264,7 @@ void CopyBufferIntoFrame(const AudioBuffer& buffer, RTC_DCHECK_EQ(AudioBuffer::kSplitBandSize, (*frame)[0][0].size()); for (size_t band = 0; band < num_bands; ++band) { for (size_t channel = 0; channel < num_channels; ++channel) { - ArrayView buffer_view( + std::span buffer_view( &buffer.split_bands_const(channel)[band][0], AudioBuffer::kSplitBandSize); std::copy(buffer_view.begin(), buffer_view.end(), @@ -790,7 +790,7 @@ EchoCanceller3::EchoCanceller3( capture_block_(num_bands_, num_capture_channels_), capture_sub_frame_view_( num_bands_, - std::vector>(num_capture_channels_)) { + std::vector>(num_capture_channels_)) { RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); if (config_selector_.active_config().delay.fixed_capture_delay_samples > 0) { @@ -811,8 +811,8 @@ EchoCanceller3::EchoCanceller3( new BlockFramer(/*num_bands=*/1, num_capture_channels_)); linear_output_block_ = std::make_unique(/*num_bands=*/1, num_capture_channels_); - linear_output_sub_frame_view_ = std::vector>>( - 1, std::vector>(num_capture_channels_)); + linear_output_sub_frame_view_ = std::vector>>( + 1, std::vector>(num_capture_channels_)); } Initialize(); @@ -845,8 +845,8 @@ void EchoCanceller3::Initialize() { num_render_channels_to_aec_, num_capture_channels_, neural_residual_echo_estimator_); - render_sub_frame_view_ = std::vector>>( - num_bands_, std::vector>(num_render_channels_to_aec_)); + render_sub_frame_view_ = std::vector>>( + num_bands_, std::vector>(num_render_channels_to_aec_)); } void EchoCanceller3::AnalyzeRender(const AudioBuffer& render) { @@ -865,7 +865,7 @@ void EchoCanceller3::AnalyzeCapture(const AudioBuffer& capture) { capture.channels_const()[0], sample_rate_hz_, 1); saturated_microphone_signal_ = false; for (size_t channel = 0; channel < capture.num_channels(); ++channel) { - saturated_microphone_signal_ |= DetectSaturation(ArrayView( + saturated_microphone_signal_ |= DetectSaturation(std::span( capture.channels_const()[channel], capture.num_frames())); if (saturated_microphone_signal_) { break; @@ -904,7 +904,7 @@ void EchoCanceller3::ProcessCapture(AudioBuffer* capture, block_delay_buffer_->DelaySignal(capture); } - ArrayView capture_lower_band = ArrayView( + std::span capture_lower_band = std::span( &capture->split_bands(0)[0][0], AudioBuffer::kSplitBandSize); data_dumper_->DumpWav("aec3_capture_input", capture_lower_band, 16000, 1); diff --git a/modules/audio_processing/aec3/echo_canceller3.h b/modules/audio_processing/aec3/echo_canceller3.h index 9005cc00b33..e12ee0abe9c 100644 --- a/modules/audio_processing/aec3/echo_canceller3.h +++ b/modules/audio_processing/aec3/echo_canceller3.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/echo_control.h" #include "api/audio/neural_residual_echo_estimator.h" @@ -220,11 +220,11 @@ class EchoCanceller3 : public EchoControl { std::unique_ptr linear_output_block_ RTC_GUARDED_BY(capture_race_checker_); Block capture_block_ RTC_GUARDED_BY(capture_race_checker_); - std::vector>> render_sub_frame_view_ + std::vector>> render_sub_frame_view_ RTC_GUARDED_BY(capture_race_checker_); - std::vector>> linear_output_sub_frame_view_ + std::vector>> linear_output_sub_frame_view_ RTC_GUARDED_BY(capture_race_checker_); - std::vector>> capture_sub_frame_view_ + std::vector>> capture_sub_frame_view_ RTC_GUARDED_BY(capture_race_checker_); std::unique_ptr block_delay_buffer_ RTC_GUARDED_BY(capture_race_checker_); diff --git a/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/modules/audio_processing/aec3/echo_canceller3_unittest.cc index 7057faeb3cc..a27b3e899a3 100644 --- a/modules/audio_processing/aec3/echo_canceller3_unittest.cc +++ b/modules/audio_processing/aec3/echo_canceller3_unittest.cc @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/echo_control.h" #include "api/audio/neural_residual_echo_estimator.h" @@ -100,8 +100,8 @@ bool VerifyOutputFrameBitexactness(size_t frame_length, return true; } -bool VerifyOutputFrameBitexactness(ArrayView reference, - ArrayView frame, +bool VerifyOutputFrameBitexactness(std::span reference, + std::span frame, int offset) { for (size_t k = 0; k < frame.size(); ++k) { int reference_index = static_cast(k) + offset; @@ -200,10 +200,10 @@ void RunAecInStereo(AudioBuffer& buffer, EchoCanceller3& aec3, float channel_0_value, float channel_1_value) { - ArrayView data_channel_0(&buffer.channels()[0][0], + std::span data_channel_0(&buffer.channels()[0][0], buffer.num_frames()); std::fill(data_channel_0.begin(), data_channel_0.end(), channel_0_value); - ArrayView data_channel_1(&buffer.channels()[1][0], + std::span data_channel_1(&buffer.channels()[1][0], buffer.num_frames()); std::fill(data_channel_1.begin(), data_channel_1.end(), channel_1_value); aec3.AnalyzeRender(&buffer); @@ -214,7 +214,7 @@ void RunAecInStereo(AudioBuffer& buffer, void RunAecInSMono(AudioBuffer& buffer, EchoCanceller3& aec3, float channel_0_value) { - ArrayView data_channel_0(&buffer.channels()[0][0], + std::span data_channel_0(&buffer.channels()[0][0], buffer.num_frames()); std::fill(data_channel_0.begin(), data_channel_0.end(), channel_0_value); aec3.AnalyzeRender(&buffer); @@ -1166,19 +1166,19 @@ TEST(EchoCanceller3, StereoContentDetectionForMonoSignals) { } TEST(EchoCanceller3, InjectedNeuralResidualEchoEstimatorIsUsed) { - class NeuralResidualEchoEstimatorImpl : public NeuralResidualEchoEstimator { + class NeuralResidualEchoEstimatorMock : public NeuralResidualEchoEstimator { public: - NeuralResidualEchoEstimatorImpl() {} + NeuralResidualEchoEstimatorMock() {} void Estimate(const Block& render, - ArrayView> capture, - ArrayView> linear_aec_output, - ArrayView> S2_linear, - ArrayView> Y2, - ArrayView> E2, + std::span> capture, + std::span> linear_aec_output, + std::span> S2_linear, + std::span> Y2, + std::span> E2, bool dominant_nearend, - ArrayView> R2, - ArrayView> R2_unbounded) override { + std::span> R2, + std::span> R2_unbounded) override { residual_echo_estimate_requested_ = true; for (auto& R2_ch : R2) { R2_ch.fill(0.0f); @@ -1195,13 +1195,20 @@ TEST(EchoCanceller3, InjectedNeuralResidualEchoEstimatorIsUsed) { return EchoCanceller3Config(); } + EchoCanceller3Config::Suppressor AdjustConfig( + const EchoCanceller3Config::Suppressor& config) const override { + return config; + } + + MOCK_METHOD(void, Reset, (), (override)); + private: bool residual_echo_estimate_requested_ = false; }; constexpr int kSampleRateHz = 16000; constexpr int kNumChannels = 1; - NeuralResidualEchoEstimatorImpl neural_residual_echo_estimator; + NeuralResidualEchoEstimatorMock neural_residual_echo_estimator; const Environment env = CreateEnvironment(); EchoCanceller3Config config; AudioBuffer buffer(/*input_rate=*/kSampleRateHz, diff --git a/modules/audio_processing/aec3/echo_path_delay_estimator.cc b/modules/audio_processing/aec3/echo_path_delay_estimator.cc index 30b66896b3c..db238d82bef 100644 --- a/modules/audio_processing/aec3/echo_path_delay_estimator.cc +++ b/modules/audio_processing/aec3/echo_path_delay_estimator.cc @@ -12,8 +12,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" @@ -67,7 +67,7 @@ std::optional EchoPathDelayEstimator::EstimateDelay( const DownsampledRenderBuffer& render_buffer, const Block& capture) { std::array downsampled_capture_data; - ArrayView downsampled_capture(downsampled_capture_data.data(), + std::span downsampled_capture(downsampled_capture_data.data(), sub_block_size_); std::array downmixed_capture; diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc index 1f0773780f2..9919b6fe4ff 100644 --- a/modules/audio_processing/aec3/echo_remover.cc +++ b/modules/audio_processing/aec3/echo_remover.cc @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/echo_control.h" #include "api/audio/neural_residual_echo_estimator.h" @@ -74,18 +74,17 @@ void LinearEchoPower(const FftData& E, } // Fades between two input signals using a fix-sized transition. -void SignalTransition(ArrayView from, - ArrayView to, - ArrayView out) { - if (from == to) { - RTC_DCHECK_EQ(to.size(), out.size()); +void SignalTransition(std::span from, + std::span to, + std::span out) { + RTC_DCHECK_EQ(from.size(), to.size()); + RTC_DCHECK_EQ(from.size(), out.size()); + if (from.data() == to.data()) { std::copy(to.begin(), to.end(), out.begin()); } else { constexpr size_t kTransitionSize = 30; constexpr float kOneByTransitionSizePlusOne = 1.f / (kTransitionSize + 1); - RTC_DCHECK_EQ(from.size(), to.size()); - RTC_DCHECK_EQ(from.size(), out.size()); RTC_DCHECK_LE(kTransitionSize, out.size()); for (size_t k = 0; k < kTransitionSize; ++k) { @@ -101,13 +100,52 @@ void SignalTransition(ArrayView from, // Computes a windowed (square root Hanning) padded FFT and updates the related // memory. void WindowedPaddedFft(const Aec3Fft& fft, - ArrayView v, - ArrayView v_old, + std::span v, + std::span v_old, FftData* V) { fft.PaddedFft(v, v_old, Aec3Fft::Window::kSqrtHanning, V); std::copy(v.begin(), v.end(), v_old.begin()); } +// Detects whether the output of the refined filter is more appropriate to use +// than the output of the coarse filter, returning the result as a bool. +bool UseRefinedOutput(const SubtractorOutput& subtractor_output) { + // As the output of the refined adaptive filter generally should be better + // than the coarse filter output, add a margin and threshold for when + // choosing the coarse filter output. + if (subtractor_output.e2_coarse < 0.9f * subtractor_output.e2_refined && + subtractor_output.y2 > 30.f * 30.f * kBlockSize && + (subtractor_output.s2_refined > 60.f * 60.f * kBlockSize || + subtractor_output.s2_coarse > 60.f * 60.f * kBlockSize)) { + return false; + } + + // If the refined filter is diverged, choose the filter output that has + // the lowest power. + if (subtractor_output.e2_coarse < subtractor_output.e2_refined && + subtractor_output.y2 < subtractor_output.e2_refined) { + return false; + } + return true; +} + +// Forms the linear filter output by smoothly transition between the refined and +// coarse filter outputs according to which of the filters is to be used. +void FormLinearFilterOutput(bool refined_filter_output_last_selected, + bool use_refined_output, + const SubtractorOutput& subtractor_output, + std::span output) { + RTC_DCHECK_EQ(subtractor_output.e_refined.size(), output.size()); + RTC_DCHECK_EQ(subtractor_output.e_coarse.size(), output.size()); + + SignalTransition(refined_filter_output_last_selected + ? subtractor_output.e_refined + : subtractor_output.e_coarse, + use_refined_output ? subtractor_output.e_refined + : subtractor_output.e_coarse, + output); +} + // Class for removing the echo from the capture signal. class EchoRemoverImpl final : public EchoRemover { public: @@ -144,14 +182,10 @@ class EchoRemoverImpl final : public EchoRemover { } private: - // Selects which of the coarse and refined linear filter outputs that is most - // appropriate to pass to the suppressor and forms the linear filter output by - // smoothly transition between those. - void FormLinearFilterOutput(const SubtractorOutput& subtractor_output, - ArrayView output); - static std::atomic instance_count_; const EchoCanceller3Config config_; + const std::optional + ml_ree_suppressor_config_; const Aec3Fft fft_; std::unique_ptr data_dumper_; const Aec3Optimization optimization_; @@ -174,6 +208,7 @@ class EchoRemoverImpl final : public EchoRemover { size_t block_counter_ = 0; int gain_change_hangover_ = 0; bool refined_filter_output_last_selected_ = true; + bool ml_ree_was_active_ = false; std::vector> e_heap_; std::vector> Y2_heap_; @@ -198,6 +233,11 @@ EchoRemoverImpl::EchoRemoverImpl( size_t num_capture_channels, NeuralResidualEchoEstimator* neural_residual_echo_estimator) : config_(config), + ml_ree_suppressor_config_( + neural_residual_echo_estimator + ? std::make_optional(neural_residual_echo_estimator->AdjustConfig( + config.suppressor)) + : std::nullopt), fft_(), data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), optimization_(DetectOptimization()), @@ -286,48 +326,48 @@ void EchoRemoverImpl::ProcessCapture( std::array high_band_comfort_noise_stack; std::array subtractor_output_stack; - ArrayView> e(e_stack.data(), + std::span> e(e_stack.data(), num_capture_channels_); - ArrayView> Y2(Y2_stack.data(), + std::span> Y2(Y2_stack.data(), num_capture_channels_); - ArrayView> E2(E2_stack.data(), + std::span> E2(E2_stack.data(), num_capture_channels_); - ArrayView> R2(R2_stack.data(), + std::span> R2(R2_stack.data(), num_capture_channels_); - ArrayView> R2_unbounded( + std::span> R2_unbounded( R2_unbounded_stack.data(), num_capture_channels_); - ArrayView> S2_linear( + std::span> S2_linear( S2_linear_stack.data(), num_capture_channels_); - ArrayView Y(Y_stack.data(), num_capture_channels_); - ArrayView E(E_stack.data(), num_capture_channels_); - ArrayView comfort_noise(comfort_noise_stack.data(), + std::span Y(Y_stack.data(), num_capture_channels_); + std::span E(E_stack.data(), num_capture_channels_); + std::span comfort_noise(comfort_noise_stack.data(), num_capture_channels_); - ArrayView high_band_comfort_noise( + std::span high_band_comfort_noise( high_band_comfort_noise_stack.data(), num_capture_channels_); - ArrayView subtractor_output(subtractor_output_stack.data(), + std::span subtractor_output(subtractor_output_stack.data(), num_capture_channels_); if (NumChannelsOnHeap(num_capture_channels_) > 0) { // If the stack-allocated space is too small, use the heap for storing the // microphone data. - e = ArrayView>(e_heap_.data(), + e = std::span>(e_heap_.data(), num_capture_channels_); - Y2 = ArrayView>( + Y2 = std::span>( Y2_heap_.data(), num_capture_channels_); - E2 = ArrayView>( + E2 = std::span>( E2_heap_.data(), num_capture_channels_); - R2 = ArrayView>( + R2 = std::span>( R2_heap_.data(), num_capture_channels_); - R2_unbounded = ArrayView>( + R2_unbounded = std::span>( R2_unbounded_heap_.data(), num_capture_channels_); - S2_linear = ArrayView>( + S2_linear = std::span>( S2_linear_heap_.data(), num_capture_channels_); - Y = ArrayView(Y_heap_.data(), num_capture_channels_); - E = ArrayView(E_heap_.data(), num_capture_channels_); + Y = std::span(Y_heap_.data(), num_capture_channels_); + E = std::span(E_heap_.data(), num_capture_channels_); comfort_noise = - ArrayView(comfort_noise_heap_.data(), num_capture_channels_); - high_band_comfort_noise = ArrayView( + std::span(comfort_noise_heap_.data(), num_capture_channels_); + high_band_comfort_noise = std::span( high_band_comfort_noise_heap_.data(), num_capture_channels_); - subtractor_output = ArrayView( + subtractor_output = std::span( subtractor_output_heap_.data(), num_capture_channels_); } @@ -384,15 +424,31 @@ void EchoRemoverImpl::ProcessCapture( subtractor_.Process(*render_buffer, *y, render_signal_analyzer_, aec_state_, subtractor_output); + const bool ml_ree_is_active = residual_echo_estimator_.IsMlReeActive(); + // Compute spectra. + bool use_refined_output; + if (use_coarse_filter_output_ && !ml_ree_is_active) { + use_refined_output = false; + for (size_t ch = 0; ch < num_capture_channels_; ++ch) { + if (UseRefinedOutput(subtractor_output[ch])) { + use_refined_output = true; + break; + } + } + } else { + use_refined_output = true; + } for (size_t ch = 0; ch < num_capture_channels_; ++ch) { - FormLinearFilterOutput(subtractor_output[ch], e[ch]); + FormLinearFilterOutput(refined_filter_output_last_selected_, + use_refined_output, subtractor_output[ch], e[ch]); WindowedPaddedFft(fft_, y->View(/*band=*/0, ch), y_old_[ch], &Y[ch]); WindowedPaddedFft(fft_, e[ch], e_old_[ch], &E[ch]); LinearEchoPower(E[ch], Y[ch], &S2_linear[ch]); Y[ch].Spectrum(optimization_, Y2[ch]); E[ch].Spectrum(optimization_, E2[ch]); } + refined_filter_output_last_selected_ = use_refined_output; const auto& nearend_spectrum = aec_state_.UsableLinearEstimate() ? E2 : Y2; // `y_old_` and `e_old_` now point to the current block. Though their channel // layout is already suitable for residual echo estimation, an alias is @@ -453,9 +509,20 @@ void EchoRemoverImpl::ProcessCapture( const bool clock_drift = config_.echo_removal_control.has_clock_drift || echo_path_variability.clock_drift; + // Select the active suppressor configuration. + const EchoCanceller3Config::Suppressor& active_suppressor_config = + (ml_ree_is_active && ml_ree_suppressor_config_.has_value()) + ? *ml_ree_suppressor_config_ + : config_.suppressor; + + // Determine if the configuration affecting the suppressor has changed. + const bool config_changed = (ml_ree_is_active != ml_ree_was_active_); + ml_ree_was_active_ = ml_ree_is_active; + // Compute preferred gains. float high_bands_gain; - suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2, R2_unbounded, + suppression_gain_.GetGain(active_suppressor_config, config_changed, + nearend_spectrum, echo_spectrum, R2, R2_unbounded, cng_.NoiseSpectrum(), render_signal_analyzer_, aec_state_, x, clock_drift, &high_bands_gain, &G); @@ -494,40 +561,6 @@ void EchoRemoverImpl::ProcessCapture( aec_state_.SaturatedCapture() ? 1 : 0); } -void EchoRemoverImpl::FormLinearFilterOutput( - const SubtractorOutput& subtractor_output, - ArrayView output) { - RTC_DCHECK_EQ(subtractor_output.e_refined.size(), output.size()); - RTC_DCHECK_EQ(subtractor_output.e_coarse.size(), output.size()); - bool use_refined_output = true; - if (use_coarse_filter_output_) { - // As the output of the refined adaptive filter generally should be better - // than the coarse filter output, add a margin and threshold for when - // choosing the coarse filter output. - if (subtractor_output.e2_coarse < 0.9f * subtractor_output.e2_refined && - subtractor_output.y2 > 30.f * 30.f * kBlockSize && - (subtractor_output.s2_refined > 60.f * 60.f * kBlockSize || - subtractor_output.s2_coarse > 60.f * 60.f * kBlockSize)) { - use_refined_output = false; - } else { - // If the refined filter is diverged, choose the filter output that has - // the lowest power. - if (subtractor_output.e2_coarse < subtractor_output.e2_refined && - subtractor_output.y2 < subtractor_output.e2_refined) { - use_refined_output = false; - } - } - } - - SignalTransition(refined_filter_output_last_selected_ - ? subtractor_output.e_refined - : subtractor_output.e_coarse, - use_refined_output ? subtractor_output.e_refined - : subtractor_output.e_coarse, - output); - refined_filter_output_last_selected_ = use_refined_output; -} - } // namespace std::unique_ptr EchoRemover::Create( diff --git a/modules/audio_processing/aec3/echo_remover_metrics.cc b/modules/audio_processing/aec3/echo_remover_metrics.cc index fce3964da53..a3f31675b0a 100644 --- a/modules/audio_processing/aec3/echo_remover_metrics.cc +++ b/modules/audio_processing/aec3/echo_remover_metrics.cc @@ -78,35 +78,35 @@ void EchoRemoverMetrics::Update( case kMetricsCollectionBlocks + 2: RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.EchoCanceller.Erl.Value", - aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, - erl_time_domain_.sum_value), + TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.sum_value), 0, 59, 30); RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.EchoCanceller.Erl.Max", - aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, - erl_time_domain_.ceil_value), + TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.ceil_value), 0, 59, 30); RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.EchoCanceller.Erl.Min", - aec3::TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, - erl_time_domain_.floor_value), + TransformDbMetricForReporting(true, 0.f, 59.f, 30.f, 1.f, + erl_time_domain_.floor_value), 0, 59, 30); break; case kMetricsCollectionBlocks + 3: RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.EchoCanceller.Erle.Value", - aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, - erle_time_domain_.sum_value), + TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.sum_value), 0, 19, 20); RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.EchoCanceller.Erle.Max", - aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, - erle_time_domain_.ceil_value), + TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.ceil_value), 0, 19, 20); RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.EchoCanceller.Erle.Min", - aec3::TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, - erle_time_domain_.floor_value), + TransformDbMetricForReporting(false, 0.f, 19.f, 0.f, 1.f, + erle_time_domain_.floor_value), 0, 19, 20); metrics_reported_ = true; RTC_DCHECK_EQ(kMetricsReportingIntervalBlocks, block_counter_); @@ -120,8 +120,6 @@ void EchoRemoverMetrics::Update( } } -namespace aec3 { - void UpdateDbMetric(const std::array& value, std::array* statistic) { RTC_DCHECK(statistic); @@ -153,6 +151,4 @@ int TransformDbMetricForReporting(bool negate, return static_cast(SafeClamp(new_value, min_value, max_value)); } -} // namespace aec3 - } // namespace webrtc diff --git a/modules/audio_processing/aec3/echo_remover_metrics.h b/modules/audio_processing/aec3/echo_remover_metrics.h index aec8084d785..ebcd27772fc 100644 --- a/modules/audio_processing/aec3/echo_remover_metrics.h +++ b/modules/audio_processing/aec3/echo_remover_metrics.h @@ -56,8 +56,6 @@ class EchoRemoverMetrics { bool metrics_reported_ = false; }; -namespace aec3 { - // Updates a banded metric of type DbMetric with the values in the supplied // array. void UpdateDbMetric(const std::array& value, @@ -71,8 +69,6 @@ int TransformDbMetricForReporting(bool negate, float scaling, float value); -} // namespace aec3 - } // namespace webrtc #endif // MODULES_AUDIO_PROCESSING_AEC3_ECHO_REMOVER_METRICS_H_ diff --git a/modules/audio_processing/aec3/echo_remover_metrics_unittest.cc b/modules/audio_processing/aec3/echo_remover_metrics_unittest.cc index 85b04b77e09..b087ad152f7 100644 --- a/modules/audio_processing/aec3/echo_remover_metrics_unittest.cc +++ b/modules/audio_processing/aec3/echo_remover_metrics_unittest.cc @@ -31,7 +31,7 @@ namespace webrtc { TEST(UpdateDbMetricDeathTest, NullValue) { std::array value; value.fill(0.f); - EXPECT_DEATH(aec3::UpdateDbMetric(value, nullptr), ""); + EXPECT_DEATH(UpdateDbMetric(value, nullptr), ""); } #endif @@ -46,7 +46,7 @@ TEST(UpdateDbMetric, Updating) { std::fill(value.begin(), value.begin() + 32, kValue0); std::fill(value.begin() + 32, value.begin() + 64, kValue1); - aec3::UpdateDbMetric(value, &statistic); + UpdateDbMetric(value, &statistic); EXPECT_FLOAT_EQ(kValue0, statistic[0].sum_value); EXPECT_FLOAT_EQ(kValue0, statistic[0].ceil_value); EXPECT_FLOAT_EQ(kValue0, statistic[0].floor_value); @@ -54,7 +54,7 @@ TEST(UpdateDbMetric, Updating) { EXPECT_FLOAT_EQ(kValue1, statistic[1].ceil_value); EXPECT_FLOAT_EQ(kValue1, statistic[1].floor_value); - aec3::UpdateDbMetric(value, &statistic); + UpdateDbMetric(value, &statistic); EXPECT_FLOAT_EQ(2.f * kValue0, statistic[0].sum_value); EXPECT_FLOAT_EQ(kValue0, statistic[0].ceil_value); EXPECT_FLOAT_EQ(kValue0, statistic[0].floor_value); @@ -78,26 +78,26 @@ TEST(TransformDbMetricForReporting, DbFsScaling) { EXPECT_NEAR(offset, -90.3f, 0.1f); EXPECT_EQ( static_cast(30.3f), - aec3::TransformDbMetricForReporting( - true, 0.f, 90.f, offset, 1.f / (kBlockSize * kBlockSize), X2[0])); + TransformDbMetricForReporting(true, 0.f, 90.f, offset, + 1.f / (kBlockSize * kBlockSize), X2[0])); } // Verifies that the TransformDbMetricForReporting method is able to properly // limit the output. TEST(TransformDbMetricForReporting, Limits) { - EXPECT_EQ(0, aec3::TransformDbMetricForReporting(false, 0.f, 10.f, 0.f, 1.f, - 0.001f)); - EXPECT_EQ(10, aec3::TransformDbMetricForReporting(false, 0.f, 10.f, 0.f, 1.f, - 100.f)); + EXPECT_EQ(0, + TransformDbMetricForReporting(false, 0.f, 10.f, 0.f, 1.f, 0.001f)); + EXPECT_EQ(10, + TransformDbMetricForReporting(false, 0.f, 10.f, 0.f, 1.f, 100.f)); } // Verifies that the TransformDbMetricForReporting method is able to properly // negate output. TEST(TransformDbMetricForReporting, Negate) { - EXPECT_EQ(10, aec3::TransformDbMetricForReporting(true, -20.f, 20.f, 0.f, 1.f, - 0.1f)); - EXPECT_EQ(-10, aec3::TransformDbMetricForReporting(true, -20.f, 20.f, 0.f, - 1.f, 10.f)); + EXPECT_EQ(10, + TransformDbMetricForReporting(true, -20.f, 20.f, 0.f, 1.f, 0.1f)); + EXPECT_EQ(-10, + TransformDbMetricForReporting(true, -20.f, 20.f, 0.f, 1.f, 10.f)); } // Verify the Update functionality of DbMetric. diff --git a/modules/audio_processing/aec3/erl_estimator.cc b/modules/audio_processing/aec3/erl_estimator.cc index 2bf6ee9e724..d5894cf0a6f 100644 --- a/modules/audio_processing/aec3/erl_estimator.cc +++ b/modules/audio_processing/aec3/erl_estimator.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" @@ -46,8 +46,8 @@ void ErlEstimator::Reset() { void ErlEstimator::Update( const std::vector& converged_filters, - ArrayView> render_spectra, - ArrayView> capture_spectra) { + std::span> render_spectra, + std::span> capture_spectra) { const size_t num_capture_channels = converged_filters.size(); RTC_DCHECK_EQ(capture_spectra.size(), num_capture_channels); @@ -90,7 +90,7 @@ void ErlEstimator::Update( const size_t num_render_channels = render_spectra.size(); std::array max_render_spectrum_data; - ArrayView max_render_spectrum = + std::span max_render_spectrum = render_spectra[/*channel=*/0]; if (num_render_channels > 1) { std::copy(render_spectra[0].begin(), render_spectra[0].end(), diff --git a/modules/audio_processing/aec3/erl_estimator.h b/modules/audio_processing/aec3/erl_estimator.h index b793ddec780..2b2ab4342e8 100644 --- a/modules/audio_processing/aec3/erl_estimator.h +++ b/modules/audio_processing/aec3/erl_estimator.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" namespace webrtc { @@ -36,8 +36,8 @@ class ErlEstimator { // Updates the ERL estimate. void Update( const std::vector& converged_filters, - ArrayView> render_spectra, - ArrayView> capture_spectra); + std::span> render_spectra, + std::span> capture_spectra); // Returns the most recent ERL estimate. const std::array& Erl() const { return erl_; } diff --git a/modules/audio_processing/aec3/erle_estimator.cc b/modules/audio_processing/aec3/erle_estimator.cc index 15de3775940..206103e42bf 100644 --- a/modules/audio_processing/aec3/erle_estimator.cc +++ b/modules/audio_processing/aec3/erle_estimator.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -56,11 +56,11 @@ void ErleEstimator::Reset(bool delay_change) { void ErleEstimator::Update( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_responses, - ArrayView avg_render_spectrum_with_reverb, - ArrayView> capture_spectra, - ArrayView> subtractor_spectra, + std::span avg_render_spectrum_with_reverb, + std::span> capture_spectra, + std::span> subtractor_spectra, const std::vector& converged_filters) { RTC_DCHECK_EQ(subband_erle_estimator_.Erle(/*onset_compensated=*/true).size(), capture_spectra.size()); diff --git a/modules/audio_processing/aec3/erle_estimator.h b/modules/audio_processing/aec3/erle_estimator.h index fe6e71a3556..fb275107ff2 100644 --- a/modules/audio_processing/aec3/erle_estimator.h +++ b/modules/audio_processing/aec3/erle_estimator.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -46,16 +46,16 @@ class ErleEstimator { // Updates the ERLE estimates. void Update( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_responses, - ArrayView + std::span avg_render_spectrum_with_reverb, - ArrayView> capture_spectra, - ArrayView> subtractor_spectra, + std::span> capture_spectra, + std::span> subtractor_spectra, const std::vector& converged_filters); // Returns the most recent subband ERLE estimates. - ArrayView> Erle( + std::span> Erle( bool onset_compensated) const { return signal_dependent_erle_estimator_ ? signal_dependent_erle_estimator_->Erle(onset_compensated) @@ -63,7 +63,7 @@ class ErleEstimator { } // Returns the non-capped subband ERLE. - ArrayView> ErleUnbounded() const { + std::span> ErleUnbounded() const { // Unbounded ERLE is only used with the subband erle estimator where the // ERLE is often capped at low values. When the signal dependent ERLE // estimator is used the capped ERLE is returned. @@ -75,7 +75,7 @@ class ErleEstimator { // Returns the subband ERLE that are estimated during onsets (only used for // testing). - ArrayView> ErleDuringOnsets() + std::span> ErleDuringOnsets() const { return subband_erle_estimator_.ErleDuringOnsets(); } @@ -90,7 +90,7 @@ class ErleEstimator { // vector with content between 0 and 1 where 1 indicates that, at this current // time instant, the linear filter is reaching its maximum subtraction // performance. - ArrayView> GetInstLinearQualityEstimates() const { + std::span> GetInstLinearQualityEstimates() const { return fullband_erle_estimator_.GetInstLinearQualityEstimates(); } diff --git a/modules/audio_processing/aec3/erle_estimator_unittest.cc b/modules/audio_processing/aec3/erle_estimator_unittest.cc index ce75cfcafe0..a6497386e9a 100644 --- a/modules/audio_processing/aec3/erle_estimator_unittest.cc +++ b/modules/audio_processing/aec3/erle_estimator_unittest.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment_factory.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -38,7 +38,7 @@ constexpr float kTrueErleOnsets = 1.0f; constexpr float kEchoPathGain = 3.f; void VerifyErleBands( - ArrayView> erle, + std::span> erle, float reference_lf, float reference_hf) { for (size_t ch = 0; ch < erle.size(); ++ch) { @@ -51,7 +51,7 @@ void VerifyErleBands( } } -void VerifyErle(ArrayView> erle, +void VerifyErle(std::span> erle, float erle_time_domain, float reference_lf, float reference_hf) { @@ -60,8 +60,8 @@ void VerifyErle(ArrayView> erle, } void VerifyErleGreaterOrEqual( - ArrayView> erle1, - ArrayView> erle2) { + std::span> erle1, + std::span> erle2) { for (size_t ch = 0; ch < erle1.size(); ++ch) { for (size_t i = 0; i < kFftLengthBy2Plus1; ++i) { EXPECT_GE(erle1[ch][i], erle2[ch][i]); @@ -90,8 +90,8 @@ void FormFarendTimeFrame(Block* x) { void FormFarendFrame(const RenderBuffer& render_buffer, float erle, std::array* X2, - ArrayView> E2, - ArrayView> Y2) { + std::span> E2, + std::span> Y2) { const auto& spectrum_buffer = render_buffer.GetSpectrumBuffer(); const int num_render_channels = spectrum_buffer.buffer[0].size(); const int num_capture_channels = Y2.size(); @@ -114,8 +114,8 @@ void FormFarendFrame(const RenderBuffer& render_buffer, void FormNearendFrame(Block* x, std::array* X2, - ArrayView> E2, - ArrayView> Y2) { + std::span> E2, + std::span> Y2) { for (int band = 0; band < x->NumBands(); ++band) { for (int ch = 0; ch < x->NumChannels(); ++ch) { std::fill(x->begin(band, ch), x->end(band, ch), 0.f); @@ -130,7 +130,7 @@ void FormNearendFrame(Block* x, } void GetFilterFreq(size_t delay_headroom_samples, - ArrayView>> + std::span>> filter_frequency_response) { const size_t delay_headroom_blocks = delay_headroom_samples / kBlockSize; for (size_t ch = 0; ch < filter_frequency_response[0].size(); ++ch) { diff --git a/modules/audio_processing/aec3/fft_data.h b/modules/audio_processing/aec3/fft_data.h index b2274963881..6bab8c9c1aa 100644 --- a/modules/audio_processing/aec3/fft_data.h +++ b/modules/audio_processing/aec3/fft_data.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" @@ -43,11 +43,11 @@ struct FftData { } // Computes the power spectrum of the data. - void SpectrumAVX2(ArrayView power_spectrum) const; + void SpectrumAVX2(std::span power_spectrum) const; // Computes the power spectrum of the data. void Spectrum(Aec3Optimization optimization, - ArrayView power_spectrum) const { + std::span power_spectrum) const { RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size()); switch (optimization) { #if defined(WEBRTC_ARCH_X86_FAMILY) diff --git a/modules/audio_processing/aec3/fft_data_avx2.cc b/modules/audio_processing/aec3/fft_data_avx2.cc index eaa93c0db10..97c34dfc31a 100644 --- a/modules/audio_processing/aec3/fft_data_avx2.cc +++ b/modules/audio_processing/aec3/fft_data_avx2.cc @@ -11,8 +11,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" #include "rtc_base/checks.h" @@ -20,7 +20,7 @@ namespace webrtc { // Computes the power spectrum of the data. -void FftData::SpectrumAVX2(ArrayView power_spectrum) const { +void FftData::SpectrumAVX2(std::span power_spectrum) const { RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size()); for (size_t k = 0; k < kFftLengthBy2; k += 8) { __m256 r = _mm256_loadu_ps(&re[k]); diff --git a/modules/audio_processing/aec3/fft_data_unittest.cc b/modules/audio_processing/aec3/fft_data_unittest.cc index a089c7b7bc6..acf42a5e3d2 100644 --- a/modules/audio_processing/aec3/fft_data_unittest.cc +++ b/modules/audio_processing/aec3/fft_data_unittest.cc @@ -78,7 +78,7 @@ TEST(FftDataDeathTest, NonNullCopyToPackedArrayOutput) { // Verifies the check for null output in Spectrum. TEST(FftDataDeathTest, NonNullSpectrumOutput) { - EXPECT_DEATH(FftData().Spectrum(Aec3Optimization::kNone, nullptr), ""); + EXPECT_DEATH(FftData().Spectrum(Aec3Optimization::kNone, {}), ""); } #endif diff --git a/modules/audio_processing/aec3/filter_analyzer.cc b/modules/audio_processing/aec3/filter_analyzer.cc index cf64737109b..e9445cf6641 100644 --- a/modules/audio_processing/aec3/filter_analyzer.cc +++ b/modules/audio_processing/aec3/filter_analyzer.cc @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" @@ -29,7 +29,7 @@ namespace webrtc { namespace { -size_t FindPeakIndex(ArrayView filter_time_domain, +size_t FindPeakIndex(std::span filter_time_domain, size_t peak_index_in, size_t start_sample, size_t end_sample) { @@ -78,7 +78,7 @@ void FilterAnalyzer::Reset() { } void FilterAnalyzer::Update( - ArrayView> filters_time_domain, + std::span> filters_time_domain, const RenderBuffer& render_buffer, bool* any_filter_consistent, float* max_echo_path_gain) { @@ -107,7 +107,7 @@ void FilterAnalyzer::Update( } void FilterAnalyzer::AnalyzeRegion( - ArrayView> filters_time_domain, + std::span> filters_time_domain, const RenderBuffer& render_buffer) { // Preprocess the filter to avoid issues with low-frequency components in the // filter. @@ -139,7 +139,7 @@ void FilterAnalyzer::AnalyzeRegion( } } -void FilterAnalyzer::UpdateFilterGain(ArrayView filter_time_domain, +void FilterAnalyzer::UpdateFilterGain(std::span filter_time_domain, FilterAnalysisState* st) { bool sufficient_time_to_converge = blocks_since_reset_ > 5 * kNumBlocksPerSecond; @@ -159,7 +159,7 @@ void FilterAnalyzer::UpdateFilterGain(ArrayView filter_time_domain, } void FilterAnalyzer::PreProcessFilters( - ArrayView> filters_time_domain) { + std::span> filters_time_domain) { for (size_t ch = 0; ch < filters_time_domain.size(); ++ch) { RTC_DCHECK_LT(region_.start_sample_, filters_time_domain[ch].size()); RTC_DCHECK_LT(region_.end_sample_, filters_time_domain[ch].size()); @@ -224,7 +224,7 @@ void FilterAnalyzer::ConsistentFilterDetector::Reset() { } bool FilterAnalyzer::ConsistentFilterDetector::Detect( - ArrayView filter_to_analyze, + std::span filter_to_analyze, const FilterRegion& region, const Block& x_block, size_t peak_index, @@ -268,7 +268,7 @@ bool FilterAnalyzer::ConsistentFilterDetector::Detect( if (significant_peak_) { bool active_render_block = false; for (int ch = 0; ch < x_block.NumChannels(); ++ch) { - ArrayView x_channel = + std::span x_channel = x_block.View(/*band=*/0, ch); const float x_energy = std::inner_product( x_channel.begin(), x_channel.end(), x_channel.begin(), 0.f); diff --git a/modules/audio_processing/aec3/filter_analyzer.h b/modules/audio_processing/aec3/filter_analyzer.h index 83c48650c5b..28c7912f9de 100644 --- a/modules/audio_processing/aec3/filter_analyzer.h +++ b/modules/audio_processing/aec3/filter_analyzer.h @@ -15,9 +15,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/block.h" @@ -40,13 +40,13 @@ class FilterAnalyzer { void Reset(); // Updates the estimates with new input data. - void Update(ArrayView> filters_time_domain, + void Update(std::span> filters_time_domain, const RenderBuffer& render_buffer, bool* any_filter_consistent, float* max_echo_path_gain); // Returns the delay in blocks for each filter. - ArrayView FilterDelaysBlocks() const { + std::span FilterDelaysBlocks() const { return filter_delays_blocks_; } @@ -59,7 +59,7 @@ class FilterAnalyzer { } // Returns the preprocessed filter. - ArrayView> GetAdjustedFilters() const { + std::span> GetAdjustedFilters() const { return h_highpass_; } @@ -69,13 +69,13 @@ class FilterAnalyzer { private: struct FilterAnalysisState; - void AnalyzeRegion(ArrayView> filters_time_domain, + void AnalyzeRegion(std::span> filters_time_domain, const RenderBuffer& render_buffer); - void UpdateFilterGain(ArrayView filters_time_domain, + void UpdateFilterGain(std::span filters_time_domain, FilterAnalysisState* st); void PreProcessFilters( - ArrayView> filters_time_domain); + std::span> filters_time_domain); void ResetRegion(); @@ -90,7 +90,7 @@ class FilterAnalyzer { public: explicit ConsistentFilterDetector(const EchoCanceller3Config& config); void Reset(); - bool Detect(ArrayView filter_to_analyze, + bool Detect(std::span filter_to_analyze, const FilterRegion& region, const Block& x_block, size_t peak_index, diff --git a/modules/audio_processing/aec3/frame_blocker.cc b/modules/audio_processing/aec3/frame_blocker.cc index 97246e14ca0..2b93e7a65b8 100644 --- a/modules/audio_processing/aec3/frame_blocker.cc +++ b/modules/audio_processing/aec3/frame_blocker.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "rtc_base/checks.h" @@ -38,7 +38,7 @@ FrameBlocker::FrameBlocker(size_t num_bands, size_t num_channels) FrameBlocker::~FrameBlocker() = default; void FrameBlocker::InsertSubFrameAndExtractBlock( - const std::vector>>& sub_frame, + const std::vector>>& sub_frame, Block* block) { RTC_DCHECK(block); RTC_DCHECK_EQ(num_bands_, block->NumBands()); diff --git a/modules/audio_processing/aec3/frame_blocker.h b/modules/audio_processing/aec3/frame_blocker.h index f9dd88b76a0..f035fed33fc 100644 --- a/modules/audio_processing/aec3/frame_blocker.h +++ b/modules/audio_processing/aec3/frame_blocker.h @@ -13,9 +13,9 @@ #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/block.h" namespace webrtc { @@ -32,7 +32,7 @@ class FrameBlocker { // Inserts one 80 sample multiband subframe from the multiband frame and // extracts one 64 sample multiband block. void InsertSubFrameAndExtractBlock( - const std::vector>>& sub_frame, + const std::vector>>& sub_frame, Block* block); // Reports whether a multiband block of 64 samples is available for // extraction. diff --git a/modules/audio_processing/aec3/frame_blocker_unittest.cc b/modules/audio_processing/aec3/frame_blocker_unittest.cc index d32cd80b293..eadfe71dfcd 100644 --- a/modules/audio_processing/aec3/frame_blocker_unittest.cc +++ b/modules/audio_processing/aec3/frame_blocker_unittest.cc @@ -11,10 +11,10 @@ #include "modules/audio_processing/aec3/frame_blocker.h" #include +#include #include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "modules/audio_processing/aec3/block_framer.h" @@ -55,12 +55,12 @@ void FillSubFrameView( size_t sub_frame_counter, int offset, std::vector>>* sub_frame, - std::vector>>* sub_frame_view) { + std::vector>>* sub_frame_view) { FillSubFrame(sub_frame_counter, offset, sub_frame); for (size_t band = 0; band < sub_frame_view->size(); ++band) { for (size_t channel = 0; channel < (*sub_frame_view)[band].size(); ++channel) { - (*sub_frame_view)[band][channel] = ArrayView( + (*sub_frame_view)[band][channel] = std::span( &(*sub_frame)[band][channel][0], (*sub_frame)[band][channel].size()); } } @@ -69,7 +69,7 @@ void FillSubFrameView( bool VerifySubFrame( size_t sub_frame_counter, int offset, - const std::vector>>& sub_frame_view) { + const std::vector>>& sub_frame_view) { std::vector>> reference_sub_frame( sub_frame_view.size(), std::vector>( @@ -115,8 +115,8 @@ void RunBlockerTest(int sample_rate_hz, size_t num_channels) { std::vector>> input_sub_frame( num_bands, std::vector>( num_channels, std::vector(kSubFrameLength, 0.f))); - std::vector>> input_sub_frame_view( - num_bands, std::vector>(num_channels)); + std::vector>> input_sub_frame_view( + num_bands, std::vector>(num_channels)); FrameBlocker blocker(num_bands, num_channels); size_t block_counter = 0; @@ -153,10 +153,10 @@ void RunBlockerAndFramerTest(int sample_rate_hz, size_t num_channels) { std::vector>> output_sub_frame( num_bands, std::vector>( num_channels, std::vector(kSubFrameLength, 0.f))); - std::vector>> output_sub_frame_view( - num_bands, std::vector>(num_channels)); - std::vector>> input_sub_frame_view( - num_bands, std::vector>(num_channels)); + std::vector>> output_sub_frame_view( + num_bands, std::vector>(num_channels)); + std::vector>> input_sub_frame_view( + num_bands, std::vector>(num_channels)); FrameBlocker blocker(num_bands, num_channels); BlockFramer framer(num_bands, num_channels); @@ -203,9 +203,9 @@ void RunWronglySizedInsertAndExtractParametersTest( num_sub_frame_bands, std::vector>( num_sub_frame_channels, std::vector(sub_frame_length, 0.f))); - std::vector>> input_sub_frame_view( + std::vector>> input_sub_frame_view( input_sub_frame.size(), - std::vector>(num_sub_frame_channels)); + std::vector>(num_sub_frame_channels)); FillSubFrameView(0, 0, &input_sub_frame, &input_sub_frame_view); FrameBlocker blocker(correct_num_bands, correct_num_channels); EXPECT_DEATH( @@ -226,9 +226,9 @@ void RunWronglySizedExtractParameterTest(int sample_rate_hz, correct_num_bands, std::vector>( correct_num_channels, std::vector(kSubFrameLength, 0.f))); - std::vector>> input_sub_frame_view( + std::vector>> input_sub_frame_view( input_sub_frame.size(), - std::vector>(correct_num_channels)); + std::vector>(correct_num_channels)); FillSubFrameView(0, 0, &input_sub_frame, &input_sub_frame_view); FrameBlocker blocker(correct_num_bands, correct_num_channels); blocker.InsertSubFrameAndExtractBlock(input_sub_frame_view, &correct_block); @@ -251,8 +251,8 @@ void RunWrongExtractOrderTest(int sample_rate_hz, std::vector>> input_sub_frame( num_bands, std::vector>( num_channels, std::vector(kSubFrameLength, 0.f))); - std::vector>> input_sub_frame_view( - input_sub_frame.size(), std::vector>(num_channels)); + std::vector>> input_sub_frame_view( + input_sub_frame.size(), std::vector>(num_channels)); FillSubFrameView(0, 0, &input_sub_frame, &input_sub_frame_view); FrameBlocker blocker(num_bands, num_channels); for (size_t k = 0; k < num_preceeding_api_calls; ++k) { @@ -398,7 +398,7 @@ TEST(FrameBlockerDeathTest, NullBlockParameter) { std::vector>> sub_frame( 1, std::vector>( 1, std::vector(kSubFrameLength, 0.f))); - std::vector>> sub_frame_view(sub_frame.size()); + std::vector>> sub_frame_view(sub_frame.size()); FillSubFrameView(0, 0, &sub_frame, &sub_frame_view); EXPECT_DEATH( FrameBlocker(1, 1).InsertSubFrameAndExtractBlock(sub_frame_view, nullptr), diff --git a/modules/audio_processing/aec3/fullband_erle_estimator.cc b/modules/audio_processing/aec3/fullband_erle_estimator.cc index 07acfa98a32..e31ec293319 100644 --- a/modules/audio_processing/aec3/fullband_erle_estimator.cc +++ b/modules/audio_processing/aec3/fullband_erle_estimator.cc @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/logging/apm_data_dumper.h" @@ -60,9 +60,9 @@ void FullBandErleEstimator::Reset() { } void FullBandErleEstimator::Update( - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters) { for (size_t ch = 0; ch < Y2.size(); ++ch) { if (converged_filters[ch]) { diff --git a/modules/audio_processing/aec3/fullband_erle_estimator.h b/modules/audio_processing/aec3/fullband_erle_estimator.h index bb710c96d0c..17af9587508 100644 --- a/modules/audio_processing/aec3/fullband_erle_estimator.h +++ b/modules/audio_processing/aec3/fullband_erle_estimator.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/logging/apm_data_dumper.h" @@ -36,9 +36,9 @@ class FullBandErleEstimator { void Reset(); // Updates the ERLE estimator. - void Update(ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + void Update(std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters); // Returns the fullband ERLE estimates in log2 units. @@ -52,7 +52,7 @@ class FullBandErleEstimator { // Returns an estimation of the current linear filter quality. It returns a // float number between 0 and 1 mapping 1 to the highest possible quality. - ArrayView> GetInstLinearQualityEstimates() const { + std::span> GetInstLinearQualityEstimates() const { return linear_filters_qualities_; } diff --git a/modules/audio_processing/aec3/matched_filter.cc b/modules/audio_processing/aec3/matched_filter.cc index 866f9416974..69962e9f1a3 100644 --- a/modules/audio_processing/aec3/matched_filter.cc +++ b/modules/audio_processing/aec3/matched_filter.cc @@ -25,14 +25,16 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/downsampled_render_buffer.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +namespace webrtc { + namespace { // Subsample rate used for computing the accumulated error. @@ -41,8 +43,8 @@ namespace { constexpr int kAccumulatedErrorSubSampleRate = 4; void UpdateAccumulatedError( - const webrtc::ArrayView instantaneous_accumulated_error, - const webrtc::ArrayView accumulated_error, + const std::span instantaneous_accumulated_error, + const std::span accumulated_error, float one_over_error_sum_anchor) { static constexpr float kSmoothConstantIncreases = 0.015f; for (size_t k = 0; k < instantaneous_accumulated_error.size(); ++k) { @@ -57,7 +59,7 @@ void UpdateAccumulatedError( } } -size_t ComputePreEchoLag(const webrtc::ArrayView accumulated_error, +size_t ComputePreEchoLag(const std::span accumulated_error, size_t lag, size_t alignment_shift_winner) { static constexpr float kPreEchoThreshold = 0.5f; @@ -77,9 +79,6 @@ size_t ComputePreEchoLag(const webrtc::ArrayView accumulated_error, } // namespace -namespace webrtc { -namespace aec3 { - #if defined(WEBRTC_HAS_NEON) inline float SumAllElements(float32x4_t elements) { @@ -92,13 +91,13 @@ void MatchedFilterCoreWithAccumulatedError_NEON( size_t x_start_index, float x2_sum_threshold, float smoothing, - webrtc::ArrayView x, - webrtc::ArrayView y, - webrtc::ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, - webrtc::ArrayView accumulated_error, - webrtc::ArrayView scratch_memory) { + std::span accumulated_error, + std::span scratch_memory) { const int h_size = static_cast(h.size()); const int x_size = static_cast(x.size()); RTC_DCHECK_EQ(0, h_size % 4); @@ -172,14 +171,14 @@ void MatchedFilterCoreWithAccumulatedError_NEON( void MatchedFilterCore_NEON(size_t x_start_index, float x2_sum_threshold, float smoothing, - webrtc::ArrayView x, - webrtc::ArrayView y, - webrtc::ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulated_error, - webrtc::ArrayView accumulated_error, - webrtc::ArrayView scratch_memory) { + std::span accumulated_error, + std::span scratch_memory) { const int h_size = static_cast(h.size()); const int x_size = static_cast(x.size()); RTC_DCHECK_EQ(0, h_size % 4); @@ -289,13 +288,13 @@ void MatchedFilterCore_NEON(size_t x_start_index, void MatchedFilterCore_AccumulatedError_SSE2(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, - ArrayView accumulated_error, - ArrayView scratch_memory) { + std::span accumulated_error, + std::span scratch_memory) { const int h_size = static_cast(h.size()); const int x_size = static_cast(x.size()); RTC_DCHECK_EQ(0, h_size % 8); @@ -385,14 +384,14 @@ void MatchedFilterCore_AccumulatedError_SSE2(size_t x_start_index, void MatchedFilterCore_SSE2(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulated_error, - ArrayView accumulated_error, - ArrayView scratch_memory) { + std::span accumulated_error, + std::span scratch_memory) { if (compute_accumulated_error) { return MatchedFilterCore_AccumulatedError_SSE2( x_start_index, x2_sum_threshold, smoothing, x, y, h, filters_updated, @@ -497,13 +496,13 @@ void MatchedFilterCore_SSE2(size_t x_start_index, void MatchedFilterCore(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulated_error, - ArrayView accumulated_error) { + std::span accumulated_error) { if (compute_accumulated_error) { std::fill(accumulated_error.begin(), accumulated_error.end(), 0.0f); } @@ -555,7 +554,7 @@ void MatchedFilterCore(size_t x_start_index, } } -size_t MaxSquarePeakIndex(ArrayView h) { +size_t MaxSquarePeakIndex(std::span h) { if (h.size() < 2) { return 0; } @@ -590,8 +589,6 @@ size_t MaxSquarePeakIndex(ArrayView h) { return lag_estimate1; } -} // namespace aec3 - MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper, Aec3Optimization optimization, size_t sub_block_size, @@ -655,7 +652,7 @@ void MatchedFilter::Reset(bool full_reset) { } void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, - ArrayView capture, + std::span capture, bool use_slow_smoothing) { RTC_DCHECK_EQ(sub_block_size_, capture.size()); auto& y = capture; @@ -693,13 +690,13 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, switch (optimization_) { #if defined(WEBRTC_ARCH_X86_FAMILY) case Aec3Optimization::kSse2: - aec3::MatchedFilterCore_SSE2( + MatchedFilterCore_SSE2( x_start_index, x2_sum_threshold, smoothing, render_buffer.buffer, y, filters_[n], &filters_updated, &error_sum, compute_pre_echo, instantaneous_accumulated_error_, scratch_memory_); break; case Aec3Optimization::kAvx2: - aec3::MatchedFilterCore_AVX2( + MatchedFilterCore_AVX2( x_start_index, x2_sum_threshold, smoothing, render_buffer.buffer, y, filters_[n], &filters_updated, &error_sum, compute_pre_echo, instantaneous_accumulated_error_, scratch_memory_); @@ -707,23 +704,23 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, #endif #if defined(WEBRTC_HAS_NEON) case Aec3Optimization::kNeon: - aec3::MatchedFilterCore_NEON( + MatchedFilterCore_NEON( x_start_index, x2_sum_threshold, smoothing, render_buffer.buffer, y, filters_[n], &filters_updated, &error_sum, compute_pre_echo, instantaneous_accumulated_error_, scratch_memory_); break; #endif default: - aec3::MatchedFilterCore(x_start_index, x2_sum_threshold, smoothing, - render_buffer.buffer, y, filters_[n], - &filters_updated, &error_sum, compute_pre_echo, - instantaneous_accumulated_error_); + MatchedFilterCore(x_start_index, x2_sum_threshold, smoothing, + render_buffer.buffer, y, filters_[n], + &filters_updated, &error_sum, compute_pre_echo, + instantaneous_accumulated_error_); } // Estimate the lag in the matched filter as the distance to the portion in // the filter that contributes the most to the matched filter output. This // is detected as the peak of the matched filter. - const size_t lag_estimate = aec3::MaxSquarePeakIndex(filters_[n]); + const size_t lag_estimate = MaxSquarePeakIndex(filters_[n]); const bool reliable = lag_estimate > 2 && lag_estimate < (filters_[n].size() - 10) && error_sum < matching_filter_threshold_ * error_sum_anchor; @@ -797,7 +794,7 @@ void MatchedFilter::LogFilterProperties(int /* sample_rate_hz */, void MatchedFilter::Dump() { for (size_t n = 0; n < filters_.size(); ++n) { - const size_t lag_estimate = aec3::MaxSquarePeakIndex(filters_[n]); + const size_t lag_estimate = MaxSquarePeakIndex(filters_[n]); std::string dumper_filter = "aec3_correlator_" + std::to_string(n) + "_h"; data_dumper_->DumpRaw(dumper_filter.c_str(), filters_[n]); std::string dumper_lag = "aec3_correlator_lag_" + std::to_string(n); diff --git a/modules/audio_processing/aec3/matched_filter.h b/modules/audio_processing/aec3/matched_filter.h index 2a910b7eb28..b0b7a0dff11 100644 --- a/modules/audio_processing/aec3/matched_filter.h +++ b/modules/audio_processing/aec3/matched_filter.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/system/arch.h" @@ -25,22 +25,20 @@ namespace webrtc { class ApmDataDumper; struct DownsampledRenderBuffer; -namespace aec3 { - #if defined(WEBRTC_HAS_NEON) // Filter core for the matched filter that is optimized for NEON. void MatchedFilterCore_NEON(size_t x_start_index, float x2_sum_threshold, float smoothing, - webrtc::ArrayView x, - webrtc::ArrayView y, - webrtc::ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulation_error, - webrtc::ArrayView accumulated_error, - webrtc::ArrayView scratch_memory); + std::span accumulated_error, + std::span scratch_memory); #endif @@ -50,27 +48,27 @@ void MatchedFilterCore_NEON(size_t x_start_index, void MatchedFilterCore_SSE2(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulated_error, - ArrayView accumulated_error, - ArrayView scratch_memory); + std::span accumulated_error, + std::span scratch_memory); // Filter core for the matched filter that is optimized for AVX2. void MatchedFilterCore_AVX2(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulated_error, - ArrayView accumulated_error, - ArrayView scratch_memory); + std::span accumulated_error, + std::span scratch_memory); #endif @@ -78,18 +76,16 @@ void MatchedFilterCore_AVX2(size_t x_start_index, void MatchedFilterCore(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulation_error, - ArrayView accumulated_error); + std::span accumulated_error); // Find largest peak of squared values in array. -size_t MaxSquarePeakIndex(ArrayView h); - -} // namespace aec3 +size_t MaxSquarePeakIndex(std::span h); // Produces recursively updated cross-correlation estimates for several signal // shifts where the intra-shift spacing is uniform. @@ -125,7 +121,7 @@ class MatchedFilter { // Updates the correlation with the values in the capture buffer. void Update(const DownsampledRenderBuffer& render_buffer, - ArrayView capture, + std::span capture, bool use_slow_smoothing); // Resets the matched filter. diff --git a/modules/audio_processing/aec3/matched_filter_avx2.cc b/modules/audio_processing/aec3/matched_filter_avx2.cc index 1312c83db64..eacd6486b10 100644 --- a/modules/audio_processing/aec3/matched_filter_avx2.cc +++ b/modules/audio_processing/aec3/matched_filter_avx2.cc @@ -12,13 +12,12 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/matched_filter.h" #include "rtc_base/checks.h" namespace webrtc { -namespace aec3 { // Let ha denote the horizontal of a, and hb the horizontal sum of b // returns [ha, hb, ha, hb] @@ -35,13 +34,13 @@ inline __m128 hsum_ab(__m256 a, __m256 b) { void MatchedFilterCore_AccumulatedError_AVX2(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, - ArrayView accumulated_error, - ArrayView scratch_memory) { + std::span accumulated_error, + std::span scratch_memory) { const int h_size = static_cast(h.size()); const int x_size = static_cast(x.size()); RTC_DCHECK_EQ(0, h_size % 16); @@ -141,14 +140,14 @@ void MatchedFilterCore_AccumulatedError_AVX2(size_t x_start_index, void MatchedFilterCore_AVX2(size_t x_start_index, float x2_sum_threshold, float smoothing, - ArrayView x, - ArrayView y, - ArrayView h, + std::span x, + std::span y, + std::span h, bool* filters_updated, float* error_sum, bool compute_accumulated_error, - ArrayView accumulated_error, - ArrayView scratch_memory) { + std::span accumulated_error, + std::span scratch_memory) { if (compute_accumulated_error) { return MatchedFilterCore_AccumulatedError_AVX2( x_start_index, x2_sum_threshold, smoothing, x, y, h, filters_updated, @@ -259,5 +258,4 @@ void MatchedFilterCore_AVX2(size_t x_start_index, } } -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc b/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc index 49091821575..51e5db84b56 100644 --- a/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc +++ b/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/delay_estimate.h" @@ -81,7 +81,7 @@ std::optional MatchedFilterLagAggregator::Aggregate( if (lag_estimate) { highest_peak_aggregator_.Aggregate( std::max(0, static_cast(lag_estimate->lag) - headroom_)); - ArrayView histogram = highest_peak_aggregator_.histogram(); + std::span histogram = highest_peak_aggregator_.histogram(); int candidate = highest_peak_aggregator_.candidate(); significant_candidate_found_ = significant_candidate_found_ || histogram[candidate] > thresholds_.converged; diff --git a/modules/audio_processing/aec3/matched_filter_lag_aggregator.h b/modules/audio_processing/aec3/matched_filter_lag_aggregator.h index 26cafbca153..d53464fb425 100644 --- a/modules/audio_processing/aec3/matched_filter_lag_aggregator.h +++ b/modules/audio_processing/aec3/matched_filter_lag_aggregator.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/delay_estimate.h" #include "modules/audio_processing/aec3/matched_filter.h" @@ -81,7 +81,7 @@ class MatchedFilterLagAggregator { void Reset(); void Aggregate(int lag); int candidate() const { return candidate_; } - ArrayView histogram() const { return histogram_; } + std::span histogram() const { return histogram_; } private: std::vector histogram_; diff --git a/modules/audio_processing/aec3/matched_filter_unittest.cc b/modules/audio_processing/aec3/matched_filter_unittest.cc index dc75bcccce9..d7f6f5e0bdc 100644 --- a/modules/audio_processing/aec3/matched_filter_unittest.cc +++ b/modules/audio_processing/aec3/matched_filter_unittest.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" @@ -41,7 +41,6 @@ #endif namespace webrtc { -namespace aec3 { namespace { std::string ProduceDebugText(size_t delay, size_t down_sampling_factor) { @@ -305,7 +304,7 @@ TEST_P(MatchedFilterTest, LagEstimation) { render_delay_buffer->PrepareCaptureProcessing(); std::array downsampled_capture_data; - ArrayView downsampled_capture(downsampled_capture_data.data(), + std::span downsampled_capture(downsampled_capture_data.data(), sub_block_size); capture_decimator.Decimate(capture[0], downsampled_capture); filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(), @@ -380,7 +379,7 @@ TEST_P(MatchedFilterTest, PreEchoEstimation) { } render_delay_buffer->PrepareCaptureProcessing(); std::array downsampled_capture_data; - ArrayView downsampled_capture(downsampled_capture_data.data(), + std::span downsampled_capture(downsampled_capture_data.data(), sub_block_size); capture_decimator.Decimate(capture[0], downsampled_capture); filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(), @@ -421,7 +420,7 @@ TEST_P(MatchedFilterTest, LagNotReliableForUncorrelatedRenderAndCapture) { Block render(kNumBands, kNumChannels); std::array capture_data; - ArrayView capture(capture_data.data(), sub_block_size); + std::span capture(capture_data.data(), sub_block_size); std::fill(capture.begin(), capture.end(), 0.f); ApmDataDumper data_dumper(0); std::unique_ptr render_delay_buffer( @@ -486,7 +485,7 @@ TEST_P(MatchedFilterTest, LagNotUpdatedForLowLevelRender) { } std::copy(render.begin(0, 0), render.end(0, 0), capture[0].begin()); std::array downsampled_capture_data; - ArrayView downsampled_capture(downsampled_capture_data.data(), + std::span downsampled_capture(downsampled_capture_data.data(), sub_block_size); capture_decimator.Decimate(capture[0], downsampled_capture); filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(), @@ -565,5 +564,4 @@ INSTANTIATE_TEST_SUITE_P(_, #endif -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/moving_average.h b/modules/audio_processing/aec3/moving_average.h deleted file mode 100644 index 68f2fd0fccf..00000000000 --- a/modules/audio_processing/aec3/moving_average.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ -#define MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ - -#include - -#include - -#include "api/array_view.h" - -namespace webrtc { -namespace aec3 { - -class MovingAverage { - public: - // Creates an instance of MovingAverage that accepts inputs of length num_elem - // and averages over mem_len inputs. - MovingAverage(size_t num_elem, size_t mem_len); - ~MovingAverage(); - - // Computes the average of input and mem_len-1 previous inputs and stores the - // result in output. - void Average(ArrayView input, ArrayView output); - - private: - const size_t num_elem_; - const size_t mem_len_; - const float scaling_; - std::vector memory_; - size_t mem_index_; -}; - -} // namespace aec3 -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_ diff --git a/modules/audio_processing/aec3/moving_average.cc b/modules/audio_processing/aec3/moving_average_spectrum.cc similarity index 58% rename from modules/audio_processing/aec3/moving_average.cc rename to modules/audio_processing/aec3/moving_average_spectrum.cc index e1e5589ff38..fb469182e1b 100644 --- a/modules/audio_processing/aec3/moving_average.cc +++ b/modules/audio_processing/aec3/moving_average_spectrum.cc @@ -9,32 +9,31 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/moving_average_spectrum.h" #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { -namespace aec3 { -MovingAverage::MovingAverage(size_t num_elem, size_t mem_len) +MovingAverageSpectrum::MovingAverageSpectrum(size_t num_elem, size_t mem_len) : num_elem_(num_elem), mem_len_(mem_len - 1), - scaling_(1.0f / static_cast(mem_len)), memory_(num_elem * mem_len_, 0.f), - mem_index_(0) { + mem_index_(0), + number_updates_(0) { RTC_DCHECK(num_elem_ > 0); RTC_DCHECK(mem_len > 0); } -MovingAverage::~MovingAverage() = default; +MovingAverageSpectrum::~MovingAverageSpectrum() = default; -void MovingAverage::Average(ArrayView input, - ArrayView output) { +void MovingAverageSpectrum::Average(std::span input, + std::span output) { RTC_DCHECK(input.size() == num_elem_); RTC_DCHECK(output.size() == num_elem_); @@ -45,9 +44,10 @@ void MovingAverage::Average(ArrayView input, std::plus()); } - // Divide by mem_len_. + // Divide by the number of points used to compute the average. + const float scaling = 1.0f / static_cast(number_updates_ + 1); for (float& o : output) { - o *= scaling_; + o *= scaling; } // Update memory. @@ -56,7 +56,19 @@ void MovingAverage::Average(ArrayView input, memory_.begin() + mem_index_ * num_elem_); mem_index_ = (mem_index_ + 1) % mem_len_; } + number_updates_ = std::min(static_cast(mem_len_), number_updates_ + 1); +} + +void MovingAverageSpectrum::UpdateMemoryLength(size_t mem_len) { + if (mem_len_ + 1 == mem_len) { + return; + } + RTC_DCHECK(mem_len > 0); + mem_len_ = mem_len - 1; + memory_.resize(num_elem_ * mem_len_); + std::fill(memory_.begin(), memory_.end(), 0.0f); + mem_index_ = 0; + number_updates_ = 0; } -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/moving_average_spectrum.h b/modules/audio_processing/aec3/moving_average_spectrum.h new file mode 100644 index 00000000000..e8a2909ee30 --- /dev/null +++ b/modules/audio_processing/aec3/moving_average_spectrum.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_SPECTRUM_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_SPECTRUM_H_ + +#include + +#include +#include + +namespace webrtc { + +class MovingAverageSpectrum { + public: + // Creates an instance of MovingAverageSpectrum that accepts inputs of length + // num_elem and averages over mem_len inputs. + MovingAverageSpectrum(size_t num_elem, size_t mem_len); + ~MovingAverageSpectrum(); + + // Computes the average of the current input and up to mem_len-1 previous + // inputs and stores the result in output. + void Average(std::span input, std::span output); + + // If a new memory length is provided, resets the state and clears the + // average memory to use the new window size. + void UpdateMemoryLength(size_t mem_len); + + private: + const size_t num_elem_; + size_t mem_len_; + std::vector memory_; + size_t mem_index_; + int number_updates_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_SPECTRUM_H_ diff --git a/modules/audio_processing/aec3/moving_average_unittest.cc b/modules/audio_processing/aec3/moving_average_spectrum_unittest.cc similarity index 66% rename from modules/audio_processing/aec3/moving_average_unittest.cc rename to modules/audio_processing/aec3/moving_average_spectrum_unittest.cc index ca985176cd3..5dbfca2d409 100644 --- a/modules/audio_processing/aec3/moving_average_unittest.cc +++ b/modules/audio_processing/aec3/moving_average_spectrum_unittest.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/moving_average_spectrum.h" #include #include @@ -17,11 +17,11 @@ namespace webrtc { -TEST(MovingAverage, Average) { +TEST(MovingAverageSpectrum, Average) { constexpr size_t num_elem = 4; constexpr size_t mem_len = 3; constexpr float e = 1e-6f; - aec3::MovingAverage ma(num_elem, mem_len); + MovingAverageSpectrum ma(num_elem, mem_len); std::array data1 = {1, 2, 3, 4}; std::array data2 = {5, 1, 9, 7}; std::array data3 = {3, 3, 5, 6}; @@ -29,16 +29,16 @@ TEST(MovingAverage, Average) { std::array output; ma.Average(data1, output); - EXPECT_NEAR(output[0], data1[0] / 3.0f, e); - EXPECT_NEAR(output[1], data1[1] / 3.0f, e); - EXPECT_NEAR(output[2], data1[2] / 3.0f, e); - EXPECT_NEAR(output[3], data1[3] / 3.0f, e); + EXPECT_NEAR(output[0], data1[0] / 1.0f, e); + EXPECT_NEAR(output[1], data1[1] / 1.0f, e); + EXPECT_NEAR(output[2], data1[2] / 1.0f, e); + EXPECT_NEAR(output[3], data1[3] / 1.0f, e); ma.Average(data2, output); - EXPECT_NEAR(output[0], (data1[0] + data2[0]) / 3.0f, e); - EXPECT_NEAR(output[1], (data1[1] + data2[1]) / 3.0f, e); - EXPECT_NEAR(output[2], (data1[2] + data2[2]) / 3.0f, e); - EXPECT_NEAR(output[3], (data1[3] + data2[3]) / 3.0f, e); + EXPECT_NEAR(output[0], (data1[0] + data2[0]) / 2.0f, e); + EXPECT_NEAR(output[1], (data1[1] + data2[1]) / 2.0f, e); + EXPECT_NEAR(output[2], (data1[2] + data2[2]) / 2.0f, e); + EXPECT_NEAR(output[3], (data1[3] + data2[3]) / 2.0f, e); ma.Average(data3, output); EXPECT_NEAR(output[0], (data1[0] + data2[0] + data3[0]) / 3.0f, e); @@ -53,11 +53,31 @@ TEST(MovingAverage, Average) { EXPECT_NEAR(output[3], (data2[3] + data3[3] + data4[3]) / 3.0f, e); } -TEST(MovingAverage, PassThrough) { +TEST(MovingAverageSpectrum, UpdateMemoryLength) { + constexpr size_t num_elem = 4; + constexpr float e = 1e-6f; + MovingAverageSpectrum ma(num_elem, 3); + std::array data1 = {1, 2, 3, 4}; + std::array data2 = {5, 1, 9, 7}; + std::array output; + + ma.Average(data1, output); + EXPECT_NEAR(output[0], data1[0], e); + + ma.UpdateMemoryLength(1); + ma.Average(data2, output); + // After update, it should behave as if it was just created with mem_len = 1. + EXPECT_NEAR(output[0], data2[0], e); + EXPECT_NEAR(output[1], data2[1], e); + EXPECT_NEAR(output[2], data2[2], e); + EXPECT_NEAR(output[3], data2[3], e); +} + +TEST(MovingAverageSpectrum, PassThrough) { constexpr size_t num_elem = 4; constexpr size_t mem_len = 1; constexpr float e = 1e-6f; - aec3::MovingAverage ma(num_elem, mem_len); + MovingAverageSpectrum ma(num_elem, mem_len); std::array data1 = {1, 2, 3, 4}; std::array data2 = {5, 1, 9, 7}; std::array data3 = {3, 3, 5, 6}; diff --git a/modules/audio_processing/aec3/nearend_detector.h b/modules/audio_processing/aec3/nearend_detector.h index a1f30a977c2..ff30f423f95 100644 --- a/modules/audio_processing/aec3/nearend_detector.h +++ b/modules/audio_processing/aec3/nearend_detector.h @@ -12,8 +12,9 @@ #define MODULES_AUDIO_PROCESSING_AEC3_NEAREND_DETECTOR_H_ #include +#include -#include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" namespace webrtc { @@ -27,12 +28,15 @@ class NearendDetector { // Updates the state selection based on latest spectral estimates. virtual void Update( - ArrayView> nearend_spectrum, - ArrayView> + std::span> nearend_spectrum, + std::span> residual_echo_spectrum, - ArrayView> + std::span> comfort_noise_spectrum, bool initial_state) = 0; + + // Sets the configuration. + virtual void SetConfig(const EchoCanceller3Config::Suppressor& config) = 0; }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/neural_residual_echo_estimator/BUILD.gn b/modules/audio_processing/aec3/neural_residual_echo_estimator/BUILD.gn index 120d46a4f6c..029a0114f6d 100644 --- a/modules/audio_processing/aec3/neural_residual_echo_estimator/BUILD.gn +++ b/modules/audio_processing/aec3/neural_residual_echo_estimator/BUILD.gn @@ -34,7 +34,6 @@ if (rtc_enable_protobuf) { "..:aec3_block", "..:aec3_common", "../..:apm_logging", - "../../../../api:array_view", "../../../../api/audio:aec3_config", "../../../../api/audio:neural_residual_echo_estimator_api", "../../../../common_audio", @@ -70,7 +69,6 @@ if (rtc_include_tests) { ":neural_residual_echo_estimator_proto", "..:aec3_block", "..:aec3_common", - "../../../../api:array_view", "../../../../rtc_base:checks", "../../../../rtc_base:random", "../../../../test:fileutils", diff --git a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.cc b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.cc index e674d0bf3cd..63615c9eb19 100644 --- a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.cc +++ b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_audio/window_generator.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" @@ -46,7 +46,7 @@ std::vector GetSqrtHanningWindow(int frame_size, float scale) { } std::array AverageAllChannels( - ArrayView> all_channels) { + std::span> all_channels) { std::array summed_block; summed_block.fill(0.0f); const float scale = kScale * 1.0f / all_channels.size(); @@ -75,11 +75,17 @@ TimeDomainFeatureExtractor::TimeDomainFeatureExtractor(int step_size) TimeDomainFeatureExtractor::~TimeDomainFeatureExtractor() = default; +void TimeDomainFeatureExtractor::Reset() { + for (auto& buffer : input_buffer_) { + buffer.clear(); + } +} + bool TimeDomainFeatureExtractor::ReadyForInference() const { for (const auto model_input_enum : kRequiredModelInputs) { const std::vector& input_buffer = input_buffer_[static_cast(model_input_enum)]; - if (input_buffer.size() != step_size_) { + if (input_buffer.size() < step_size_) { return false; } } @@ -87,7 +93,7 @@ bool TimeDomainFeatureExtractor::ReadyForInference() const { } void TimeDomainFeatureExtractor::UpdateBuffers( - ArrayView> all_channels, + std::span> all_channels, ModelInputEnum input_type) { if (!RequiredInput(input_type)) { return; @@ -95,11 +101,11 @@ void TimeDomainFeatureExtractor::UpdateBuffers( std::vector& input_buffer = input_buffer_[static_cast(input_type)]; std::array summed_block = AverageAllChannels(all_channels); - input_buffer.insert(input_buffer.end(), summed_block.cbegin(), - summed_block.cend()); + input_buffer.insert(input_buffer.end(), summed_block.begin(), + summed_block.end()); } -void TimeDomainFeatureExtractor::PrepareModelInput(ArrayView model_input, +void TimeDomainFeatureExtractor::PrepareModelInput(std::span model_input, ModelInputEnum input_type) { if (!RequiredInput(input_type)) { return; @@ -107,9 +113,9 @@ void TimeDomainFeatureExtractor::PrepareModelInput(ArrayView model_input, std::vector& input_buffer = input_buffer_[static_cast(input_type)]; RTC_CHECK_EQ(input_buffer.size(), step_size_); - std::copy(model_input.cbegin() + step_size_, model_input.cend(), + std::copy(model_input.begin() + step_size_, model_input.end(), model_input.begin()); - std::copy(input_buffer.cbegin(), input_buffer.cend(), + std::copy(input_buffer.begin(), input_buffer.end(), model_input.end() - step_size_); input_buffer.clear(); } @@ -134,6 +140,22 @@ FrequencyDomainFeatureExtractor::FrequencyDomainFeatureExtractor(int step_size) } } +void FrequencyDomainFeatureExtractor::Reset() { + std::memset(spectrum_, 0, sizeof(float) * frame_size_); + for (auto& buffers : input_buffer_) { + for (auto& buffer : buffers) { + buffer.clear(); + } + } + for (auto& states : pffft_states_) { + for (auto& state : states) { + if (state) { + std::memset(state->data(), 0, sizeof(float) * frame_size_); + } + } + } +} + FrequencyDomainFeatureExtractor::~FrequencyDomainFeatureExtractor() { pffft_destroy_setup(pffft_setup_); pffft_aligned_free(work_); @@ -144,7 +166,7 @@ bool FrequencyDomainFeatureExtractor::ReadyForInference() const { for (const auto model_input_enum : kRequiredModelInputs) { const std::vector>& input_buffer = input_buffer_[static_cast(model_input_enum)]; - if (input_buffer.empty() || input_buffer[0].size() != step_size_) { + if (input_buffer.empty() || input_buffer[0].size() < step_size_) { return false; } } @@ -152,10 +174,10 @@ bool FrequencyDomainFeatureExtractor::ReadyForInference() const { } void FrequencyDomainFeatureExtractor::ComputeAndAddPowerSpectra( - ArrayView frame, + std::span frame, std::unique_ptr& pffft_state, int number_channels, - ArrayView power_spectra) { + std::span power_spectra) { const float kAverageScale = 1.0f / number_channels; if (pffft_state == nullptr) { pffft_state = std::make_unique(frame_size_); @@ -180,7 +202,7 @@ void FrequencyDomainFeatureExtractor::ComputeAndAddPowerSpectra( } void FrequencyDomainFeatureExtractor::UpdateBuffers( - ArrayView> all_channels, + std::span> all_channels, ModelInputEnum input_type) { if (!RequiredInput(input_type)) { return; @@ -189,14 +211,14 @@ void FrequencyDomainFeatureExtractor::UpdateBuffers( input_buffer_[static_cast(input_type)]; input_buffer.resize(all_channels.size()); for (size_t ch = 0; ch < all_channels.size(); ++ch) { - const ArrayView& frame_in = all_channels[ch]; + const std::span& frame_in = all_channels[ch]; std::vector& input_buffer_ch = input_buffer[ch]; - input_buffer_ch.insert(input_buffer_ch.end(), frame_in.cbegin(), - frame_in.cend()); + input_buffer_ch.insert(input_buffer_ch.end(), frame_in.begin(), + frame_in.end()); } } void FrequencyDomainFeatureExtractor::PrepareModelInput( - ArrayView model_input, + std::span model_input, ModelInputEnum input_type) { if (!RequiredInput(input_type)) { return; diff --git a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.h b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.h index ebaed9a0ce8..2071fb944a8 100644 --- a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.h +++ b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "third_party/pffft/src/pffft.h" @@ -45,12 +45,15 @@ class FeatureExtractor { // Buffers the frames for matching the expecting inference step size. virtual void UpdateBuffers( - ArrayView> all_channels, + std::span> all_channels, ModelInputEnum input_type) = 0; // Uses the internal buffer data for producing the model input tensors. - virtual void PrepareModelInput(ArrayView model_input, + virtual void PrepareModelInput(std::span model_input, ModelInputEnum input_type) = 0; + + // Resets the internal state of the feature extractor. + virtual void Reset() = 0; }; class TimeDomainFeatureExtractor : public FeatureExtractor { @@ -58,13 +61,15 @@ class TimeDomainFeatureExtractor : public FeatureExtractor { explicit TimeDomainFeatureExtractor(int step_size); ~TimeDomainFeatureExtractor() override; + void Reset() override; + bool ReadyForInference() const override; void UpdateBuffers( - ArrayView> all_channels, + std::span> all_channels, ModelInputEnum input_type) override; - void PrepareModelInput(ArrayView model_input, + void PrepareModelInput(std::span model_input, ModelInputEnum input_type) override; private: @@ -77,13 +82,15 @@ class FrequencyDomainFeatureExtractor : public FeatureExtractor { explicit FrequencyDomainFeatureExtractor(int step_size); ~FrequencyDomainFeatureExtractor() override; + void Reset() override; + bool ReadyForInference() const override; void UpdateBuffers( - ArrayView> all_channels, + std::span> all_channels, ModelInputEnum input_type) override; - void PrepareModelInput(ArrayView model_input, + void PrepareModelInput(std::span model_input, ModelInputEnum input_type) override; private: @@ -101,10 +108,10 @@ class FrequencyDomainFeatureExtractor : public FeatureExtractor { float* const data_; }; - void ComputeAndAddPowerSpectra(ArrayView frame, + void ComputeAndAddPowerSpectra(std::span frame, std::unique_ptr& pffft_state, int number_channels, - ArrayView power_spectra); + std::span power_spectra); const size_t step_size_; const int frame_size_; diff --git a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor_unittest.cc b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor_unittest.cc index 090c7cf071c..757af1bc7a2 100644 --- a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor_unittest.cc +++ b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor_unittest.cc @@ -53,9 +53,9 @@ print(format_as_cpp_array(expected_output2, "expected_output2")) #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "test/gmock.h" #include "test/gtest.h" @@ -229,9 +229,9 @@ class FrequencyDomainFeatureExtractorTest : public ::testing::TestWithParam { protected: void UpdateBlock(FrequencyDomainFeatureExtractor& extractor, - ArrayView block, + std::span block, int num_channels) { - std::vector> all_blocks; + std::vector> all_blocks; all_blocks.reserve(num_channels); for (int i = 0; i < num_channels; ++i) { all_blocks.push_back(block); @@ -248,7 +248,7 @@ TEST_P(FrequencyDomainFeatureExtractorTest, BasicTest) { std::vector output(kStepSize + 1); // First frame. for (int i = 0; i < kStepSize; i += kBlockSize) { - ArrayView block(&noise1_scaled[i], kBlockSize); + std::span block(&noise1_scaled[i], kBlockSize); this->UpdateBlock(extractor, block, num_channels); } EXPECT_TRUE(extractor.ReadyForInference()); @@ -258,7 +258,7 @@ TEST_P(FrequencyDomainFeatureExtractorTest, BasicTest) { EXPECT_FALSE(extractor.ReadyForInference()); for (int i = kStepSize; i < 2 * kStepSize; i += kBlockSize) { - ArrayView block(&noise1_scaled[i], kBlockSize); + std::span block(&noise1_scaled[i], kBlockSize); this->UpdateBlock(extractor, block, num_channels); } EXPECT_TRUE(extractor.ReadyForInference()); @@ -270,7 +270,7 @@ TEST_P(FrequencyDomainFeatureExtractorTest, BasicTest) { EXPECT_FALSE(extractor.ReadyForInference()); // Second frame. for (int i = 0; i < kStepSize; i += kBlockSize) { - ArrayView block(&noise2_scaled[i], kBlockSize); + std::span block(&noise2_scaled[i], kBlockSize); this->UpdateBlock(extractor, block, num_channels); } EXPECT_TRUE(extractor.ReadyForInference()); @@ -279,7 +279,7 @@ TEST_P(FrequencyDomainFeatureExtractorTest, BasicTest) { } for (int i = kStepSize; i < 2 * kStepSize; i += kBlockSize) { - ArrayView block(&noise2_scaled[i], kBlockSize); + std::span block(&noise2_scaled[i], kBlockSize); this->UpdateBlock(extractor, block, num_channels); } EXPECT_TRUE(extractor.ReadyForInference()); @@ -290,6 +290,19 @@ TEST_P(FrequencyDomainFeatureExtractorTest, BasicTest) { } } +TEST_P(FrequencyDomainFeatureExtractorTest, ResetsState) { + const int num_channels = this->GetParam(); + std::array block{}; + FrequencyDomainFeatureExtractor extractor(kStepSize); + EXPECT_FALSE(extractor.ReadyForInference()); + for (int i = 0; i < kStepSize; i += kBlockSize) { + this->UpdateBlock(extractor, block, num_channels); + } + EXPECT_TRUE(extractor.ReadyForInference()); + extractor.Reset(); + EXPECT_FALSE(extractor.ReadyForInference()); +} + INSTANTIATE_TEST_SUITE_P(NumChannels, FrequencyDomainFeatureExtractorTest, ::testing::Values(1, 2)); @@ -305,11 +318,11 @@ TEST(TimeDomainFeatureExtractorTest, BasicTest) { block1[i] = i; block2[i] = i + kBlockSize; } - ArrayView block1_view(block1); - ArrayView> all_blocks1(&block1_view, + std::span block1_view(block1); + std::span> all_blocks1(&block1_view, 1); - ArrayView block2_view(block2); - ArrayView> all_blocks2(&block2_view, + std::span block2_view(block2); + std::span> all_blocks2(&block2_view, 1); for (auto input_type : kExpectedInputs) { @@ -330,5 +343,21 @@ TEST(TimeDomainFeatureExtractorTest, BasicTest) { EXPECT_FALSE(extractor.ReadyForInference()); } +TEST(TimeDomainFeatureExtractorTest, ResetsState) { + TimeDomainFeatureExtractor extractor(kStepSize); + const std::array block{}; + const std::array, 1> all_blocks = { + block}; + EXPECT_FALSE(extractor.ReadyForInference()); + for (size_t i = 0; i < kStepSize / kBlockSize; ++i) { + for (auto input_type : kExpectedInputs) { + extractor.UpdateBuffers(all_blocks, input_type); + } + } + EXPECT_TRUE(extractor.ReadyForInference()); + extractor.Reset(); + EXPECT_FALSE(extractor.ReadyForInference()); +} + } // namespace } // namespace webrtc diff --git a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.cc b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.cc index 3c8121c334d..a18f1e511f8 100644 --- a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.cc +++ b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.cc @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include #include "absl/base/nullability.h" -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/neural_residual_echo_estimator.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -82,8 +82,8 @@ std::optional ReadModelMetadata( // Downsamples the model output mask to the AEC3 frequency resolution and // transforms it from a nearend prediction to an echo power mask. -void DownsampleAndTransformMask(ArrayView mask, - ArrayView downsampled_mask) { +void DownsampleAndTransformMask(std::span mask, + std::span downsampled_mask) { const int kDownsampleFactor = static_cast((mask.size() - 1) / kFftLengthBy2); downsampled_mask[0] = mask[0]; @@ -293,36 +293,46 @@ class TfLiteModelRunner : public NeuralResidualEchoEstimatorImpl::ModelRunner { for (const auto input_enum : {ModelInputEnum::kMic, ModelInputEnum::kLinearAecOutput, ModelInputEnum::kAecRef}) { - ArrayView input_tensor = GetInput(input_enum); + std::span input_tensor = GetInput(input_enum); std::fill(input_tensor.begin(), input_tensor.end(), 0.0f); } } ~TfLiteModelRunner() override {} + void Reset() override { + std::fill(model_state_.begin(), model_state_.end(), 0.0f); + for (const auto input_enum : + {ModelInputEnum::kMic, ModelInputEnum::kLinearAecOutput, + ModelInputEnum::kAecRef}) { + std::span input_tensor = GetInput(input_enum); + std::fill(input_tensor.begin(), input_tensor.end(), 0.0f); + } + } + int StepSize() const override { return step_size_; } - ArrayView GetInput( + std::span GetInput( FeatureExtractor::ModelInputEnum input_enum) override { size_t index = input_tensor_indexes_[static_cast(input_enum)]; TfLiteTensor* input_tensor = tflite_interpreter_->tensor(index); float* input_typed_tensor = reinterpret_cast(input_tensor->data.data); - return ArrayView(input_typed_tensor, + return std::span(input_typed_tensor, tflite::NumElements(input_tensor)); } - ArrayView GetOutput( + std::span GetOutput( FeatureExtractor::ModelOutputEnum output_enum) override { if (!use_unbounded_mask_ && output_enum == ModelOutputEnum::kUnboundedEchoMask) { - return ArrayView(); + return std::span(); } size_t index = output_tensor_indexes_[static_cast(output_enum)]; const TfLiteTensor* output_tensor = tflite_interpreter_->tensor(index); const float* output_typed_tensor = reinterpret_cast(output_tensor->data.data); - return ArrayView(output_typed_tensor, + return std::span(output_typed_tensor, tflite::NumElements(output_tensor)); } @@ -399,8 +409,12 @@ NeuralResidualEchoEstimatorImpl::LoadTfLiteModel( return nullptr; } std::unique_ptr interpreter; - if (tflite::InterpreterBuilder(*model, op_resolver)(&interpreter) != - kTfLiteOk) { + tflite::InterpreterBuilder interpreter_builder(*model, op_resolver); + if (interpreter_builder.SetNumThreads(1) != kTfLiteOk) { + RTC_LOG(LS_ERROR) << "Error setting interpreter num threads"; + return nullptr; + } + if (interpreter_builder(&interpreter) != kTfLiteOk) { RTC_LOG(LS_ERROR) << "Error creating interpreter"; return nullptr; } @@ -463,16 +477,25 @@ NeuralResidualEchoEstimatorImpl::NeuralResidualEchoEstimatorImpl( } } +void NeuralResidualEchoEstimatorImpl::Reset() { + model_runner_->Reset(); + if (feature_extractor_) { + feature_extractor_->Reset(); + } + output_mask_.fill(0.0f); + output_mask_unbounded_.fill(0.0f); +} + void NeuralResidualEchoEstimatorImpl::Estimate( const Block& render, - ArrayView> y, - ArrayView> e, - ArrayView> S2, - ArrayView> Y2, - ArrayView> E2, + std::span> y, + std::span> e, + std::span> S2, + std::span> Y2, + std::span> E2, bool dominant_nearend, - ArrayView> R2, - ArrayView> R2_unbounded) { + std::span> R2, + std::span> R2_unbounded) { DumpInputs(render, y, e); render_channels_.clear(); for (int i = 0; i < render.NumChannels(); ++i) { @@ -502,11 +525,11 @@ void NeuralResidualEchoEstimatorImpl::Estimate( ModelInputEnum::kAecRef); if (model_runner_->Invoke()) { // Downsample output mask to match the AEC3 frequency resolution. - ArrayView output_mask = + std::span output_mask = model_runner_->GetOutput(ModelOutputEnum::kEchoMask); DownsampleAndTransformMask(output_mask, output_mask_); if (use_unbounded_mask_) { - ArrayView output_mask_unbounded = + std::span output_mask_unbounded = model_runner_->GetOutput(ModelOutputEnum::kUnboundedEchoMask); DownsampleAndTransformMask(output_mask_unbounded, output_mask_unbounded_); @@ -546,6 +569,15 @@ void NeuralResidualEchoEstimatorImpl::Estimate( EchoCanceller3Config NeuralResidualEchoEstimatorImpl::GetConfiguration( bool multi_channel) const { EchoCanceller3Config config; + config.suppressor = AdjustConfig(config.suppressor); + config.filter.enable_coarse_filter_output_usage = false; + return config; +} + +EchoCanceller3Config::Suppressor NeuralResidualEchoEstimatorImpl::AdjustConfig( + const EchoCanceller3Config::Suppressor& suppressor_config) const { + EchoCanceller3Config::Suppressor adjusted_suppressor_config = + suppressor_config; EchoCanceller3Config::Suppressor::MaskingThresholds tuning_masking_thresholds( /*enr_transparent=*/0.0f, /*enr_suppress=*/1.0f, /*emr_transparent=*/0.3f); @@ -553,21 +585,21 @@ EchoCanceller3Config NeuralResidualEchoEstimatorImpl::GetConfiguration( /*mask_lf=*/tuning_masking_thresholds, /*mask_hf=*/tuning_masking_thresholds, /*max_inc_factor=*/100.0f, /*max_dec_factor_lf=*/0.0f); - config.filter.enable_coarse_filter_output_usage = false; - config.suppressor.nearend_average_blocks = 1; - config.suppressor.normal_tuning = tuning; - config.suppressor.nearend_tuning = tuning; - config.suppressor.dominant_nearend_detection.enr_threshold = 0.5f; - config.suppressor.dominant_nearend_detection.trigger_threshold = 2; - config.suppressor.high_frequency_suppression.limiting_gain_band = 24; - config.suppressor.high_frequency_suppression.bands_in_limiting_gain = 3; - return config; + adjusted_suppressor_config.nearend_average_blocks = 1; + adjusted_suppressor_config.normal_tuning = tuning; + adjusted_suppressor_config.nearend_tuning = tuning; + adjusted_suppressor_config.dominant_nearend_detection.enr_threshold = 0.5f; + adjusted_suppressor_config.dominant_nearend_detection.trigger_threshold = 2; + adjusted_suppressor_config.high_frequency_suppression.limiting_gain_band = 24; + adjusted_suppressor_config.high_frequency_suppression.bands_in_limiting_gain = + 3; + return adjusted_suppressor_config; } void NeuralResidualEchoEstimatorImpl::DumpInputs( const Block& render, - ArrayView> y, - ArrayView> e) { + std::span> y, + std::span> e) { data_dumper_->DumpWav("ml_ree_mic_input", y[0], 16000, 1); data_dumper_->DumpWav("ml_ree_linear_aec_output", e[0], 16000, 1); data_dumper_->DumpWav("ml_ree_aec_ref", render.View(0, 0), 16000, 1); diff --git a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.h b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.h index f00a458fee8..f92358eef5f 100644 --- a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.h +++ b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl.h @@ -13,10 +13,10 @@ #include #include +#include #include #include "absl/base/nullability.h" -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/neural_residual_echo_estimator.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -45,12 +45,13 @@ class NeuralResidualEchoEstimatorImpl : public NeuralResidualEchoEstimator { virtual ~ModelRunner() = default; virtual int StepSize() const = 0; - virtual ArrayView GetInput( + virtual std::span GetInput( FeatureExtractor::ModelInputEnum input_enum) = 0; - virtual ArrayView GetOutput( + virtual std::span GetOutput( FeatureExtractor::ModelOutputEnum output_enum) = 0; virtual const audioproc::ReeModelMetadata& GetMetadata() const = 0; virtual bool Invoke() = 0; + virtual void Reset() = 0; }; // Loads a model into a ModelRunner and creates a NeuralResidualEchoEstimator @@ -70,21 +71,25 @@ class NeuralResidualEchoEstimatorImpl : public NeuralResidualEchoEstimator { void Estimate( const Block& render, - ArrayView> y, - ArrayView> e, - ArrayView> S2, - ArrayView> Y2, - ArrayView> E2, + std::span> y, + std::span> e, + std::span> S2, + std::span> Y2, + std::span> E2, bool dominant_nearend, - ArrayView> R2, - ArrayView> R2_unbounded) override; + std::span> R2, + std::span> R2_unbounded) override; EchoCanceller3Config GetConfiguration(bool multi_channel) const override; + EchoCanceller3Config::Suppressor AdjustConfig( + const EchoCanceller3Config::Suppressor& config) const override; + + void Reset() override; private: void DumpInputs(const Block& render, - ArrayView> y, - ArrayView> e); + std::span> y, + std::span> e); // Encapsulates all ML model invocation work. const std::unique_ptr model_runner_; @@ -97,9 +102,9 @@ class NeuralResidualEchoEstimatorImpl : public NeuralResidualEchoEstimator { std::array output_mask_; std::array output_mask_unbounded_; - std::vector> render_channels_; - std::vector> y_channels_; - std::vector> e_channels_; + std::vector> render_channels_; + std::vector> y_channels_; + std::vector> e_channels_; static int instance_count_; // Pointer to a data dumper that is used for debugging purposes. diff --git a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl_unittest.cc b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl_unittest.cc index 0da4983405b..a8681fb1e23 100644 --- a/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl_unittest.cc +++ b/modules/audio_processing/aec3/neural_residual_echo_estimator/neural_residual_echo_estimator_impl_unittest.cc @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "modules/audio_processing/aec3/neural_residual_echo_estimator/neural_feature_extractor.h" @@ -78,36 +78,34 @@ class MockModelRunner : public NeuralResidualEchoEstimatorImpl::ModelRunner { int StepSize() const override { return constants_.step_size; } - ArrayView GetInput(ModelInputEnum input_enum) override { + std::span GetInput(ModelInputEnum input_enum) override { switch (input_enum) { case ModelInputEnum::kMic: - return webrtc::ArrayView(input_mic_.data(), - constants_.frame_size); + return std::span(input_mic_.data(), constants_.frame_size); case ModelInputEnum::kLinearAecOutput: - return webrtc::ArrayView(input_linear_aec_output_.data(), - constants_.frame_size); + return std::span(input_linear_aec_output_.data(), + constants_.frame_size); case ModelInputEnum::kAecRef: - return webrtc::ArrayView(input_aec_ref_.data(), - constants_.frame_size); + return std::span(input_aec_ref_.data(), constants_.frame_size); case ModelInputEnum::kModelState: case ModelInputEnum::kNumInputs: RTC_CHECK(false); - return webrtc::ArrayView(); + return std::span(); } } - ArrayView GetOutput(ModelOutputEnum output_enum) override { + std::span GetOutput(ModelOutputEnum output_enum) override { switch (output_enum) { case ModelOutputEnum::kEchoMask: case ModelOutputEnum::kUnboundedEchoMask: - return ArrayView(output_echo_mask_.data(), + return std::span(output_echo_mask_.data(), constants_.frame_size_by_2_plus_1); case ModelOutputEnum::kModelState: // Mock model state output if needed, for now return empty - return ArrayView(); + return std::span(); default: RTC_CHECK(false); - return ArrayView(); + return std::span(); } } @@ -117,6 +115,8 @@ class MockModelRunner : public NeuralResidualEchoEstimatorImpl::ModelRunner { MOCK_METHOD(bool, Invoke, (), (override)); + MOCK_METHOD(void, Reset, (), (override)); + const ModelConstants constants_; const audioproc::ReeModelMetadata metadata_; std::vector input_mic_; @@ -142,14 +142,14 @@ TEST_P(NeuralResidualEchoEstimatorImplTest, constexpr int kNumCaptureChannels = 1; std::array x; - std::vector> y{kNumCaptureChannels}; - std::vector> e{kNumCaptureChannels}; - std::vector> E2{kNumCaptureChannels}; - std::vector> S2{kNumCaptureChannels}; - std::vector> Y2{kNumCaptureChannels}; - std::vector> R2{kNumCaptureChannels}; - std::vector> R2_unbounded{ - kNumCaptureChannels}; + std::vector> y(kNumCaptureChannels); + std::vector> e(kNumCaptureChannels); + std::vector> E2(kNumCaptureChannels); + std::vector> S2(kNumCaptureChannels); + std::vector> Y2(kNumCaptureChannels); + std::vector> R2(kNumCaptureChannels); + std::vector> R2_unbounded( + kNumCaptureChannels); auto mock_model_runner = std::make_unique(model_constants); for (int i = 0; i < model_constants.frame_size; ++i) { @@ -218,14 +218,14 @@ TEST_P(NeuralResidualEchoEstimatorImplTest, OutputMaskIsApplied) { constexpr int kNumCaptureChannels = 1; std::array x; - std::vector> y{kNumCaptureChannels}; - std::vector> e{kNumCaptureChannels}; - std::vector> E2{kNumCaptureChannels}; + std::vector> y(kNumCaptureChannels); + std::vector> e(kNumCaptureChannels); + std::vector> E2(kNumCaptureChannels); std::vector> S2{kNumCaptureChannels}; std::vector> Y2{kNumCaptureChannels}; std::vector> R2{kNumCaptureChannels}; - std::vector> R2_unbounded{ - kNumCaptureChannels}; + std::vector> R2_unbounded( + kNumCaptureChannels); std::fill(x.begin(), x.end(), 10000); std::fill(y[0].begin(), y[0].end(), 10000); std::fill(e[0].begin(), e[0].end(), 10000); @@ -272,6 +272,48 @@ TEST_P(NeuralResidualEchoEstimatorImplTest, OutputMaskIsApplied) { } } +TEST_P(NeuralResidualEchoEstimatorImplTest, ResetsState) { + const ModelConstants model_constants = GetParam(); + if (model_constants.step_size == kBlockSize) { + // This reset test needs to have a step size larger than the block size. + return; + } + SCOPED_TRACE(testing::Message() + << "model_constants.frame_size=" << model_constants.frame_size); + + constexpr int kNumCaptureChannels = 1; + std::vector> y(kNumCaptureChannels); + std::vector> e(kNumCaptureChannels); + std::vector> E2(kNumCaptureChannels); + std::vector> S2(kNumCaptureChannels); + std::vector> Y2(kNumCaptureChannels); + std::vector> R2(kNumCaptureChannels); + std::vector> R2_unbounded( + kNumCaptureChannels); + + auto mock_model_runner = std::make_unique(model_constants); + auto* mock_model_runner_ptr = mock_model_runner.get(); + NeuralResidualEchoEstimatorImpl estimator(std::move(mock_model_runner)); + + EXPECT_CALL(*mock_model_runner_ptr, Invoke()).Times(1); + EXPECT_CALL(*mock_model_runner_ptr, Reset()).Times(1); + + const int num_blocks_to_process = model_constants.step_size / kBlockSize; + for (int frame = 0; frame < 2; ++frame) { + for (int block = 0; block < num_blocks_to_process; ++block) { + Block render_block(1, 1); + estimator.Estimate(render_block, y, e, S2, Y2, E2, + /*dominant_nearend=*/false, R2, R2_unbounded); + if (frame == 1 && block == 0) { + // Resetting after the first block clears the estimator internal + // buffers. This prevents the model from receiving a full input frame, + // so Invoke() will not be called again within this second frame. + estimator.Reset(); + } + } + } +} + TEST(NeuralResidualEchoEstimatorWithRealModelTest, RunEstimationWithRealTfLiteModel) { std::string model_path = test::ResourcePath( @@ -294,10 +336,10 @@ TEST(NeuralResidualEchoEstimatorWithRealModelTest, std::array x; std::vector> y{kNumCaptureChannels}; std::vector> e{kNumCaptureChannels}; - std::vector> E2{kNumCaptureChannels}; - std::vector> S2{kNumCaptureChannels}; - std::vector> Y2{kNumCaptureChannels}; - std::vector> R2{kNumCaptureChannels}; + std::vector> E2(kNumCaptureChannels); + std::vector> S2(kNumCaptureChannels); + std::vector> Y2(kNumCaptureChannels); + std::vector> R2(kNumCaptureChannels); std::vector> R2_unbounded{ kNumCaptureChannels}; Random random_generator(4635U); diff --git a/modules/audio_processing/aec3/refined_filter_update_gain.cc b/modules/audio_processing/aec3/refined_filter_update_gain.cc index 80a55c21b3b..3686903970a 100644 --- a/modules/audio_processing/aec3/refined_filter_update_gain.cc +++ b/modules/audio_processing/aec3/refined_filter_update_gain.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/echo_path_variability.h" @@ -71,7 +71,7 @@ void RefinedFilterUpdateGain::Compute( const std::array& render_power, const RenderSignalAnalyzer& render_signal_analyzer, const SubtractorOutput& subtractor_output, - ArrayView erl, + std::span erl, size_t size_partitions, bool saturated_capture_signal, bool disallow_leakage_diverged, diff --git a/modules/audio_processing/aec3/refined_filter_update_gain.h b/modules/audio_processing/aec3/refined_filter_update_gain.h index 660a0137b5e..937ba8f974a 100644 --- a/modules/audio_processing/aec3/refined_filter_update_gain.h +++ b/modules/audio_processing/aec3/refined_filter_update_gain.h @@ -16,8 +16,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -49,7 +49,7 @@ class RefinedFilterUpdateGain { void Compute(const std::array& render_power, const RenderSignalAnalyzer& render_signal_analyzer, const SubtractorOutput& subtractor_output, - ArrayView erl, + std::span erl, size_t size_partitions, bool saturated_capture_signal, bool disallow_leakage_diverged, diff --git a/modules/audio_processing/aec3/render_buffer.h b/modules/audio_processing/aec3/render_buffer.h index 8f76bd04afe..6fc0317c6b2 100644 --- a/modules/audio_processing/aec3/render_buffer.h +++ b/modules/audio_processing/aec3/render_buffer.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "modules/audio_processing/aec3/block_buffer.h" @@ -48,7 +48,7 @@ class RenderBuffer { } // Get the spectrum from one of the FFTs in the buffer. - ArrayView> Spectrum( + std::span> Spectrum( int buffer_offset_ffts) const { int position = spectrum_buffer_->OffsetIndex(spectrum_buffer_->read, buffer_offset_ffts); @@ -56,7 +56,7 @@ class RenderBuffer { } // Returns the circular fft buffer. - ArrayView> GetFftBuffer() const { + std::span> GetFftBuffer() const { return fft_buffer_->buffer; } @@ -88,12 +88,12 @@ class RenderBuffer { int Headroom() const { // The write and read indices are decreased over time. int headroom = - fft_buffer_->write < fft_buffer_->read + fft_buffer_->write <= fft_buffer_->read ? fft_buffer_->read - fft_buffer_->write : fft_buffer_->size - fft_buffer_->write + fft_buffer_->read; RTC_DCHECK_LE(0, headroom); - RTC_DCHECK_GE(fft_buffer_->size, headroom); + RTC_DCHECK_GT(fft_buffer_->size, headroom); return headroom; } diff --git a/modules/audio_processing/aec3/render_delay_buffer.cc b/modules/audio_processing/aec3/render_delay_buffer.cc index b595eefc9e5..ecc7431fa48 100644 --- a/modules/audio_processing/aec3/render_delay_buffer.cc +++ b/modules/audio_processing/aec3/render_delay_buffer.cc @@ -19,9 +19,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_fft.h" @@ -104,7 +104,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer { int ComputeDelay() const; void ApplyTotalDelay(int delay); void InsertBlock(const Block& block, int previous_write); - bool DetectActiveRender(ArrayView x) const; + bool DetectActiveRender(std::span x) const; bool DetectExcessRenderBlocks(); void IncrementWriteIndices(); void IncrementLowRateReadIndices(); @@ -405,7 +405,7 @@ void RenderDelayBufferImpl::InsertBlock(const Block& block, if (render_linear_amplitude_gain_ != 1.f) { for (size_t band = 0; band < num_bands; ++band) { for (size_t ch = 0; ch < num_render_channels; ++ch) { - ArrayView b_view = b.buffer[b.write].View(band, ch); + std::span b_view = b.buffer[b.write].View(band, ch); for (float& sample : b_view) { sample *= render_linear_amplitude_gain_; } @@ -428,7 +428,7 @@ void RenderDelayBufferImpl::InsertBlock(const Block& block, } } -bool RenderDelayBufferImpl::DetectActiveRender(ArrayView x) const { +bool RenderDelayBufferImpl::DetectActiveRender(std::span x) const { const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); return x_energy > (config_.render_levels.active_render_limit * config_.render_levels.active_render_limit) * diff --git a/modules/audio_processing/aec3/render_delay_controller.cc b/modules/audio_processing/aec3/render_delay_controller.cc index a4046f26c1d..f7243d1c7c8 100644 --- a/modules/audio_processing/aec3/render_delay_controller.cc +++ b/modules/audio_processing/aec3/render_delay_controller.cc @@ -58,7 +58,6 @@ class RenderDelayControllerImpl final : public RenderDelayController { RenderDelayControllerMetrics metrics_; std::optional delay_samples_; size_t capture_call_counter_ = 0; - int delay_change_counter_ = 0; DelayEstimate::Quality last_delay_estimate_quality_; }; @@ -102,7 +101,6 @@ void RenderDelayControllerImpl::Reset(bool reset_delay_confidence) { delay_ = std::nullopt; delay_samples_ = std::nullopt; delay_estimator_.Reset(reset_delay_confidence); - delay_change_counter_ = 0; if (reset_delay_confidence) { last_delay_estimate_quality_ = DelayEstimate::Quality::kCoarse; } @@ -119,29 +117,7 @@ std::optional RenderDelayControllerImpl::GetDelay( auto delay_samples = delay_estimator_.EstimateDelay(render_buffer, capture); if (delay_samples) { - if (!delay_samples_ || delay_samples->delay != delay_samples_->delay) { - delay_change_counter_ = 0; - } - if (delay_samples_) { - delay_samples_->blocks_since_last_change = - delay_samples_->delay == delay_samples->delay - ? delay_samples_->blocks_since_last_change + 1 - : 0; - delay_samples_->blocks_since_last_update = 0; - delay_samples_->delay = delay_samples->delay; - delay_samples_->quality = delay_samples->quality; - } else { - delay_samples_ = delay_samples; - } - } else { - if (delay_samples_) { - ++delay_samples_->blocks_since_last_change; - ++delay_samples_->blocks_since_last_update; - } - } - - if (delay_change_counter_ < 2 * kNumBlocksPerSecond) { - ++delay_change_counter_; + delay_samples_ = delay_samples; } if (delay_samples_) { @@ -151,7 +127,7 @@ std::optional RenderDelayControllerImpl::GetDelay( delay_samples_->quality == DelayEstimate::Quality::kRefined; delay_ = ComputeBufferDelay( delay_, use_hysteresis ? hysteresis_limit_blocks_ : 0, *delay_samples_); - last_delay_estimate_quality_ = delay_samples_->quality; + last_delay_estimate_quality_ = delay_->quality; } metrics_.Update(delay_samples_ ? std::optional(delay_samples_->delay) @@ -163,7 +139,6 @@ std::optional RenderDelayControllerImpl::GetDelay( delay_samples ? delay_samples->delay : 0); data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", delay_ ? delay_->delay : 0); - return delay_; } diff --git a/modules/audio_processing/aec3/render_signal_analyzer.cc b/modules/audio_processing/aec3/render_signal_analyzer.cc index ae013e71896..9efb4bc2a40 100644 --- a/modules/audio_processing/aec3/render_signal_analyzer.cc +++ b/modules/audio_processing/aec3/render_signal_analyzer.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" @@ -43,7 +43,7 @@ void IdentifySmallNarrowBandRegions( std::array channel_counters; channel_counters.fill(0); - ArrayView> X2 = + std::span> X2 = render_buffer.Spectrum(*delay_partitions); for (size_t ch = 0; ch < X2.size(); ++ch) { for (size_t k = 1; k < kFftLengthBy2; ++k) { @@ -74,7 +74,7 @@ void IdentifyStrongNarrowBandComponent(const RenderBuffer& render_buffer, const Block& x_latest = render_buffer.GetBlock(0); float max_peak_level = 0.f; for (int channel = 0; channel < x_latest.NumChannels(); ++channel) { - ArrayView X2_latest = + std::span X2_latest = render_buffer.Spectrum(0)[channel]; // Identify the spectral peak. diff --git a/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc b/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc index 9cb8ebe005f..7a14adb75ae 100644 --- a/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc +++ b/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc @@ -19,7 +19,6 @@ #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_fft.h" diff --git a/modules/audio_processing/aec3/residual_echo_estimator.cc b/modules/audio_processing/aec3/residual_echo_estimator.cc index 9330f0773da..fd821cc3b1c 100644 --- a/modules/audio_processing/aec3/residual_echo_estimator.cc +++ b/modules/audio_processing/aec3/residual_echo_estimator.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/neural_residual_echo_estimator.h" #include "api/environment/environment.h" @@ -89,9 +89,9 @@ void GetRenderIndexesToAnalyze( // Estimates the residual echo power based on the echo return loss enhancement // (ERLE) and the linear power estimate. void LinearEstimate( - ArrayView> S2_linear, - ArrayView> erle, - ArrayView> R2) { + std::span> S2_linear, + std::span> erle, + std::span> R2) { RTC_DCHECK_EQ(S2_linear.size(), erle.size()); RTC_DCHECK_EQ(S2_linear.size(), R2.size()); @@ -108,7 +108,7 @@ void LinearEstimate( // gain. void NonLinearEstimate(float echo_path_gain, const std::array& X2, - ArrayView> R2) { + std::span> R2) { const size_t num_capture_channels = R2.size(); for (size_t ch = 0; ch < num_capture_channels; ++ch) { for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { @@ -119,7 +119,7 @@ void NonLinearEstimate(float echo_path_gain, // Applies a soft noise gate to the echo generating power. void ApplyNoiseGate(const EchoCanceller3Config::EchoModel& config, - ArrayView X2) { + std::span X2) { for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { if (config.noise_gate_power > X2[k]) { X2[k] = std::max(0.f, X2[k] - config.noise_gate_slope * @@ -134,7 +134,7 @@ void EchoGeneratingPower(size_t num_render_channels, const SpectrumBuffer& spectrum_buffer, const EchoCanceller3Config::EchoModel& echo_model, int filter_delay_blocks, - ArrayView X2) { + std::span X2) { int idx_stop; int idx_start; GetRenderIndexesToAnalyze(spectrum_buffer, echo_model, filter_delay_blocks, @@ -193,19 +193,21 @@ ResidualEchoEstimator::~ResidualEchoEstimator() = default; void ResidualEchoEstimator::Estimate( const AecState& aec_state, const RenderBuffer& render_buffer, - ArrayView> capture, - ArrayView> linear_aec_output, - ArrayView> S2_linear, - ArrayView> Y2, - ArrayView> E2, + std::span> capture, + std::span> linear_aec_output, + std::span> S2_linear, + std::span> Y2, + std::span> E2, bool dominant_nearend, - ArrayView> R2, - ArrayView> R2_unbounded) { + std::span> R2, + std::span> R2_unbounded) { RTC_DCHECK_EQ(R2.size(), Y2.size()); RTC_DCHECK_EQ(R2.size(), S2_linear.size()); const size_t num_capture_channels = R2.size(); + is_ml_ree_active_ = neural_residual_echo_estimator_ != nullptr && + aec_state.UsableLinearEstimate(); // Estimate the power of the stationary noise in the render signal. UpdateRenderNoisePower(render_buffer); @@ -235,9 +237,9 @@ void ResidualEchoEstimator::Estimate( R2, R2_unbounded); } - // Estimate the residual echo power. - if (aec_state.UsableLinearEstimate()) { - if (neural_residual_echo_estimator_ == nullptr) { + // Estimate the residual echo power, used when ml_ree is not active. + if (!is_ml_ree_active_) { + if (aec_state.UsableLinearEstimate()) { // When there is saturated echo, assume the same spectral content as is // present in the microphone signal. if (aec_state.SaturatedEcho()) { @@ -256,48 +258,48 @@ void ResidualEchoEstimator::Estimate( dominant_nearend); AddReverb(R2); AddReverb(R2_unbounded); - } - } else { - const float echo_path_gain = - GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/true); - - // When there is saturated echo, assume the same spectral content as is - // present in the microphone signal. - if (aec_state.SaturatedEcho()) { - for (size_t ch = 0; ch < num_capture_channels; ++ch) { - std::copy(Y2[ch].begin(), Y2[ch].end(), R2[ch].begin()); - std::copy(Y2[ch].begin(), Y2[ch].end(), R2_unbounded[ch].begin()); - } } else { - // Estimate the echo generating signal power. - std::array X2; - EchoGeneratingPower(num_render_channels_, - render_buffer.GetSpectrumBuffer(), config_.echo_model, - aec_state.MinDirectPathFilterDelay(), X2); - if (!aec_state.UseStationarityProperties()) { - ApplyNoiseGate(config_.echo_model, X2); - } + const float echo_path_gain = + GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/true); - // Subtract the stationary noise power to avoid stationary noise causing - // excessive echo suppression. - for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { - X2[k] -= config_.echo_model.stationary_gate_slope * X2_noise_floor_[k]; - X2[k] = std::max(0.f, X2[k]); - } + // When there is saturated echo, assume the same spectral content as is + // present in the microphone signal. + if (aec_state.SaturatedEcho()) { + for (size_t ch = 0; ch < num_capture_channels; ++ch) { + std::copy(Y2[ch].begin(), Y2[ch].end(), R2[ch].begin()); + std::copy(Y2[ch].begin(), Y2[ch].end(), R2_unbounded[ch].begin()); + } + } else { + // Estimate the echo generating signal power. + std::array X2; + EchoGeneratingPower( + num_render_channels_, render_buffer.GetSpectrumBuffer(), + config_.echo_model, aec_state.MinDirectPathFilterDelay(), X2); + if (!aec_state.UseStationarityProperties()) { + ApplyNoiseGate(config_.echo_model, X2); + } - NonLinearEstimate(echo_path_gain, X2, R2); - NonLinearEstimate(echo_path_gain, X2, R2_unbounded); - } + // Subtract the stationary noise power to avoid stationary noise causing + // excessive echo suppression. + for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { + X2[k] -= + config_.echo_model.stationary_gate_slope * X2_noise_floor_[k]; + X2[k] = std::max(0.f, X2[k]); + } - if (config_.echo_model.model_reverb_in_nonlinear_mode && - !aec_state.TransparentModeActive()) { - UpdateReverb(ReverbType::kNonLinear, aec_state, render_buffer, - dominant_nearend); - AddReverb(R2); - AddReverb(R2_unbounded); + NonLinearEstimate(echo_path_gain, X2, R2); + NonLinearEstimate(echo_path_gain, X2, R2_unbounded); + } + + if (config_.echo_model.model_reverb_in_nonlinear_mode && + !aec_state.TransparentModeActive()) { + UpdateReverb(ReverbType::kNonLinear, aec_state, render_buffer, + dominant_nearend); + AddReverb(R2); + AddReverb(R2_unbounded); + } } } - if (aec_state.UseStationarityProperties()) { // Scale the echo according to echo audibility. std::array residual_scaling; @@ -315,14 +317,17 @@ void ResidualEchoEstimator::Reset() { echo_reverb_.Reset(); X2_noise_floor_counter_.fill(config_.echo_model.noise_floor_hold); X2_noise_floor_.fill(config_.echo_model.min_noise_floor_power); + if (neural_residual_echo_estimator_) { + neural_residual_echo_estimator_->Reset(); + } } void ResidualEchoEstimator::UpdateRenderNoisePower( const RenderBuffer& render_buffer) { std::array render_power_data; - ArrayView> X2 = + std::span> X2 = render_buffer.Spectrum(0); - ArrayView render_power = X2[/*channel=*/0]; + std::span render_power = X2[/*channel=*/0]; if (num_render_channels_ > 1) { render_power_data.fill(0.f); for (size_t ch = 0; ch < num_render_channels_; ++ch) { @@ -366,9 +371,9 @@ void ResidualEchoEstimator::UpdateReverb(ReverbType reverb_type, // Compute render power for the reverb. std::array render_power_data; - ArrayView> X2 = + std::span> X2 = render_buffer.Spectrum(first_reverb_partition); - ArrayView render_power = X2[/*channel=*/0]; + std::span render_power = X2[/*channel=*/0]; if (num_render_channels_ > 1) { render_power_data.fill(0.f); for (size_t ch = 0; ch < num_render_channels_; ++ch) { @@ -394,11 +399,11 @@ void ResidualEchoEstimator::UpdateReverb(ReverbType reverb_type, } // Adds the estimated power of the reverb to the residual echo power. void ResidualEchoEstimator::AddReverb( - ArrayView> R2) const { + std::span> R2) const { const size_t num_capture_channels = R2.size(); // Add the reverb power. - ArrayView reverb_power = + std::span reverb_power = echo_reverb_.reverb(); for (size_t ch = 0; ch < num_capture_channels; ++ch) { for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) { diff --git a/modules/audio_processing/aec3/residual_echo_estimator.h b/modules/audio_processing/aec3/residual_echo_estimator.h index 22358bca2aa..0a53e0d2034 100644 --- a/modules/audio_processing/aec3/residual_echo_estimator.h +++ b/modules/audio_processing/aec3/residual_echo_estimator.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/neural_residual_echo_estimator.h" #include "api/environment/environment.h" @@ -40,14 +40,17 @@ class ResidualEchoEstimator { void Estimate( const AecState& aec_state, const RenderBuffer& render_buffer, - ArrayView> capture, - ArrayView> linear_aec_output, - ArrayView> S2_linear, - ArrayView> Y2, - ArrayView> E2, + std::span> capture, + std::span> linear_aec_output, + std::span> S2_linear, + std::span> Y2, + std::span> E2, bool dominant_nearend, - ArrayView> R2, - ArrayView> R2_unbounded); + std::span> R2, + std::span> R2_unbounded); + + // Returns true if ML-REE is active. + bool IsMlReeActive() const { return is_ml_ree_active_; } private: enum class ReverbType { kLinear, kNonLinear }; @@ -67,7 +70,7 @@ class ResidualEchoEstimator { // Adds the estimated unmodelled echo power to the residual echo power // estimate. - void AddReverb(ArrayView> R2) const; + void AddReverb(std::span> R2) const; // Gets the echo path gain to apply. float GetEchoPathGain(const AecState& aec_state, @@ -84,6 +87,7 @@ class ResidualEchoEstimator { std::array X2_noise_floor_counter_; ReverbModel echo_reverb_; NeuralResidualEchoEstimator* neural_residual_echo_estimator_; + bool is_ml_ree_active_ = false; }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc index 05c8c58b3ac..a3351c831ec 100644 --- a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc +++ b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -122,7 +122,7 @@ class ResidualEchoEstimatorTest { R2_, R2_unbounded_); } - ArrayView> R2() const { + std::span> R2() const { return R2_; } diff --git a/modules/audio_processing/aec3/reverb_decay_estimator.cc b/modules/audio_processing/aec3/reverb_decay_estimator.cc index bd7c4251ed4..b010629ecee 100644 --- a/modules/audio_processing/aec3/reverb_decay_estimator.cc +++ b/modules/audio_processing/aec3/reverb_decay_estimator.cc @@ -16,8 +16,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/logging/apm_data_dumper.h" @@ -34,7 +34,7 @@ constexpr float kEarlyReverbFirstPointAtLinearRegressors = -0.5f * kBlocksPerSection * kFftLengthBy2 + 0.5f; // Averages the values in a block of size kFftLengthBy2; -float BlockAverage(ArrayView v, size_t block_index) { +float BlockAverage(std::span v, size_t block_index) { constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2; const int i = block_index * kFftLengthBy2; RTC_DCHECK_GE(v.size(), i + kFftLengthBy2); @@ -62,7 +62,7 @@ constexpr float SymmetricArithmetricSum(int N) { } // Returns the peak energy of an impulse response. -float BlockEnergyPeak(ArrayView h, int peak_block) { +float BlockEnergyPeak(std::span h, int peak_block) { RTC_DCHECK_LE((peak_block + 1) * kFftLengthBy2, h.size()); RTC_DCHECK_GE(peak_block, 0); float peak_value = @@ -73,7 +73,7 @@ float BlockEnergyPeak(ArrayView h, int peak_block) { } // Returns the average energy of an impulse response block. -float BlockEnergyAverage(ArrayView h, int block_index) { +float BlockEnergyAverage(std::span h, int block_index) { RTC_DCHECK_LE((block_index + 1) * kFftLengthBy2, h.size()); RTC_DCHECK_GE(block_index, 0); constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2; @@ -103,7 +103,7 @@ ReverbDecayEstimator::ReverbDecayEstimator(const EchoCanceller3Config& config) ReverbDecayEstimator::~ReverbDecayEstimator() = default; -void ReverbDecayEstimator::Update(ArrayView filter, +void ReverbDecayEstimator::Update(std::span filter, const std::optional& filter_quality, int filter_delay_blocks, bool usable_linear_filter, @@ -159,7 +159,7 @@ void ReverbDecayEstimator::ResetDecayEstimation() { late_reverb_end_ = 0; } -void ReverbDecayEstimator::EstimateDecay(ArrayView filter, +void ReverbDecayEstimator::EstimateDecay(std::span filter, int peak_block) { auto& h = filter; RTC_DCHECK_EQ(0, h.size() % kFftLengthBy2); @@ -223,9 +223,8 @@ void ReverbDecayEstimator::EstimateDecay(ArrayView filter, early_reverb_estimator_.Reset(); } -void ReverbDecayEstimator::AnalyzeFilter(ArrayView filter) { - auto h = ArrayView( - filter.begin() + block_to_analyze_ * kFftLengthBy2, kFftLengthBy2); +void ReverbDecayEstimator::AnalyzeFilter(std::span filter) { + auto h = filter.subspan(block_to_analyze_ * kFftLengthBy2, kFftLengthBy2); // Compute squared filter coeffiecients for the block to analyze_; std::array h2; diff --git a/modules/audio_processing/aec3/reverb_decay_estimator.h b/modules/audio_processing/aec3/reverb_decay_estimator.h index 721cd2bbec3..08fd6ca9004 100644 --- a/modules/audio_processing/aec3/reverb_decay_estimator.h +++ b/modules/audio_processing/aec3/reverb_decay_estimator.h @@ -12,10 +12,9 @@ #define MODULES_AUDIO_PROCESSING_AEC3_REVERB_DECAY_ESTIMATOR_H_ #include +#include #include -#include "api/array_view.h" - namespace webrtc { class ApmDataDumper; @@ -27,7 +26,7 @@ class ReverbDecayEstimator { explicit ReverbDecayEstimator(const EchoCanceller3Config& config); ~ReverbDecayEstimator(); // Updates the decay estimate. - void Update(ArrayView filter, + void Update(std::span filter, const std::optional& filter_quality, int filter_delay_blocks, bool usable_linear_filter, @@ -45,8 +44,8 @@ class ReverbDecayEstimator { void Dump(ApmDataDumper* data_dumper) const; private: - void EstimateDecay(ArrayView filter, int peak_block); - void AnalyzeFilter(ArrayView filter); + void EstimateDecay(std::span filter, int peak_block); + void AnalyzeFilter(std::span filter); void ResetDecayEstimation(); diff --git a/modules/audio_processing/aec3/reverb_frequency_response.cc b/modules/audio_processing/aec3/reverb_frequency_response.cc index c375ad1820c..574eefc4c9e 100644 --- a/modules/audio_processing/aec3/reverb_frequency_response.cc +++ b/modules/audio_processing/aec3/reverb_frequency_response.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" @@ -28,8 +28,8 @@ namespace { // Computes the ratio of the energies between the direct path and the tail. The // energy is computed in the power spectrum domain discarding the DC // contributions. -float AverageDecayWithinFilter(ArrayView freq_resp_direct_path, - ArrayView freq_resp_tail) { +float AverageDecayWithinFilter(std::span freq_resp_direct_path, + std::span freq_resp_tail) { // Skipping the DC for the ratio computation constexpr size_t kSkipBins = 1; RTC_CHECK_EQ(freq_resp_direct_path.size(), freq_resp_tail.size()); @@ -76,10 +76,10 @@ void ReverbFrequencyResponse::Update( frequency_response, int filter_delay_blocks, float linear_filter_quality) { - ArrayView freq_resp_tail( + std::span freq_resp_tail( frequency_response[frequency_response.size() - 1]); - ArrayView freq_resp_direct_path( + std::span freq_resp_direct_path( frequency_response[filter_delay_blocks]); float average_decay = diff --git a/modules/audio_processing/aec3/reverb_frequency_response.h b/modules/audio_processing/aec3/reverb_frequency_response.h index 3fecba7396a..ee36bce0e03 100644 --- a/modules/audio_processing/aec3/reverb_frequency_response.h +++ b/modules/audio_processing/aec3/reverb_frequency_response.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" namespace webrtc { @@ -35,7 +35,7 @@ class ReverbFrequencyResponse { bool stationary_block); // Returns the estimated frequency response for the reverb. - ArrayView FrequencyResponse() const { return tail_response_; } + std::span FrequencyResponse() const { return tail_response_; } private: void Update(const std::vector>& diff --git a/modules/audio_processing/aec3/reverb_model.cc b/modules/audio_processing/aec3/reverb_model.cc index 7bd719c76d8..63c30cbaa64 100644 --- a/modules/audio_processing/aec3/reverb_model.cc +++ b/modules/audio_processing/aec3/reverb_model.cc @@ -11,8 +11,7 @@ #include "modules/audio_processing/aec3/reverb_model.h" #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -27,7 +26,7 @@ void ReverbModel::Reset() { } void ReverbModel::UpdateReverbNoFreqShaping( - ArrayView power_spectrum, + std::span power_spectrum, float power_spectrum_scaling, float reverb_decay) { if (reverb_decay > 0) { @@ -39,8 +38,8 @@ void ReverbModel::UpdateReverbNoFreqShaping( } } -void ReverbModel::UpdateReverb(ArrayView power_spectrum, - ArrayView power_spectrum_scaling, +void ReverbModel::UpdateReverb(std::span power_spectrum, + std::span power_spectrum_scaling, float reverb_decay) { if (reverb_decay > 0) { // Update the estimate of the reverberant power. diff --git a/modules/audio_processing/aec3/reverb_model.h b/modules/audio_processing/aec3/reverb_model.h index 15e24895e60..a05f38016f8 100644 --- a/modules/audio_processing/aec3/reverb_model.h +++ b/modules/audio_processing/aec3/reverb_model.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_AEC3_REVERB_MODEL_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" namespace webrtc { @@ -29,7 +29,7 @@ class ReverbModel { void Reset(); // Returns the reverb. - ArrayView reverb() const { return reverb_; } + std::span reverb() const { return reverb_; } // The methods UpdateReverbNoFreqShaping and UpdateReverb update the // estimate of the reverberation contribution to an input/output power @@ -37,13 +37,13 @@ class ReverbModel { // power spectrum is pre-scaled. Use the method UpdateReverb when a different // scaling should be applied per frequency and UpdateReverb_no_freq_shape if // the same scaling should be used for all the frequencies. - void UpdateReverbNoFreqShaping(ArrayView power_spectrum, + void UpdateReverbNoFreqShaping(std::span power_spectrum, float power_spectrum_scaling, float reverb_decay); // Update the reverb based on new data. - void UpdateReverb(ArrayView power_spectrum, - ArrayView power_spectrum_scaling, + void UpdateReverb(std::span power_spectrum, + std::span power_spectrum_scaling, float reverb_decay); private: diff --git a/modules/audio_processing/aec3/reverb_model_estimator.cc b/modules/audio_processing/aec3/reverb_model_estimator.cc index 64652999092..afbd110e6db 100644 --- a/modules/audio_processing/aec3/reverb_model_estimator.cc +++ b/modules/audio_processing/aec3/reverb_model_estimator.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/reverb_decay_estimator.h" @@ -41,11 +41,11 @@ ReverbModelEstimator::ReverbModelEstimator(const EchoCanceller3Config& config, ReverbModelEstimator::~ReverbModelEstimator() = default; void ReverbModelEstimator::Update( - ArrayView> impulse_responses, - ArrayView>> + std::span> impulse_responses, + std::span>> frequency_responses, - ArrayView> linear_filter_qualities, - ArrayView filter_delays_blocks, + std::span> linear_filter_qualities, + std::span filter_delays_blocks, const std::vector& usable_linear_estimates, bool stationary_block) { const size_t num_capture_channels = reverb_decay_estimators_.size(); diff --git a/modules/audio_processing/aec3/reverb_model_estimator.h b/modules/audio_processing/aec3/reverb_model_estimator.h index 08bbc864478..50bd97e0660 100644 --- a/modules/audio_processing/aec3/reverb_model_estimator.h +++ b/modules/audio_processing/aec3/reverb_model_estimator.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" // kFftLengthBy2Plus1 #include "modules/audio_processing/aec3/reverb_decay_estimator.h" @@ -36,11 +36,11 @@ class ReverbModelEstimator { // Updates the estimates based on new data. void Update( - ArrayView> impulse_responses, - ArrayView>> + std::span> impulse_responses, + std::span>> frequency_responses, - ArrayView> linear_filter_qualities, - ArrayView filter_delays_blocks, + std::span> linear_filter_qualities, + std::span filter_delays_blocks, const std::vector& usable_linear_estimates, bool stationary_block); @@ -54,7 +54,7 @@ class ReverbModelEstimator { // Return the frequency response of the reverberant echo. // TODO(peah): Correct to properly support multiple channels. - ArrayView GetReverbFrequencyResponse() const { + std::span GetReverbFrequencyResponse() const { return reverb_frequency_responses_[0].FrequencyResponse(); } diff --git a/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc b/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc index e3cde9e4e18..8f5be5a74b7 100644 --- a/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc +++ b/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_fft.h" @@ -110,7 +110,7 @@ void ReverbModelEstimatorTest::CreateImpulseResponseWithDecay() { H_j.Spectrum(Aec3Optimization::kNone, H2_[ch][j]); } } - ArrayView H2_tail(H2_[0][H2_[0].size() - 1]); + std::span H2_tail(H2_[0][H2_[0].size() - 1]); true_power_tail_ = std::accumulate(H2_tail.begin(), H2_tail.end(), 0.f); } void ReverbModelEstimatorTest::RunEstimator() { diff --git a/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc b/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc index d0a854c57b7..571b4823dee 100644 --- a/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc +++ b/modules/audio_processing/aec3/signal_dependent_erle_estimator.cc @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/render_buffer.h" @@ -187,13 +187,13 @@ void SignalDependentErleEstimator::Reset() { // correction factor to the erle that is given as an input to this method. void SignalDependentErleEstimator::Update( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_responses, - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, - ArrayView> average_erle, - ArrayView> + std::span X2, + std::span> Y2, + std::span> E2, + std::span> average_erle, + std::span> average_erle_onset_compensated, const std::vector& converged_filters) { RTC_DCHECK_GT(num_sections_, 1); @@ -241,7 +241,7 @@ void SignalDependentErleEstimator::Dump( // together constitute 90% of the estimated echo energy. void SignalDependentErleEstimator::ComputeNumberOfActiveFilterSections( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_responses) { RTC_DCHECK_GT(num_sections_, 1); // Computes an approximation of the power spectrum if the filter would have @@ -254,17 +254,17 @@ void SignalDependentErleEstimator::ComputeNumberOfActiveFilterSections( } void SignalDependentErleEstimator::UpdateCorrectionFactors( - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters) { for (size_t ch = 0; ch < converged_filters.size(); ++ch) { if (converged_filters[ch]) { constexpr float kX2BandEnergyThreshold = 44015068.0f; constexpr float kSmthConstantDecreases = 0.1f; constexpr float kSmthConstantIncreases = kSmthConstantDecreases / 2.f; - auto subband_powers = [](ArrayView power_spectrum, - ArrayView power_spectrum_subbands) { + auto subband_powers = [](std::span power_spectrum, + std::span power_spectrum_subbands) { for (size_t subband = 0; subband < kSubbands; ++subband) { RTC_DCHECK_LE(kBandBoundaries[subband + 1], power_spectrum.size()); power_spectrum_subbands[subband] = std::accumulate( @@ -354,7 +354,7 @@ void SignalDependentErleEstimator::UpdateCorrectionFactors( void SignalDependentErleEstimator::ComputeEchoEstimatePerFilterSection( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_responses) { const SpectrumBuffer& spectrum_render_buffer = render_buffer.GetSpectrumBuffer(); diff --git a/modules/audio_processing/aec3/signal_dependent_erle_estimator.h b/modules/audio_processing/aec3/signal_dependent_erle_estimator.h index 4c58bd4f4f3..992057d78f5 100644 --- a/modules/audio_processing/aec3/signal_dependent_erle_estimator.h +++ b/modules/audio_processing/aec3/signal_dependent_erle_estimator.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/render_buffer.h" @@ -39,7 +39,7 @@ class SignalDependentErleEstimator { void Reset(); // Returns the Erle per frequency subband. - ArrayView> Erle( + std::span> Erle( bool onset_compensated) const { return onset_compensated && use_onset_detection_ ? erle_onset_compensated_ : erle_; @@ -49,13 +49,13 @@ class SignalDependentErleEstimator { // to be an estimation of the average Erle achieved by the linear filter. void Update( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_response, - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, - ArrayView> average_erle, - ArrayView> + std::span X2, + std::span> Y2, + std::span> E2, + std::span> average_erle, + std::span> average_erle_onset_compensated, const std::vector& converged_filters); @@ -66,18 +66,18 @@ class SignalDependentErleEstimator { private: void ComputeNumberOfActiveFilterSections( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_responses); void UpdateCorrectionFactors( - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters); void ComputeEchoEstimatePerFilterSection( const RenderBuffer& render_buffer, - ArrayView>> + std::span>> filter_frequency_responses); void ComputeActiveFilterSections(); diff --git a/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc b/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc index c834b39e94a..611b25ca079 100644 --- a/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc +++ b/modules/audio_processing/aec3/signal_dependent_erle_estimator_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" @@ -56,14 +56,14 @@ class TestInputs { size_t num_capture_channels); ~TestInputs(); const RenderBuffer& GetRenderBuffer() { return *render_buffer_; } - ArrayView GetX2() { return X2_; } - ArrayView> GetY2() const { + std::span GetX2() { return X2_; } + std::span> GetY2() const { return Y2_; } - ArrayView> GetE2() const { + std::span> GetE2() const { return E2_; } - ArrayView>> GetH2() + std::span>> GetH2() const { return H2_; } diff --git a/modules/audio_processing/aec3/stationarity_estimator.cc b/modules/audio_processing/aec3/stationarity_estimator.cc index cba546bdd16..7b3f8f8c095 100644 --- a/modules/audio_processing/aec3/stationarity_estimator.cc +++ b/modules/audio_processing/aec3/stationarity_estimator.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/spectrum_buffer.h" #include "modules/audio_processing/logging/apm_data_dumper.h" @@ -45,7 +45,7 @@ void StationarityEstimator::Reset() { // Update just the noise estimator. Usefull until the delay is known void StationarityEstimator::UpdateNoiseEstimator( - ArrayView> spectrum) { + std::span> spectrum) { noise_.Update(spectrum); data_dumper_->DumpRaw("aec3_stationarity_noise_spectrum", noise_.Spectrum()); data_dumper_->DumpRaw("aec3_stationarity_is_block_stationary", @@ -54,7 +54,7 @@ void StationarityEstimator::UpdateNoiseEstimator( void StationarityEstimator::UpdateStationarityFlags( const SpectrumBuffer& spectrum_buffer, - ArrayView render_reverb_contribution_spectrum, + std::span render_reverb_contribution_spectrum, int idx_current, int num_lookahead) { std::array indexes; @@ -99,7 +99,7 @@ bool StationarityEstimator::IsBlockStationary() const { bool StationarityEstimator::EstimateBandStationarity( const SpectrumBuffer& spectrum_buffer, - ArrayView average_reverb, + std::span average_reverb, const std::array& indexes, size_t band) const { constexpr float kThrStationarity = 10.f; @@ -168,12 +168,12 @@ void StationarityEstimator::NoiseSpectrum::Reset() { } void StationarityEstimator::NoiseSpectrum::Update( - ArrayView> spectrum) { + std::span> spectrum) { RTC_DCHECK_LE(1, spectrum[0].size()); const int num_render_channels = static_cast(spectrum.size()); std::array avg_spectrum_data; - ArrayView avg_spectrum; + std::span avg_spectrum; if (num_render_channels == 1) { avg_spectrum = spectrum[0]; } else { diff --git a/modules/audio_processing/aec3/stationarity_estimator.h b/modules/audio_processing/aec3/stationarity_estimator.h index 0c10a099b05..e0a66cac0b1 100644 --- a/modules/audio_processing/aec3/stationarity_estimator.h +++ b/modules/audio_processing/aec3/stationarity_estimator.h @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" // kFftLengthBy2Plus1... #include "rtc_base/checks.h" @@ -35,13 +35,13 @@ class StationarityEstimator { // Update just the noise estimator. Usefull until the delay is known void UpdateNoiseEstimator( - ArrayView> spectrum); + std::span> spectrum); // Update the flag indicating whether this current frame is stationary. For // getting a more robust estimation, it looks at future and/or past frames. void UpdateStationarityFlags( const SpectrumBuffer& spectrum_buffer, - ArrayView render_reverb_contribution_spectrum, + std::span render_reverb_contribution_spectrum, int idx_current, int num_lookahead); @@ -61,7 +61,7 @@ class StationarityEstimator { // Get an estimation of the stationarity for the current band by looking // at the past/present/future available data. bool EstimateBandStationarity(const SpectrumBuffer& spectrum_buffer, - ArrayView average_reverb, + std::span average_reverb, const std::array& indexes, size_t band) const; @@ -86,10 +86,10 @@ class StationarityEstimator { // Update the noise power spectrum with a new frame. void Update( - ArrayView> spectrum); + std::span> spectrum); // Get the noise estimation power spectrum. - ArrayView Spectrum() const { return noise_spectrum_; } + std::span Spectrum() const { return noise_spectrum_; } // Get the noise power spectrum at a certain band. float Power(size_t band) const { diff --git a/modules/audio_processing/aec3/subband_erle_estimator.cc b/modules/audio_processing/aec3/subband_erle_estimator.cc index 8db40a25797..c71d5e8d062 100644 --- a/modules/audio_processing/aec3/subband_erle_estimator.cc +++ b/modules/audio_processing/aec3/subband_erle_estimator.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" @@ -83,9 +83,9 @@ void SubbandErleEstimator::Reset() { } void SubbandErleEstimator::Update( - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters) { UpdateAccumulatedSpectra(X2, Y2, E2, converged_filters); UpdateBands(converged_filters); @@ -221,9 +221,9 @@ void SubbandErleEstimator::ResetAccumulatedSpectra() { } void SubbandErleEstimator::UpdateAccumulatedSpectra( - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters) { auto& st = accum_spectra_; RTC_DCHECK_EQ(st.E2.size(), E2.size()); diff --git a/modules/audio_processing/aec3/subband_erle_estimator.h b/modules/audio_processing/aec3/subband_erle_estimator.h index fdfaad8872d..5e639fe2e4e 100644 --- a/modules/audio_processing/aec3/subband_erle_estimator.h +++ b/modules/audio_processing/aec3/subband_erle_estimator.h @@ -15,9 +15,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "modules/audio_processing/aec3/aec3_common.h" @@ -37,25 +37,25 @@ class SubbandErleEstimator { void Reset(); // Updates the ERLE estimate. - void Update(ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + void Update(std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters); // Returns the ERLE estimate. - ArrayView> Erle( + std::span> Erle( bool onset_compensated) const { return onset_compensated && use_onset_detection_ ? erle_onset_compensated_ : erle_; } // Returns the non-capped ERLE estimate. - ArrayView> ErleUnbounded() const { + std::span> ErleUnbounded() const { return erle_unbounded_; } // Returns the ERLE estimate at onsets (only used for testing). - ArrayView> ErleDuringOnsets() + std::span> ErleDuringOnsets() const { return erle_during_onsets_; } @@ -76,9 +76,9 @@ class SubbandErleEstimator { }; void UpdateAccumulatedSpectra( - ArrayView X2, - ArrayView> Y2, - ArrayView> E2, + std::span X2, + std::span> Y2, + std::span> E2, const std::vector& converged_filters); void ResetAccumulatedSpectra(); diff --git a/modules/audio_processing/aec3/subband_nearend_detector.cc b/modules/audio_processing/aec3/subband_nearend_detector.cc index 37b356367bc..8cd85e4ca0d 100644 --- a/modules/audio_processing/aec3/subband_nearend_detector.cc +++ b/modules/audio_processing/aec3/subband_nearend_detector.cc @@ -13,11 +13,11 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" -#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/moving_average_spectrum.h" namespace webrtc { SubbandNearendDetector::SubbandNearendDetector( @@ -26,18 +26,18 @@ SubbandNearendDetector::SubbandNearendDetector( : config_(config), num_capture_channels_(num_capture_channels), nearend_smoothers_(num_capture_channels_, - aec3::MovingAverage(kFftLengthBy2Plus1, - config_.nearend_average_blocks)), + MovingAverageSpectrum(kFftLengthBy2Plus1, + config_.nearend_average_blocks)), one_over_subband_length1_( 1.f / (config_.subband1.high - config_.subband1.low + 1)), one_over_subband_length2_( 1.f / (config_.subband2.high - config_.subband2.low + 1)) {} void SubbandNearendDetector::Update( - ArrayView> nearend_spectrum, - ArrayView> + std::span> nearend_spectrum, + std::span> /* residual_echo_spectrum */, - ArrayView> + std::span> comfort_noise_spectrum, bool /* initial_state */) { nearend_state_ = false; @@ -73,4 +73,17 @@ void SubbandNearendDetector::Update( (nearend_power_subband1 > config_.snr_threshold * noise_power)); } } + +void SubbandNearendDetector::SetConfig( + const EchoCanceller3Config::Suppressor& config) { + config_ = config.subband_nearend_detection; + one_over_subband_length1_ = + 1.f / (config_.subband1.high - config_.subband1.low + 1); + one_over_subband_length2_ = + 1.f / (config_.subband2.high - config_.subband2.low + 1); + for (auto& smoother : nearend_smoothers_) { + smoother.UpdateMemoryLength(config_.nearend_average_blocks); + } +} + } // namespace webrtc diff --git a/modules/audio_processing/aec3/subband_nearend_detector.h b/modules/audio_processing/aec3/subband_nearend_detector.h index ab733b9acbd..e28391f61db 100644 --- a/modules/audio_processing/aec3/subband_nearend_detector.h +++ b/modules/audio_processing/aec3/subband_nearend_detector.h @@ -13,12 +13,12 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" -#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/moving_average_spectrum.h" #include "modules/audio_processing/aec3/nearend_detector.h" namespace webrtc { @@ -34,19 +34,22 @@ class SubbandNearendDetector : public NearendDetector { // Updates the state selection based on latest spectral estimates. void Update( - ArrayView> nearend_spectrum, - ArrayView> + std::span> nearend_spectrum, + std::span> residual_echo_spectrum, - ArrayView> + std::span> comfort_noise_spectrum, bool initial_state) override; + // Sets the configuration. + void SetConfig(const EchoCanceller3Config::Suppressor& config) override; + private: - const EchoCanceller3Config::Suppressor::SubbandNearendDetection config_; + EchoCanceller3Config::Suppressor::SubbandNearendDetection config_; const size_t num_capture_channels_; - std::vector nearend_smoothers_; - const float one_over_subband_length1_; - const float one_over_subband_length2_; + std::vector nearend_smoothers_; + float one_over_subband_length1_; + float one_over_subband_length2_; bool nearend_state_ = false; }; diff --git a/modules/audio_processing/aec3/subtractor.cc b/modules/audio_processing/aec3/subtractor.cc index bfce13f42db..8f9d7752e9b 100644 --- a/modules/audio_processing/aec3/subtractor.cc +++ b/modules/audio_processing/aec3/subtractor.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" @@ -48,7 +48,7 @@ bool UseCoarseFilterResetHangover(const FieldTrialsView& field_trials) { void PredictionError(const Aec3Fft& fft, const FftData& S, - ArrayView y, + std::span y, std::array* e, std::array* s) { std::array tmp; @@ -64,10 +64,10 @@ void PredictionError(const Aec3Fft& fft, } } -void ScaleFilterOutput(ArrayView y, +void ScaleFilterOutput(std::span y, float factor, - ArrayView e, - ArrayView s) { + std::span e, + std::span s) { RTC_DCHECK_EQ(y.size(), e.size()); RTC_DCHECK_EQ(y.size(), s.size()); for (size_t k = 0; k < y.size(); ++k) { @@ -197,7 +197,7 @@ void Subtractor::Process(const RenderBuffer& render_buffer, const Block& capture, const RenderSignalAnalyzer& render_signal_analyzer, const AecState& aec_state, - ArrayView outputs) { + std::span outputs) { RTC_DCHECK_EQ(num_capture_channels_, capture.NumChannels()); // Compute the render powers. @@ -223,7 +223,7 @@ void Subtractor::Process(const RenderBuffer& render_buffer, // Process all capture channels for (size_t ch = 0; ch < num_capture_channels_; ++ch) { SubtractorOutput& output = outputs[ch]; - ArrayView y = capture.View(/*band=*/0, ch); + std::span y = capture.View(/*band=*/0, ch); FftData& E_refined = output.E_refined; FftData E_coarse; std::array& e_refined = output.e_refined; diff --git a/modules/audio_processing/aec3/subtractor.h b/modules/audio_processing/aec3/subtractor.h index 148bdf2c1f7..e0402900ebd 100644 --- a/modules/audio_processing/aec3/subtractor.h +++ b/modules/audio_processing/aec3/subtractor.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "modules/audio_processing/aec3/adaptive_fir_filter.h" @@ -54,7 +54,7 @@ class Subtractor { const Block& capture, const RenderSignalAnalyzer& render_signal_analyzer, const AecState& aec_state, - ArrayView outputs); + std::span outputs); void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); @@ -77,7 +77,7 @@ class Subtractor { void DumpFilters() { data_dumper_->DumpRaw( "aec3_subtractor_h_refined", - ArrayView( + std::span( refined_impulse_responses_[0].data(), GetTimeDomainLength( refined_filters_[0]->max_filter_size_partitions()))); @@ -85,7 +85,7 @@ class Subtractor { RTC_DCHECK_GT(coarse_impulse_responses_.size(), 0); data_dumper_->DumpRaw( "aec3_subtractor_h_coarse", - ArrayView( + std::span( coarse_impulse_responses_[0].data(), GetTimeDomainLength( coarse_filter_[0]->max_filter_size_partitions()))); diff --git a/modules/audio_processing/aec3/subtractor_output.cc b/modules/audio_processing/aec3/subtractor_output.cc index 7a5bd17ef91..811014c90cf 100644 --- a/modules/audio_processing/aec3/subtractor_output.cc +++ b/modules/audio_processing/aec3/subtractor_output.cc @@ -12,8 +12,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -36,7 +35,7 @@ void SubtractorOutput::Reset() { y2 = 0.f; } -void SubtractorOutput::ComputeMetrics(ArrayView y) { +void SubtractorOutput::ComputeMetrics(std::span y) { const auto sum_of_squares = [](float a, float b) { return a + b * b; }; y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares); e2_refined = diff --git a/modules/audio_processing/aec3/subtractor_output.h b/modules/audio_processing/aec3/subtractor_output.h index 10349f59372..6fb1d0d8be9 100644 --- a/modules/audio_processing/aec3/subtractor_output.h +++ b/modules/audio_processing/aec3/subtractor_output.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/fft_data.h" @@ -44,7 +44,7 @@ struct SubtractorOutput { void Reset(); // Updates the powers of the signals. - void ComputeMetrics(ArrayView y); + void ComputeMetrics(std::span y); }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/subtractor_output_analyzer.cc b/modules/audio_processing/aec3/subtractor_output_analyzer.cc index f0cb67278f9..52d806c8375 100644 --- a/modules/audio_processing/aec3/subtractor_output_analyzer.cc +++ b/modules/audio_processing/aec3/subtractor_output_analyzer.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/subtractor_output.h" #include "rtc_base/checks.h" @@ -24,7 +24,7 @@ SubtractorOutputAnalyzer::SubtractorOutputAnalyzer(size_t num_capture_channels) : filters_converged_(num_capture_channels, false) {} void SubtractorOutputAnalyzer::Update( - ArrayView subtractor_output, + std::span subtractor_output, bool* any_filter_converged, bool* any_coarse_filter_converged, bool* all_filters_diverged) { diff --git a/modules/audio_processing/aec3/subtractor_output_analyzer.h b/modules/audio_processing/aec3/subtractor_output_analyzer.h index 760ffe63b04..9559dab2725 100644 --- a/modules/audio_processing/aec3/subtractor_output_analyzer.h +++ b/modules/audio_processing/aec3/subtractor_output_analyzer.h @@ -12,9 +12,9 @@ #define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_ #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/subtractor_output.h" namespace webrtc { @@ -26,7 +26,7 @@ class SubtractorOutputAnalyzer { ~SubtractorOutputAnalyzer() = default; // Analyses the subtractor output. - void Update(ArrayView subtractor_output, + void Update(std::span subtractor_output, bool* any_filter_converged, bool* any_coarse_filter_converged, bool* all_filters_diverged); diff --git a/modules/audio_processing/aec3/subtractor_unittest.cc b/modules/audio_processing/aec3/subtractor_unittest.cc index 9d2be96e524..5d30146e940 100644 --- a/modules/audio_processing/aec3/subtractor_unittest.cc +++ b/modules/audio_processing/aec3/subtractor_unittest.cc @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -106,14 +106,14 @@ std::vector RunSubtractorTest( num_render_channels); for (size_t ch = 0; ch < num_render_channels; ++ch) { x_hp_filter[ch] = std::make_unique( - ArrayView( + std::span( kHighPassFilterCoefficients)); } std::vector> y_hp_filter( num_capture_channels); for (size_t ch = 0; ch < num_capture_channels; ++ch) { y_hp_filter[ch] = std::make_unique( - ArrayView( + std::span( kHighPassFilterCoefficients)); } @@ -130,7 +130,7 @@ std::vector RunSubtractorTest( } else { for (size_t capture_ch = 0; capture_ch < num_capture_channels; ++capture_ch) { - ArrayView y_view = y.View(/*band=*/0, capture_ch); + std::span y_view = y.View(/*band=*/0, capture_ch); for (size_t render_ch = 0; render_ch < num_render_channels; ++render_ch) { std::array y_channel; diff --git a/modules/audio_processing/aec3/suppression_filter.cc b/modules/audio_processing/aec3/suppression_filter.cc index fa44591b131..bd895e8c5a5 100644 --- a/modules/audio_processing/aec3/suppression_filter.cc +++ b/modules/audio_processing/aec3/suppression_filter.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/block.h" #include "modules/audio_processing/aec3/fft_data.h" @@ -86,11 +86,11 @@ SuppressionFilter::SuppressionFilter(Aec3Optimization optimization, SuppressionFilter::~SuppressionFilter() = default; void SuppressionFilter::ApplyGain( - ArrayView comfort_noise, - ArrayView comfort_noise_high_band, + std::span comfort_noise, + std::span comfort_noise_high_band, const std::array& suppression_gain, float high_bands_gain, - ArrayView E_lowest_band, + std::span E_lowest_band, Block* e) { RTC_DCHECK(e); RTC_DCHECK_EQ(e->NumBands(), NumBandsForRate(sample_rate_hz_)); @@ -100,7 +100,7 @@ void SuppressionFilter::ApplyGain( for (size_t i = 0; i < kFftLengthBy2Plus1; ++i) { noise_gain[i] = 1.f - suppression_gain[i] * suppression_gain[i]; } - aec3::VectorMath(optimization_).Sqrt(noise_gain); + VectorMath(optimization_).Sqrt(noise_gain); const float high_bands_noise_scaling = 0.4f * std::sqrt(1.f - high_bands_gain * high_bands_gain); diff --git a/modules/audio_processing/aec3/suppression_filter.h b/modules/audio_processing/aec3/suppression_filter.h index 4a6de28eba8..a37d783551a 100644 --- a/modules/audio_processing/aec3/suppression_filter.h +++ b/modules/audio_processing/aec3/suppression_filter.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_fft.h" #include "modules/audio_processing/aec3/block.h" @@ -33,11 +33,11 @@ class SuppressionFilter { SuppressionFilter(const SuppressionFilter&) = delete; SuppressionFilter& operator=(const SuppressionFilter&) = delete; - void ApplyGain(ArrayView comfort_noise, - ArrayView comfort_noise_high_bands, + void ApplyGain(std::span comfort_noise, + std::span comfort_noise_high_bands, const std::array& suppression_gain, float high_bands_gain, - ArrayView E_lowest_band, + std::span E_lowest_band, Block* e); private: diff --git a/modules/audio_processing/aec3/suppression_gain.cc b/modules/audio_processing/aec3/suppression_gain.cc index 0ba04aec9ff..c5ca8ff2338 100644 --- a/modules/audio_processing/aec3/suppression_gain.cc +++ b/modules/audio_processing/aec3/suppression_gain.cc @@ -18,14 +18,14 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/block.h" #include "modules/audio_processing/aec3/dominant_nearend_detector.h" -#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/moving_average_spectrum.h" #include "modules/audio_processing/aec3/render_signal_analyzer.h" #include "modules/audio_processing/aec3/subband_nearend_detector.h" #include "modules/audio_processing/aec3/vector_math.h" @@ -41,14 +41,16 @@ void LimitLowFrequencyGains(std::array* gain) { (*gain)[0] = (*gain)[1] = std::min((*gain)[1], (*gain)[2]); } -void LimitHighFrequencyGains(const EchoCanceller3Config::Suppressor& config, - std::array* gain) { +void LimitHighFrequencyGains( + const EchoCanceller3Config::Suppressor::HighFrequencySuppression& + high_frequency_suppression, + bool conservative_hf_suppression, + std::array* gain) { // Limit the high frequency gains to avoid echo leakage due to an imperfect // filter. - const int limiting_gain_band = - config.high_frequency_suppression.limiting_gain_band; + const int limiting_gain_band = high_frequency_suppression.limiting_gain_band; const int bands_in_limiting_gain = - config.high_frequency_suppression.bands_in_limiting_gain; + high_frequency_suppression.bands_in_limiting_gain; if (bands_in_limiting_gain > 0) { RTC_DCHECK_GE(limiting_gain_band, 0); RTC_DCHECK_LE(limiting_gain_band + bands_in_limiting_gain, gain->size()); @@ -63,7 +65,7 @@ void LimitHighFrequencyGains(const EchoCanceller3Config::Suppressor& config, } (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2Minus1]; - if (config.conservative_hf_suppression) { + if (conservative_hf_suppression) { // Limits the gain in the frequencies for which the adaptive filter has not // converged. // TODO(peah): Make adaptive to take the actual filter error into account. @@ -83,14 +85,15 @@ void LimitHighFrequencyGains(const EchoCanceller3Config::Suppressor& config, } // Scales the echo according to assessed audibility at the other end. -void WeightEchoForAudibility(const EchoCanceller3Config& config, - ArrayView echo, - ArrayView weighted_echo) { +void WeightEchoForAudibility( + const EchoCanceller3Config::EchoAudibility& echo_audibility, + std::span echo, + std::span weighted_echo) { RTC_DCHECK_EQ(kFftLengthBy2Plus1, echo.size()); RTC_DCHECK_EQ(kFftLengthBy2Plus1, weighted_echo.size()); auto weigh = [](float threshold, float normalizer, size_t begin, size_t end, - ArrayView echo, ArrayView weighted_echo) { + std::span echo, std::span weighted_echo) { for (size_t k = begin; k < end; ++k) { if (echo[k] < threshold) { float tmp = (threshold - echo[k]) * normalizer; @@ -101,19 +104,19 @@ void WeightEchoForAudibility(const EchoCanceller3Config& config, } }; - float threshold = config.echo_audibility.floor_power * - config.echo_audibility.audibility_threshold_lf; - float normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + float threshold = + echo_audibility.floor_power * echo_audibility.audibility_threshold_lf; + float normalizer = 1.f / (threshold - echo_audibility.floor_power); weigh(threshold, normalizer, 0, 3, echo, weighted_echo); - threshold = config.echo_audibility.floor_power * - config.echo_audibility.audibility_threshold_mf; - normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + threshold = + echo_audibility.floor_power * echo_audibility.audibility_threshold_mf; + normalizer = 1.f / (threshold - echo_audibility.floor_power); weigh(threshold, normalizer, 3, 7, echo, weighted_echo); - threshold = config.echo_audibility.floor_power * - config.echo_audibility.audibility_threshold_hf; - normalizer = 1.f / (threshold - config.echo_audibility.floor_power); + threshold = + echo_audibility.floor_power * echo_audibility.audibility_threshold_hf; + normalizer = 1.f / (threshold - echo_audibility.floor_power); weigh(threshold, normalizer, 7, kFftLengthBy2Plus1, echo, weighted_echo); } @@ -122,8 +125,9 @@ void WeightEchoForAudibility(const EchoCanceller3Config& config, std::atomic SuppressionGain::instance_count_(0); float SuppressionGain::UpperBandsGain( - ArrayView> echo_spectrum, - ArrayView> + const EchoCanceller3Config::Suppressor& suppressor_config, + std::span> echo_spectrum, + std::span> comfort_noise_spectrum, const std::optional& narrow_peak_band, bool saturated_echo, @@ -172,7 +176,7 @@ float SuppressionGain::UpperBandsGain( // upper bands. float anti_howling_gain; const float activation_threshold = - kBlockSize * config_.suppressor.high_bands_suppression + kBlockSize * suppressor_config.high_bands_suppression .anti_howling_activation_threshold; if (high_band_energy < std::max(low_band_energy, activation_threshold)) { anti_howling_gain = 1.f; @@ -181,15 +185,15 @@ float SuppressionGain::UpperBandsGain( RTC_DCHECK_LE(low_band_energy, high_band_energy); RTC_DCHECK_NE(0.f, high_band_energy); anti_howling_gain = - config_.suppressor.high_bands_suppression.anti_howling_gain * + suppressor_config.high_bands_suppression.anti_howling_gain * sqrtf(low_band_energy / high_band_energy); } float gain_bound = 1.f; if (!dominant_nearend_detector_->IsNearendState()) { // Bound the upper gain during significant echo activity. - const auto& cfg = config_.suppressor.high_bands_suppression; - auto low_frequency_energy = [](ArrayView spectrum) { + const auto& cfg = suppressor_config.high_bands_suppression; + auto low_frequency_energy = [](std::span spectrum) { RTC_DCHECK_LE(16, spectrum.size()); return std::accumulate(spectrum.begin() + 1, spectrum.begin() + 16, 0.f); }; @@ -230,16 +234,18 @@ void SuppressionGain::GainToNoAudibleEcho( // Compute the minimum gain as the attenuating gain to put the signal just // above the zero sample values. -void SuppressionGain::GetMinGain(ArrayView weighted_residual_echo, - ArrayView last_nearend, - ArrayView last_echo, - bool low_noise_render, - bool saturated_echo, - ArrayView min_gain) const { +void SuppressionGain::GetMinGain( + const EchoCanceller3Config::Suppressor& suppressor_config, + std::span weighted_residual_echo, + std::span last_nearend, + std::span last_echo, + bool low_noise_render, + bool saturated_echo, + std::span min_gain) const { if (!saturated_echo) { const float min_echo_power = - low_noise_render ? config_.echo_audibility.low_render_limit - : config_.echo_audibility.normal_render_limit; + low_noise_render ? echo_audibility_config_.low_render_limit + : echo_audibility_config_.normal_render_limit; for (size_t k = 0; k < min_gain.size(); ++k) { min_gain[k] = weighted_residual_echo[k] > 0.f @@ -249,16 +255,16 @@ void SuppressionGain::GetMinGain(ArrayView weighted_residual_echo, } if (!initial_state_ || - config_.suppressor.lf_smoothing_during_initial_phase) { + suppressor_config.lf_smoothing_during_initial_phase) { const float& dec = dominant_nearend_detector_->IsNearendState() ? nearend_params_.max_dec_factor_lf : normal_params_.max_dec_factor_lf; - for (int k = 0; k <= config_.suppressor.last_lf_smoothing_band; ++k) { + for (int k = 0; k <= suppressor_config.last_lf_smoothing_band; ++k) { // Make sure the gains of the low frequencies do not decrease too // quickly after strong nearend. if (last_nearend[k] > last_echo[k] || - k <= config_.suppressor.last_permanent_lf_smoothing_band) { + k <= suppressor_config.last_permanent_lf_smoothing_band) { min_gain[k] = std::max(min_gain[k], last_gain_[k] * dec); min_gain[k] = std::min(min_gain[k], 1.f); } @@ -271,28 +277,30 @@ void SuppressionGain::GetMinGain(ArrayView weighted_residual_echo, // Compute the maximum gain by limiting the gain increase from the previous // gain. -void SuppressionGain::GetMaxGain(ArrayView max_gain) const { +void SuppressionGain::GetMaxGain(float floor_first_increase, + std::span max_gain) const { const auto& inc = dominant_nearend_detector_->IsNearendState() ? nearend_params_.max_inc_factor : normal_params_.max_inc_factor; - const auto& floor = config_.suppressor.floor_first_increase; for (size_t k = 0; k < max_gain.size(); ++k) { - max_gain[k] = std::min(std::max(last_gain_[k] * inc, floor), 1.f); + max_gain[k] = + std::min(std::max(last_gain_[k] * inc, floor_first_increase), 1.f); } } void SuppressionGain::LowerBandGain( + const EchoCanceller3Config::Suppressor& suppressor_config, bool low_noise_render, const AecState& aec_state, - ArrayView> suppressor_input, - ArrayView> residual_echo, - ArrayView> comfort_noise, + std::span> suppressor_input, + std::span> residual_echo, + std::span> comfort_noise, bool clock_drift, std::array* gain) { gain->fill(1.f); const bool saturated_echo = aec_state.SaturatedEcho(); std::array max_gain; - GetMaxGain(max_gain); + GetMaxGain(suppressor_config.floor_first_increase, max_gain); for (size_t ch = 0; ch < num_capture_channels_; ++ch) { std::array G; @@ -301,11 +309,12 @@ void SuppressionGain::LowerBandGain( // Weight echo power in terms of audibility. std::array weighted_residual_echo; - WeightEchoForAudibility(config_, residual_echo[ch], weighted_residual_echo); + WeightEchoForAudibility(echo_audibility_config_, residual_echo[ch], + weighted_residual_echo); std::array min_gain; - GetMinGain(weighted_residual_echo, last_nearend_[ch], last_echo_[ch], - low_noise_render, saturated_echo, min_gain); + GetMinGain(suppressor_config, weighted_residual_echo, last_nearend_[ch], + last_echo_[ch], low_noise_render, saturated_echo, min_gain); GainToNoAudibleEcho(nearend, weighted_residual_echo, comfort_noise[0], &G); @@ -325,15 +334,17 @@ void SuppressionGain::LowerBandGain( // Use conservative high-frequency gains during clock-drift or when not in // dominant nearend. if (!dominant_nearend_detector_->IsNearendState() || clock_drift || - config_.suppressor.conservative_hf_suppression) { - LimitHighFrequencyGains(config_.suppressor, gain); + suppressor_config.conservative_hf_suppression) { + LimitHighFrequencyGains(suppressor_config.high_frequency_suppression, + suppressor_config.conservative_hf_suppression, + gain); } // Store computed gains. std::copy(gain->begin(), gain->end(), last_gain_.begin()); // Transform gains to amplitude domain. - aec3::VectorMath(optimization_).Sqrt(*gain); + VectorMath(optimization_).Sqrt(*gain); } SuppressionGain::SuppressionGain(const EchoCanceller3Config& config, @@ -342,32 +353,29 @@ SuppressionGain::SuppressionGain(const EchoCanceller3Config& config, size_t num_capture_channels) : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), optimization_(optimization), - config_(config), num_capture_channels_(num_capture_channels), - state_change_duration_blocks_( - static_cast(config_.filter.config_change_duration_blocks)), + echo_audibility_config_(config.echo_audibility), + use_subband_nearend_detection_( + config.suppressor.use_subband_nearend_detection), last_nearend_(num_capture_channels_, {0}), last_echo_(num_capture_channels_, {0}), nearend_smoothers_( num_capture_channels_, - aec3::MovingAverage(kFftLengthBy2Plus1, - config.suppressor.nearend_average_blocks)), - nearend_params_(config_.suppressor.last_lf_band, - config_.suppressor.first_hf_band, - config_.suppressor.nearend_tuning), - normal_params_(config_.suppressor.last_lf_band, - config_.suppressor.first_hf_band, - config_.suppressor.normal_tuning), - use_unbounded_echo_spectrum_(config.suppressor.dominant_nearend_detection - .use_unbounded_echo_spectrum) { - RTC_DCHECK_LT(0, state_change_duration_blocks_); + MovingAverageSpectrum(kFftLengthBy2Plus1, + config.suppressor.nearend_average_blocks)), + nearend_params_(config.suppressor.last_lf_band, + config.suppressor.first_hf_band, + config.suppressor.nearend_tuning), + normal_params_(config.suppressor.last_lf_band, + config.suppressor.first_hf_band, + config.suppressor.normal_tuning) { last_gain_.fill(1.f); - if (config_.suppressor.use_subband_nearend_detection) { + if (config.suppressor.use_subband_nearend_detection) { dominant_nearend_detector_ = std::make_unique( - config_.suppressor.subband_nearend_detection, num_capture_channels_); + config.suppressor.subband_nearend_detection, num_capture_channels_); } else { dominant_nearend_detector_ = std::make_unique( - config_.suppressor.dominant_nearend_detection, num_capture_channels_); + config.suppressor.dominant_nearend_detection, num_capture_channels_); } RTC_DCHECK(dominant_nearend_detector_); } @@ -375,13 +383,15 @@ SuppressionGain::SuppressionGain(const EchoCanceller3Config& config, SuppressionGain::~SuppressionGain() = default; void SuppressionGain::GetGain( - ArrayView> nearend_spectrum, - ArrayView> echo_spectrum, - ArrayView> + const EchoCanceller3Config::Suppressor& suppressor_config, + bool config_changed, + std::span> nearend_spectrum, + std::span> echo_spectrum, + std::span> residual_echo_spectrum, - ArrayView> + std::span> residual_echo_spectrum_unbounded, - ArrayView> + std::span> comfort_noise_spectrum, const RenderSignalAnalyzer& render_signal_analyzer, const AecState& aec_state, @@ -392,10 +402,15 @@ void SuppressionGain::GetGain( RTC_DCHECK(high_bands_gain); RTC_DCHECK(low_band_gain); + if (config_changed) { + UpdateStateDependingOnConfig(suppressor_config); + } + // Choose residual echo spectrum for dominant nearend detection. - const auto echo = use_unbounded_echo_spectrum_ - ? residual_echo_spectrum_unbounded - : residual_echo_spectrum; + const auto echo = + suppressor_config.dominant_nearend_detection.use_unbounded_echo_spectrum + ? residual_echo_spectrum_unbounded + : residual_echo_spectrum; // Update the nearend state selection. dominant_nearend_detector_->Update(nearend_spectrum, echo, @@ -403,17 +418,17 @@ void SuppressionGain::GetGain( // Compute gain for the lower band. bool low_noise_render = low_render_detector_.Detect(render); - LowerBandGain(low_noise_render, aec_state, nearend_spectrum, - residual_echo_spectrum, comfort_noise_spectrum, clock_drift, - low_band_gain); + LowerBandGain(suppressor_config, low_noise_render, aec_state, + nearend_spectrum, residual_echo_spectrum, + comfort_noise_spectrum, clock_drift, low_band_gain); // Compute the gain for the upper bands. const std::optional narrow_peak_band = render_signal_analyzer.NarrowPeakBand(); - *high_bands_gain = - UpperBandsGain(echo_spectrum, comfort_noise_spectrum, narrow_peak_band, - aec_state.SaturatedEcho(), render, *low_band_gain); + *high_bands_gain = UpperBandsGain( + suppressor_config, echo_spectrum, comfort_noise_spectrum, + narrow_peak_band, aec_state.SaturatedEcho(), render, *low_band_gain); data_dumper_->DumpRaw("aec3_dominant_nearend", dominant_nearend_detector_->IsNearendState()); @@ -421,11 +436,24 @@ void SuppressionGain::GetGain( void SuppressionGain::SetInitialState(bool state) { initial_state_ = state; - if (state) { - initial_state_change_counter_ = state_change_duration_blocks_; - } else { - initial_state_change_counter_ = 0; +} + +void SuppressionGain::UpdateStateDependingOnConfig( + const EchoCanceller3Config::Suppressor& suppressor_config) { + RTC_DCHECK_EQ(suppressor_config.use_subband_nearend_detection, + use_subband_nearend_detection_); + // Update nearend average blocks. + for (auto& smoother : nearend_smoothers_) { + smoother.UpdateMemoryLength(suppressor_config.nearend_average_blocks); } + dominant_nearend_detector_->SetConfig(suppressor_config); + nearend_params_.SetConfig(suppressor_config.last_lf_band, + suppressor_config.first_hf_band, + suppressor_config.nearend_tuning); + + normal_params_.SetConfig(suppressor_config.last_lf_band, + suppressor_config.first_hf_band, + suppressor_config.normal_tuning); } // Detects when the render signal can be considered to have low power and @@ -452,9 +480,16 @@ bool SuppressionGain::LowNoiseRenderDetector::Detect(const Block& render) { SuppressionGain::GainParameters::GainParameters( int last_lf_band, int first_hf_band, - const EchoCanceller3Config::Suppressor::Tuning& tuning) - : max_inc_factor(tuning.max_inc_factor), - max_dec_factor_lf(tuning.max_dec_factor_lf) { + const EchoCanceller3Config::Suppressor::Tuning& tuning) { + SetConfig(last_lf_band, first_hf_band, tuning); +} + +void SuppressionGain::GainParameters::SetConfig( + int last_lf_band, + int first_hf_band, + const EchoCanceller3Config::Suppressor::Tuning& tuning) { + max_inc_factor = tuning.max_inc_factor; + max_dec_factor_lf = tuning.max_dec_factor_lf; // Compute per-band masking thresholds. RTC_DCHECK_LT(last_lf_band, first_hf_band); auto& lf = tuning.mask_lf; diff --git a/modules/audio_processing/aec3/suppression_gain.h b/modules/audio_processing/aec3/suppression_gain.h index b0a56d04ad1..6d0920774c0 100644 --- a/modules/audio_processing/aec3/suppression_gain.h +++ b/modules/audio_processing/aec3/suppression_gain.h @@ -16,17 +16,18 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/block.h" -#include "modules/audio_processing/aec3/moving_average.h" +#include "modules/audio_processing/aec3/moving_average_spectrum.h" #include "modules/audio_processing/aec3/nearend_detector.h" #include "modules/audio_processing/aec3/render_signal_analyzer.h" #include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/gtest_prod_util.h" namespace webrtc { @@ -42,13 +43,15 @@ class SuppressionGain { SuppressionGain& operator=(const SuppressionGain&) = delete; void GetGain( - ArrayView> nearend_spectrum, - ArrayView> echo_spectrum, - ArrayView> + const EchoCanceller3Config::Suppressor& suppressor_config, + bool config_changed, + std::span> nearend_spectrum, + std::span> echo_spectrum, + std::span> residual_echo_spectrum, - ArrayView> + std::span> residual_echo_spectrum_unbounded, - ArrayView> + std::span> comfort_noise_spectrum, const RenderSignalAnalyzer& render_signal_analyzer, const AecState& aec_state, @@ -65,10 +68,17 @@ class SuppressionGain { void SetInitialState(bool state); private: + FRIEND_TEST_ALL_PREFIXES(SuppressionGainTest, UpdateStateDependingOnConfig); + + // Updates the internal state, e.g. sizes and parameters, if the config + // changes. + void UpdateStateDependingOnConfig( + const EchoCanceller3Config::Suppressor& suppressor_config); // Computes the gain to apply for the bands beyond the first band. float UpperBandsGain( - ArrayView> echo_spectrum, - ArrayView> + const EchoCanceller3Config::Suppressor& suppressor_config, + std::span> echo_spectrum, + std::span> comfort_noise_spectrum, const std::optional& narrow_peak_band, bool saturated_echo, @@ -81,22 +91,24 @@ class SuppressionGain { std::array* gain) const; void LowerBandGain( + const EchoCanceller3Config::Suppressor& suppressor_config, bool stationary_with_low_power, const AecState& aec_state, - ArrayView> suppressor_input, - ArrayView> residual_echo, - ArrayView> comfort_noise, + std::span> suppressor_input, + std::span> residual_echo, + std::span> comfort_noise, bool clock_drift, std::array* gain); - void GetMinGain(ArrayView weighted_residual_echo, - ArrayView last_nearend, - ArrayView last_echo, + void GetMinGain(const EchoCanceller3Config::Suppressor& suppressor_config, + std::span weighted_residual_echo, + std::span last_nearend, + std::span last_echo, bool low_noise_render, bool saturated_echo, - ArrayView min_gain) const; + std::span min_gain) const; - void GetMaxGain(ArrayView max_gain) const; + void GetMaxGain(float floor_first_increase, std::span max_gain) const; class LowNoiseRenderDetector { public: @@ -111,8 +123,11 @@ class SuppressionGain { int last_lf_band, int first_hf_band, const EchoCanceller3Config::Suppressor::Tuning& tuning); - const float max_inc_factor; - const float max_dec_factor_lf; + void SetConfig(int last_lf_band, + int first_hf_band, + const EchoCanceller3Config::Suppressor::Tuning& tuning); + float max_inc_factor; + float max_dec_factor_lf; std::array enr_transparent_; std::array enr_suppress_; std::array emr_transparent_; @@ -121,21 +136,17 @@ class SuppressionGain { static std::atomic instance_count_; std::unique_ptr data_dumper_; const Aec3Optimization optimization_; - const EchoCanceller3Config config_; const size_t num_capture_channels_; - const int state_change_duration_blocks_; + const EchoCanceller3Config::EchoAudibility echo_audibility_config_; + const bool use_subband_nearend_detection_; std::array last_gain_; std::vector> last_nearend_; std::vector> last_echo_; LowNoiseRenderDetector low_render_detector_; bool initial_state_ = true; - int initial_state_change_counter_ = 0; - std::vector nearend_smoothers_; - const GainParameters nearend_params_; - const GainParameters normal_params_; - // Determines if the dominant nearend detector uses the unbounded residual - // echo spectrum. - const bool use_unbounded_echo_spectrum_; + std::vector nearend_smoothers_; + GainParameters nearend_params_; + GainParameters normal_params_; std::unique_ptr dominant_nearend_detector_; }; diff --git a/modules/audio_processing/aec3/suppression_gain_unittest.cc b/modules/audio_processing/aec3/suppression_gain_unittest.cc index 2da9589261b..0bd36a13ae6 100644 --- a/modules/audio_processing/aec3/suppression_gain_unittest.cc +++ b/modules/audio_processing/aec3/suppression_gain_unittest.cc @@ -34,7 +34,6 @@ #include "test/gtest.h" namespace webrtc { -namespace aec3 { #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) @@ -59,23 +58,26 @@ TEST(SuppressionGainDeathTest, NullOutputGains) { AecState aec_state(CreateEnvironment(), EchoCanceller3Config{}, 1); EXPECT_DEATH( SuppressionGain(EchoCanceller3Config{}, DetectOptimization(), 16000, 1) - .GetGain(E2, S2, R2, R2_unbounded, N2, + .GetGain(EchoCanceller3Config{}.suppressor, /*config_changed=*/false, + E2, S2, R2, R2_unbounded, N2, RenderSignalAnalyzer((EchoCanceller3Config{})), aec_state, - Block(3, 1), false, &high_bands_gain, nullptr), + Block(3, 1), /*clock_drift=*/false, &high_bands_gain, + nullptr), ""); } #endif // Does a sanity check that the gains are correctly computed. -TEST(SuppressionGain, BasicGainComputation) { +TEST(SuppressionGainTest, BasicGainComputation) { constexpr size_t kNumRenderChannels = 1; constexpr size_t kNumCaptureChannels = 2; constexpr int kSampleRateHz = 16000; constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz); - SuppressionGain suppression_gain(EchoCanceller3Config(), DetectOptimization(), - kSampleRateHz, kNumCaptureChannels); - RenderSignalAnalyzer analyzer(EchoCanceller3Config{}); + EchoCanceller3Config config; + SuppressionGain suppression_gain(config, DetectOptimization(), kSampleRateHz, + kNumCaptureChannels); + RenderSignalAnalyzer analyzer(config); float high_bands_gain; std::vector> E2(kNumCaptureChannels); std::vector> S2(kNumCaptureChannels, @@ -89,7 +91,6 @@ TEST(SuppressionGain, BasicGainComputation) { std::vector output(kNumCaptureChannels); Block x(kNumBands, kNumRenderChannels); const Environment env = CreateEnvironment(); - EchoCanceller3Config config; AecState aec_state(env, config, kNumCaptureChannels); ApmDataDumper data_dumper(42); Subtractor subtractor(env, config, kNumRenderChannels, kNumCaptureChannels, @@ -121,8 +122,9 @@ TEST(SuppressionGain, BasicGainComputation) { aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(), subtractor.FilterImpulseResponses(), *render_delay_buffer->GetRenderBuffer(), E2, Y2, output); - suppression_gain.GetGain(E2, S2, R2, R2_unbounded, N2, analyzer, aec_state, - x, false, &high_bands_gain, &g); + suppression_gain.GetGain(config.suppressor, /*config_changed=*/false, E2, + S2, R2, R2_unbounded, N2, analyzer, aec_state, x, + /*clock_drift=*/false, &high_bands_gain, &g); } std::for_each(g.begin(), g.end(), [](float a) { EXPECT_NEAR(1.0f, a, 0.001f); }); @@ -141,8 +143,9 @@ TEST(SuppressionGain, BasicGainComputation) { aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(), subtractor.FilterImpulseResponses(), *render_delay_buffer->GetRenderBuffer(), E2, Y2, output); - suppression_gain.GetGain(E2, S2, R2, R2_unbounded, N2, analyzer, aec_state, - x, false, &high_bands_gain, &g); + suppression_gain.GetGain(config.suppressor, /*config_changed=*/false, E2, + S2, R2, R2_unbounded, N2, analyzer, aec_state, x, + /*clock_drift=*/false, &high_bands_gain, &g); } std::for_each(g.begin(), g.end(), [](float a) { EXPECT_NEAR(1.0f, a, 0.001f); }); @@ -153,12 +156,39 @@ TEST(SuppressionGain, BasicGainComputation) { R2_unbounded[1].fill(10000000000000.0f); for (int k = 0; k < 10; ++k) { - suppression_gain.GetGain(E2, S2, R2, R2_unbounded, N2, analyzer, aec_state, - x, false, &high_bands_gain, &g); + suppression_gain.GetGain(config.suppressor, /*config_changed=*/false, E2, + S2, R2, R2_unbounded, N2, analyzer, aec_state, x, + /*clock_drift=*/false, &high_bands_gain, &g); } std::for_each(g.begin(), g.end(), [](float a) { EXPECT_NEAR(0.0f, a, 0.001f); }); } -} // namespace aec3 +TEST(SuppressionGainTest, UpdateStateDependingOnConfig) { + constexpr size_t kNumCaptureChannels = 1; + constexpr int kSampleRateHz = 16000; + EchoCanceller3Config config; + config.suppressor.nearend_tuning.max_inc_factor = 2.0f; + config.suppressor.normal_tuning.max_dec_factor_lf = 0.2f; + + SuppressionGain suppression_gain(config, DetectOptimization(), kSampleRateHz, + kNumCaptureChannels); + + // Initial call to set up the state. + suppression_gain.UpdateStateDependingOnConfig(config.suppressor); + + EXPECT_EQ(suppression_gain.nearend_params_.max_inc_factor, 2.0f); + EXPECT_EQ(suppression_gain.normal_params_.max_dec_factor_lf, 0.2f); + + // Change config and verify state is updated. + EchoCanceller3Config new_config = config; + new_config.suppressor.nearend_tuning.max_inc_factor = 3.0f; + new_config.suppressor.normal_tuning.max_dec_factor_lf = 0.3f; + + suppression_gain.UpdateStateDependingOnConfig(new_config.suppressor); + + EXPECT_EQ(suppression_gain.nearend_params_.max_inc_factor, 3.0f); + EXPECT_EQ(suppression_gain.normal_params_.max_dec_factor_lf, 0.3f); +} + } // namespace webrtc diff --git a/modules/audio_processing/aec3/vector_math.h b/modules/audio_processing/aec3/vector_math.h index 9fd7792d3f5..c733aa02281 100644 --- a/modules/audio_processing/aec3/vector_math.h +++ b/modules/audio_processing/aec3/vector_math.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "rtc_base/checks.h" @@ -29,7 +29,6 @@ #endif namespace webrtc { -namespace aec3 { // Provides optimizations for mathematical operations based on vectors. class VectorMath { @@ -38,8 +37,8 @@ class VectorMath { : optimization_(optimization) {} // Elementwise square root. - void SqrtAVX2(ArrayView x); - void Sqrt(ArrayView x) { + void SqrtAVX2(std::span x); + void Sqrt(std::span x) { switch (optimization_) { #if defined(WEBRTC_ARCH_X86_FAMILY) case Aec3Optimization::kSse2: { @@ -112,12 +111,12 @@ class VectorMath { } // Elementwise vector multiplication z = x * y. - void MultiplyAVX2(ArrayView x, - ArrayView y, - ArrayView z); - void Multiply(ArrayView x, - ArrayView y, - ArrayView z) { + void MultiplyAVX2(std::span x, + std::span y, + std::span z); + void Multiply(std::span x, + std::span y, + std::span z) { RTC_DCHECK_EQ(z.size(), x.size()); RTC_DCHECK_EQ(z.size(), y.size()); switch (optimization_) { @@ -167,8 +166,8 @@ class VectorMath { } // Elementwise vector accumulation z += x. - void AccumulateAVX2(ArrayView x, ArrayView z); - void Accumulate(ArrayView x, ArrayView z) { + void AccumulateAVX2(std::span x, std::span z); + void Accumulate(std::span x, std::span z) { RTC_DCHECK_EQ(z.size(), x.size()); switch (optimization_) { #if defined(WEBRTC_ARCH_X86_FAMILY) @@ -220,8 +219,6 @@ class VectorMath { Aec3Optimization optimization_; }; -} // namespace aec3 - } // namespace webrtc #endif // MODULES_AUDIO_PROCESSING_AEC3_VECTOR_MATH_H_ diff --git a/modules/audio_processing/aec3/vector_math_avx2.cc b/modules/audio_processing/aec3/vector_math_avx2.cc index da4dd6e5159..ec251048730 100644 --- a/modules/audio_processing/aec3/vector_math_avx2.cc +++ b/modules/audio_processing/aec3/vector_math_avx2.cc @@ -11,16 +11,15 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/aec3/vector_math.h" #include "rtc_base/checks.h" namespace webrtc { -namespace aec3 { // Elementwise square root. -void VectorMath::SqrtAVX2(ArrayView x) { +void VectorMath::SqrtAVX2(std::span x) { const int x_size = static_cast(x.size()); const int vector_limit = x_size >> 3; @@ -37,9 +36,9 @@ void VectorMath::SqrtAVX2(ArrayView x) { } // Elementwise vector multiplication z = x * y. -void VectorMath::MultiplyAVX2(ArrayView x, - ArrayView y, - ArrayView z) { +void VectorMath::MultiplyAVX2(std::span x, + std::span y, + std::span z) { RTC_DCHECK_EQ(z.size(), x.size()); RTC_DCHECK_EQ(z.size(), y.size()); const int x_size = static_cast(x.size()); @@ -59,7 +58,7 @@ void VectorMath::MultiplyAVX2(ArrayView x, } // Elementwise vector accumulation z += x. -void VectorMath::AccumulateAVX2(ArrayView x, ArrayView z) { +void VectorMath::AccumulateAVX2(std::span x, std::span z) { RTC_DCHECK_EQ(z.size(), x.size()); const int x_size = static_cast(x.size()); const int vector_limit = x_size >> 3; @@ -77,5 +76,4 @@ void VectorMath::AccumulateAVX2(ArrayView x, ArrayView z) { } } -} // namespace aec3 } // namespace webrtc diff --git a/modules/audio_processing/aec3/vector_math_unittest.cc b/modules/audio_processing/aec3/vector_math_unittest.cc index a47263b305d..1f3525b6ac4 100644 --- a/modules/audio_processing/aec3/vector_math_unittest.cc +++ b/modules/audio_processing/aec3/vector_math_unittest.cc @@ -34,9 +34,9 @@ TEST(VectorMath, Sqrt) { } std::copy(x.begin(), x.end(), z.begin()); - aec3::VectorMath(Aec3Optimization::kNone).Sqrt(z); + VectorMath(Aec3Optimization::kNone).Sqrt(z); std::copy(x.begin(), x.end(), z_neon.begin()); - aec3::VectorMath(Aec3Optimization::kNeon).Sqrt(z_neon); + VectorMath(Aec3Optimization::kNeon).Sqrt(z_neon); for (size_t k = 0; k < z.size(); ++k) { EXPECT_NEAR(z[k], z_neon[k], 0.0001f); EXPECT_NEAR(sqrtf(x[k]), z_neon[k], 0.0001f); @@ -54,8 +54,8 @@ TEST(VectorMath, Multiply) { y[k] = (2.f / 3.f) * k; } - aec3::VectorMath(Aec3Optimization::kNone).Multiply(x, y, z); - aec3::VectorMath(Aec3Optimization::kNeon).Multiply(x, y, z_neon); + VectorMath(Aec3Optimization::kNone).Multiply(x, y, z); + VectorMath(Aec3Optimization::kNeon).Multiply(x, y, z_neon); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_neon[k]); EXPECT_FLOAT_EQ(x[k] * y[k], z_neon[k]); @@ -72,8 +72,8 @@ TEST(VectorMath, Accumulate) { z[k] = z_neon[k] = 2.f * k; } - aec3::VectorMath(Aec3Optimization::kNone).Accumulate(x, z); - aec3::VectorMath(Aec3Optimization::kNeon).Accumulate(x, z_neon); + VectorMath(Aec3Optimization::kNone).Accumulate(x, z); + VectorMath(Aec3Optimization::kNeon).Accumulate(x, z_neon); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_neon[k]); EXPECT_FLOAT_EQ(x[k] + 2.f * x[k], z_neon[k]); @@ -94,9 +94,9 @@ TEST(VectorMath, Sse2Sqrt) { } std::copy(x.begin(), x.end(), z.begin()); - aec3::VectorMath(Aec3Optimization::kNone).Sqrt(z); + VectorMath(Aec3Optimization::kNone).Sqrt(z); std::copy(x.begin(), x.end(), z_sse2.begin()); - aec3::VectorMath(Aec3Optimization::kSse2).Sqrt(z_sse2); + VectorMath(Aec3Optimization::kSse2).Sqrt(z_sse2); EXPECT_EQ(z, z_sse2); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_sse2[k]); @@ -116,9 +116,9 @@ TEST(VectorMath, Avx2Sqrt) { } std::copy(x.begin(), x.end(), z.begin()); - aec3::VectorMath(Aec3Optimization::kNone).Sqrt(z); + VectorMath(Aec3Optimization::kNone).Sqrt(z); std::copy(x.begin(), x.end(), z_avx2.begin()); - aec3::VectorMath(Aec3Optimization::kAvx2).Sqrt(z_avx2); + VectorMath(Aec3Optimization::kAvx2).Sqrt(z_avx2); EXPECT_EQ(z, z_avx2); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_avx2[k]); @@ -139,8 +139,8 @@ TEST(VectorMath, Sse2Multiply) { y[k] = (2.f / 3.f) * k; } - aec3::VectorMath(Aec3Optimization::kNone).Multiply(x, y, z); - aec3::VectorMath(Aec3Optimization::kSse2).Multiply(x, y, z_sse2); + VectorMath(Aec3Optimization::kNone).Multiply(x, y, z); + VectorMath(Aec3Optimization::kSse2).Multiply(x, y, z_sse2); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_sse2[k]); EXPECT_FLOAT_EQ(x[k] * y[k], z_sse2[k]); @@ -160,8 +160,8 @@ TEST(VectorMath, Avx2Multiply) { y[k] = (2.f / 3.f) * k; } - aec3::VectorMath(Aec3Optimization::kNone).Multiply(x, y, z); - aec3::VectorMath(Aec3Optimization::kAvx2).Multiply(x, y, z_avx2); + VectorMath(Aec3Optimization::kNone).Multiply(x, y, z); + VectorMath(Aec3Optimization::kAvx2).Multiply(x, y, z_avx2); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_avx2[k]); EXPECT_FLOAT_EQ(x[k] * y[k], z_avx2[k]); @@ -180,8 +180,8 @@ TEST(VectorMath, Sse2Accumulate) { z[k] = z_sse2[k] = 2.f * k; } - aec3::VectorMath(Aec3Optimization::kNone).Accumulate(x, z); - aec3::VectorMath(Aec3Optimization::kSse2).Accumulate(x, z_sse2); + VectorMath(Aec3Optimization::kNone).Accumulate(x, z); + VectorMath(Aec3Optimization::kSse2).Accumulate(x, z_sse2); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_sse2[k]); EXPECT_FLOAT_EQ(x[k] + 2.f * x[k], z_sse2[k]); @@ -200,8 +200,8 @@ TEST(VectorMath, Avx2Accumulate) { z[k] = z_avx2[k] = 2.f * k; } - aec3::VectorMath(Aec3Optimization::kNone).Accumulate(x, z); - aec3::VectorMath(Aec3Optimization::kAvx2).Accumulate(x, z_avx2); + VectorMath(Aec3Optimization::kNone).Accumulate(x, z); + VectorMath(Aec3Optimization::kAvx2).Accumulate(x, z_avx2); for (size_t k = 0; k < z.size(); ++k) { EXPECT_FLOAT_EQ(z[k], z_avx2[k]); EXPECT_FLOAT_EQ(x[k] + 2.f * x[k], z_avx2[k]); diff --git a/modules/audio_processing/aec_dump/BUILD.gn b/modules/audio_processing/aec_dump/BUILD.gn index 227d6dec6bc..586a38ed796 100644 --- a/modules/audio_processing/aec_dump/BUILD.gn +++ b/modules/audio_processing/aec_dump/BUILD.gn @@ -33,8 +33,6 @@ if (rtc_include_tests) { deps = [ "..:aec_dump_interface", "..:audio_frame_view", - "..:audioproc_test_utils", - "../", "../../../api/audio:audio_frame_api", "../../../api/audio:audio_processing", "../../../test:test_support", @@ -48,8 +46,6 @@ if (rtc_include_tests) { deps = [ ":mock_aec_dump", - "..:audioproc_test_utils", - "../", "../../../api:scoped_refptr", "../../../api/audio:audio_processing", "../../../api/audio:builtin_audio_processing_builder", @@ -98,10 +94,7 @@ if (rtc_enable_protobuf) { defines = [] deps = [ ":aec_dump", - ":aec_dump_impl", "..:aec_dump_interface", - "..:audioproc_debug_proto", - "../", "../../../api/audio:audio_processing", "../../../rtc_base:task_queue_for_test", "../../../test:fileutils", diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc index 9d9608245f3..76de803b8fe 100644 --- a/modules/audio_processing/aec_dump/aec_dump_impl.cc +++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc @@ -45,10 +45,6 @@ void CopyFromConfigToEvent(const InternalAPMConfig& config, pb_cfg->set_aec_extended_filter_enabled(config.aec_extended_filter_enabled); pb_cfg->set_aec_suppression_level(config.aec_suppression_level); - pb_cfg->set_aecm_enabled(config.aecm_enabled); - pb_cfg->set_aecm_comfort_noise_enabled(config.aecm_comfort_noise_enabled); - pb_cfg->set_aecm_routing_mode(config.aecm_routing_mode); - pb_cfg->set_agc_enabled(config.agc_enabled); pb_cfg->set_agc_mode(config.agc_mode); pb_cfg->set_agc_limiter_enabled(config.agc_limiter_enabled); @@ -67,6 +63,7 @@ void CopyFromConfigToEvent(const InternalAPMConfig& config, config.pre_amplifier_fixed_gain_factor); pb_cfg->set_experiments_description(config.experiments_description); + pb_cfg->set_api_config_string(config.api_config_string); } } // namespace @@ -172,7 +169,7 @@ void AecDumpImpl::WriteRenderStreamMessage( for (int i = 0; i < src.num_channels(); ++i) { const auto& channel_view = src.channel(i); - msg->add_channel(channel_view.begin(), sizeof(float) * channel_view.size()); + msg->add_channel(channel_view.data(), sizeof(float) * channel_view.size()); } PostWriteToFileTask(std::move(event)); @@ -188,7 +185,7 @@ void AecDumpImpl::WriteRenderStreamMessage(const float* const* data, for (int i = 0; i < num_channels; ++i) { MonoView channel_view(data[i], samples_per_channel); - msg->add_channel(channel_view.begin(), sizeof(float) * channel_view.size()); + msg->add_channel(channel_view.data(), sizeof(float) * channel_view.size()); } PostWriteToFileTask(std::move(event)); diff --git a/modules/audio_processing/aec_dump/capture_stream_info.cc b/modules/audio_processing/aec_dump/capture_stream_info.cc index 993386658e3..47933212c83 100644 --- a/modules/audio_processing/aec_dump/capture_stream_info.cc +++ b/modules/audio_processing/aec_dump/capture_stream_info.cc @@ -27,7 +27,7 @@ void CaptureStreamInfo::AddInput(const AudioFrameView& src) { void CaptureStreamInfo::AddInputChannel(MonoView channel) { auto* stream = event_->mutable_stream(); - stream->add_input_channel(channel.begin(), sizeof(float) * channel.size()); + stream->add_input_channel(channel.data(), sizeof(float) * channel.size()); } void CaptureStreamInfo::AddOutput(const AudioFrameView& src) { @@ -38,7 +38,7 @@ void CaptureStreamInfo::AddOutput(const AudioFrameView& src) { void CaptureStreamInfo::AddOutputChannel(MonoView channel) { auto* stream = event_->mutable_stream(); - stream->add_output_channel(channel.begin(), sizeof(float) * channel.size()); + stream->add_output_channel(channel.data(), sizeof(float) * channel.size()); } void CaptureStreamInfo::AddInput(const int16_t* const data, diff --git a/modules/audio_processing/aecm/BUILD.gn b/modules/audio_processing/aecm/BUILD.gn deleted file mode 100644 index a945d0c58da..00000000000 --- a/modules/audio_processing/aecm/BUILD.gn +++ /dev/null @@ -1,43 +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. - -import("../../../webrtc.gni") - -rtc_library("aecm_core") { - sources = [ - "aecm_core.cc", - "aecm_core.h", - "aecm_defines.h", - "echo_control_mobile.cc", - "echo_control_mobile.h", - ] - deps = [ - "../../../common_audio:common_audio_c", - "../../../rtc_base:checks", - "../../../rtc_base:safe_conversions", - "../../../rtc_base:sanitizer", - "../utility:legacy_delay_estimator", - ] - cflags = [] - - if (rtc_build_with_neon) { - sources += [ "aecm_core_neon.cc" ] - - if (current_cpu != "arm64") { - # Enable compilation for the NEON instruction set. - suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] - cflags += [ "-mfpu=neon" ] - } - } - - if (current_cpu == "mipsel") { - sources += [ "aecm_core_mips.cc" ] - } else { - sources += [ "aecm_core_c.cc" ] - } -} diff --git a/modules/audio_processing/aecm/aecm_core.cc b/modules/audio_processing/aecm/aecm_core.cc deleted file mode 100644 index d61da769478..00000000000 --- a/modules/audio_processing/aecm/aecm_core.cc +++ /dev/null @@ -1,1128 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/aecm/aecm_core.h" - -#include -#include -#include -#include - -#include "common_audio/signal_processing/include/signal_processing_library.h" -#include "common_audio/signal_processing/include/spl_inl.h" -#include "modules/audio_processing/aecm/aecm_defines.h" -#include "modules/audio_processing/aecm/echo_control_mobile.h" -#include "modules/audio_processing/utility/delay_estimator_wrapper.h" -#include "rtc_base/checks.h" -#include "rtc_base/numerics/safe_conversions.h" - -extern "C" { -#include "common_audio/ring_buffer.h" -#include "common_audio/signal_processing/include/real_fft.h" -} - -namespace webrtc { - -namespace { - -#ifdef AEC_DEBUG -FILE* dfile; -FILE* testfile; -#endif - -// Initialization table for echo channel in 8 kHz -const int16_t kChannelStored8kHz[PART_LEN1] = { - 2040, 1815, 1590, 1498, 1405, 1395, 1385, 1418, 1451, 1506, 1562, - 1644, 1726, 1804, 1882, 1918, 1953, 1982, 2010, 2025, 2040, 2034, - 2027, 2021, 2014, 1997, 1980, 1925, 1869, 1800, 1732, 1683, 1635, - 1604, 1572, 1545, 1517, 1481, 1444, 1405, 1367, 1331, 1294, 1270, - 1245, 1239, 1233, 1247, 1260, 1282, 1303, 1338, 1373, 1407, 1441, - 1470, 1499, 1524, 1549, 1565, 1582, 1601, 1621, 1649, 1676}; - -// Initialization table for echo channel in 16 kHz -const int16_t kChannelStored16kHz[PART_LEN1] = { - 2040, 1590, 1405, 1385, 1451, 1562, 1726, 1882, 1953, 2010, 2040, - 2027, 2014, 1980, 1869, 1732, 1635, 1572, 1517, 1444, 1367, 1294, - 1245, 1233, 1260, 1303, 1373, 1441, 1499, 1549, 1582, 1621, 1676, - 1741, 1802, 1861, 1921, 1983, 2040, 2102, 2170, 2265, 2375, 2515, - 2651, 2781, 2922, 3075, 3253, 3471, 3738, 3976, 4151, 4258, 4308, - 4288, 4270, 4253, 4237, 4179, 4086, 3947, 3757, 3484, 3153}; - -} // namespace - -const int16_t WebRtcAecm_kCosTable[] = { - 8192, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112, 8091, 8067, - 8041, 8012, 7982, 7948, 7912, 7874, 7834, 7791, 7745, 7697, 7647, - 7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164, 7094, 7021, 6947, - 6870, 6791, 6710, 6627, 6542, 6455, 6366, 6275, 6182, 6087, 5991, - 5892, 5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, 4930, 4815, - 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845, 3719, 3591, 3462, - 3331, 3200, 3068, 2935, 2801, 2667, 2531, 2395, 2258, 2120, 1981, - 1842, 1703, 1563, 1422, 1281, 1140, 998, 856, 713, 571, 428, - 285, 142, 0, -142, -285, -428, -571, -713, -856, -998, -1140, - -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, -2531, -2667, - -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845, -3971, -4095, - -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155, -5265, -5374, - -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275, -6366, -6455, - -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, -7233, -7299, - -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791, -7834, -7874, - -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, -8147, -8160, - -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172, -8160, -8147, - -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834, - -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, -7299, -7233, - -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542, -6455, -6366, - -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481, -5374, -5265, - -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219, -4096, -3971, - -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, -2667, -2531, - -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, -1140, -998, - -856, -713, -571, -428, -285, -142, 0, 142, 285, 428, 571, - 713, 856, 998, 1140, 1281, 1422, 1563, 1703, 1842, 1981, 2120, - 2258, 2395, 2531, 2667, 2801, 2935, 3068, 3200, 3331, 3462, 3591, - 3719, 3845, 3971, 4095, 4219, 4341, 4461, 4580, 4698, 4815, 4930, - 5043, 5155, 5265, 5374, 5481, 5586, 5690, 5792, 5892, 5991, 6087, - 6182, 6275, 6366, 6455, 6542, 6627, 6710, 6791, 6870, 6947, 7021, - 7094, 7164, 7233, 7299, 7362, 7424, 7483, 7540, 7595, 7647, 7697, - 7745, 7791, 7834, 7874, 7912, 7948, 7982, 8012, 8041, 8067, 8091, - 8112, 8130, 8147, 8160, 8172, 8180, 8187, 8190}; - -const int16_t WebRtcAecm_kSinTable[] = { - 0, 142, 285, 428, 571, 713, 856, 998, 1140, 1281, 1422, - 1563, 1703, 1842, 1981, 2120, 2258, 2395, 2531, 2667, 2801, 2935, - 3068, 3200, 3331, 3462, 3591, 3719, 3845, 3971, 4095, 4219, 4341, - 4461, 4580, 4698, 4815, 4930, 5043, 5155, 5265, 5374, 5481, 5586, - 5690, 5792, 5892, 5991, 6087, 6182, 6275, 6366, 6455, 6542, 6627, - 6710, 6791, 6870, 6947, 7021, 7094, 7164, 7233, 7299, 7362, 7424, - 7483, 7540, 7595, 7647, 7697, 7745, 7791, 7834, 7874, 7912, 7948, - 7982, 8012, 8041, 8067, 8091, 8112, 8130, 8147, 8160, 8172, 8180, - 8187, 8190, 8191, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112, - 8091, 8067, 8041, 8012, 7982, 7948, 7912, 7874, 7834, 7791, 7745, - 7697, 7647, 7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164, 7094, - 7021, 6947, 6870, 6791, 6710, 6627, 6542, 6455, 6366, 6275, 6182, - 6087, 5991, 5892, 5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, - 4930, 4815, 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845, 3719, - 3591, 3462, 3331, 3200, 3068, 2935, 2801, 2667, 2531, 2395, 2258, - 2120, 1981, 1842, 1703, 1563, 1422, 1281, 1140, 998, 856, 713, - 571, 428, 285, 142, 0, -142, -285, -428, -571, -713, -856, - -998, -1140, -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, - -2531, -2667, -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845, - -3971, -4095, -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155, - -5265, -5374, -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275, - -6366, -6455, -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, - -7233, -7299, -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791, - -7834, -7874, -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, - -8147, -8160, -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172, - -8160, -8147, -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912, - -7874, -7834, -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, - -7299, -7233, -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542, - -6455, -6366, -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481, - -5374, -5265, -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219, - -4096, -3971, -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, - -2667, -2531, -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, - -1140, -998, -856, -713, -571, -428, -285, -142}; - -// Moves the pointer to the next entry and inserts `far_spectrum` and -// corresponding Q-domain in its buffer. -// -// Inputs: -// - self : Pointer to the delay estimation instance -// - far_spectrum : Pointer to the far end spectrum -// - far_q : Q-domain of far end spectrum -// -void WebRtcAecm_UpdateFarHistory(AecmCore* self, - uint16_t* far_spectrum, - int far_q) { - // Get new buffer position - self->far_history_pos++; - if (self->far_history_pos >= MAX_DELAY) { - self->far_history_pos = 0; - } - // Update Q-domain buffer - self->far_q_domains[self->far_history_pos] = far_q; - // Update far end spectrum buffer - memcpy(&(self->far_history[self->far_history_pos * PART_LEN1]), far_spectrum, - sizeof(uint16_t) * PART_LEN1); -} - -// Returns a pointer to the far end spectrum aligned to current near end -// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been -// called before AlignedFarend(...). Otherwise, you get the pointer to the -// previous frame. The memory is only valid until the next call of -// WebRtc_DelayEstimatorProcessFix(...). -// -// Inputs: -// - self : Pointer to the AECM instance. -// - delay : Current delay estimate. -// -// Output: -// - far_q : The Q-domain of the aligned far end spectrum -// -// Return value: -// - far_spectrum : Pointer to the aligned far end spectrum -// NULL - Error -// -const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self, - int* far_q, - int delay) { - int buffer_position = 0; - RTC_DCHECK(self); - buffer_position = self->far_history_pos - delay; - - // Check buffer position - if (buffer_position < 0) { - buffer_position += MAX_DELAY; - } - // Get Q-domain - *far_q = self->far_q_domains[buffer_position]; - // Return far end spectrum - return &(self->far_history[buffer_position * PART_LEN1]); -} - -// Declare function pointers. -CalcLinearEnergies WebRtcAecm_CalcLinearEnergies; -StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel; -ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel; - -AecmCore* WebRtcAecm_CreateCore() { - // Allocate zero-filled memory. - AecmCore* aecm = static_cast(calloc(1, sizeof(AecmCore))); - - aecm->farFrameBuf = - WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aecm->farFrameBuf) { - WebRtcAecm_FreeCore(aecm); - return nullptr; - } - - aecm->nearNoisyFrameBuf = - WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aecm->nearNoisyFrameBuf) { - WebRtcAecm_FreeCore(aecm); - return nullptr; - } - - aecm->nearCleanFrameBuf = - WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aecm->nearCleanFrameBuf) { - WebRtcAecm_FreeCore(aecm); - return nullptr; - } - - aecm->outFrameBuf = - WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t)); - if (!aecm->outFrameBuf) { - WebRtcAecm_FreeCore(aecm); - return nullptr; - } - - aecm->delay_estimator_farend = - WebRtc_CreateDelayEstimatorFarend(PART_LEN1, MAX_DELAY); - if (aecm->delay_estimator_farend == nullptr) { - WebRtcAecm_FreeCore(aecm); - return nullptr; - } - aecm->delay_estimator = - WebRtc_CreateDelayEstimator(aecm->delay_estimator_farend, 0); - if (aecm->delay_estimator == nullptr) { - WebRtcAecm_FreeCore(aecm); - return nullptr; - } - // TODO(bjornv): Explicitly disable robust delay validation until no - // performance regression has been established. Then remove the line. - WebRtc_enable_robust_validation(aecm->delay_estimator, 0); - - aecm->real_fft = WebRtcSpl_CreateRealFFT(PART_LEN_SHIFT); - if (aecm->real_fft == nullptr) { - WebRtcAecm_FreeCore(aecm); - return nullptr; - } - - // Init some aecm pointers. 16 and 32 byte alignment is only necessary - // for Neon code currently. - aecm->xBuf = (int16_t*)(((uintptr_t)aecm->xBuf_buf + 31) & ~31); - aecm->dBufClean = (int16_t*)(((uintptr_t)aecm->dBufClean_buf + 31) & ~31); - aecm->dBufNoisy = (int16_t*)(((uintptr_t)aecm->dBufNoisy_buf + 31) & ~31); - aecm->outBuf = (int16_t*)(((uintptr_t)aecm->outBuf_buf + 15) & ~15); - aecm->channelStored = - (int16_t*)(((uintptr_t)aecm->channelStored_buf + 15) & ~15); - aecm->channelAdapt16 = - (int16_t*)(((uintptr_t)aecm->channelAdapt16_buf + 15) & ~15); - aecm->channelAdapt32 = - (int32_t*)(((uintptr_t)aecm->channelAdapt32_buf + 31) & ~31); - - return aecm; -} - -void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path) { - int i = 0; - - // Reset the stored channel - memcpy(aecm->channelStored, echo_path, sizeof(int16_t) * PART_LEN1); - // Reset the adapted channels - memcpy(aecm->channelAdapt16, echo_path, sizeof(int16_t) * PART_LEN1); - for (i = 0; i < PART_LEN1; i++) { - aecm->channelAdapt32[i] = (int32_t)aecm->channelAdapt16[i] << 16; - } - - // Reset channel storing variables - aecm->mseAdaptOld = 1000; - aecm->mseStoredOld = 1000; - aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX; - aecm->mseChannelCount = 0; -} - -static void CalcLinearEnergiesC(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored) { - int i; - - // Get energy for the delayed far end signal and estimated - // echo using both stored and adapted channels. - for (i = 0; i < PART_LEN1; i++) { - echo_est[i] = - WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); - (*far_energy) += (uint32_t)(far_spectrum[i]); - *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; - (*echo_energy_stored) += (uint32_t)echo_est[i]; - } -} - -static void StoreAdaptiveChannelC(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est) { - int i; - - // During startup we store the channel every block. - memcpy(aecm->channelStored, aecm->channelAdapt16, - sizeof(int16_t) * PART_LEN1); - // Recalculate echo estimate - for (i = 0; i < PART_LEN; i += 4) { - echo_est[i] = - WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); - echo_est[i + 1] = - WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1], far_spectrum[i + 1]); - echo_est[i + 2] = - WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2], far_spectrum[i + 2]); - echo_est[i + 3] = - WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3], far_spectrum[i + 3]); - } - echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); -} - -static void ResetAdaptiveChannelC(AecmCore* aecm) { - int i; - - // The stored channel has a significantly lower MSE than the adaptive one for - // two consecutive calculations. Reset the adaptive channel. - memcpy(aecm->channelAdapt16, aecm->channelStored, - sizeof(int16_t) * PART_LEN1); - // Restore the W32 channel - for (i = 0; i < PART_LEN; i += 4) { - aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; - aecm->channelAdapt32[i + 1] = (int32_t)aecm->channelStored[i + 1] << 16; - aecm->channelAdapt32[i + 2] = (int32_t)aecm->channelStored[i + 2] << 16; - aecm->channelAdapt32[i + 3] = (int32_t)aecm->channelStored[i + 3] << 16; - } - aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; -} - -// Initialize function pointers for ARM Neon platform. -#if defined(WEBRTC_HAS_NEON) -static void WebRtcAecm_InitNeon(void) { - WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannelNeon; - WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannelNeon; - WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergiesNeon; -} -#endif - -// Initialize function pointers for MIPS platform. -#if defined(MIPS32_LE) -static void WebRtcAecm_InitMips(void) { -#if defined(MIPS_DSP_R1_LE) - WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannel_mips; - WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannel_mips; -#endif - WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergies_mips; -} -#endif - -// WebRtcAecm_InitCore(...) -// -// This function initializes the AECM instant created with -// WebRtcAecm_CreateCore(...) Input: -// - aecm : Pointer to the Echo Suppression instance -// - samplingFreq : Sampling Frequency -// -// Output: -// - aecm : Initialized instance -// -// Return value : 0 - Ok -// -1 - Error -// -int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq) { - int i = 0; - int32_t tmp32 = PART_LEN1 * PART_LEN1; - int16_t tmp16 = PART_LEN1; - - if (samplingFreq != 8000 && samplingFreq != 16000) { - samplingFreq = 8000; - return -1; - } - // sanity check of sampling frequency - aecm->mult = (int16_t)samplingFreq / 8000; - - aecm->farBufWritePos = 0; - aecm->farBufReadPos = 0; - aecm->knownDelay = 0; - aecm->lastKnownDelay = 0; - - WebRtc_InitBuffer(aecm->farFrameBuf); - WebRtc_InitBuffer(aecm->nearNoisyFrameBuf); - WebRtc_InitBuffer(aecm->nearCleanFrameBuf); - WebRtc_InitBuffer(aecm->outFrameBuf); - - memset(aecm->xBuf_buf, 0, sizeof(aecm->xBuf_buf)); - memset(aecm->dBufClean_buf, 0, sizeof(aecm->dBufClean_buf)); - memset(aecm->dBufNoisy_buf, 0, sizeof(aecm->dBufNoisy_buf)); - memset(aecm->outBuf_buf, 0, sizeof(aecm->outBuf_buf)); - - aecm->seed = 666; - aecm->totCount = 0; - - if (WebRtc_InitDelayEstimatorFarend(aecm->delay_estimator_farend) != 0) { - return -1; - } - if (WebRtc_InitDelayEstimator(aecm->delay_estimator) != 0) { - return -1; - } - // Set far end histories to zero - memset(aecm->far_history, 0, sizeof(uint16_t) * PART_LEN1 * MAX_DELAY); - memset(aecm->far_q_domains, 0, sizeof(int) * MAX_DELAY); - aecm->far_history_pos = MAX_DELAY; - - aecm->nlpFlag = 1; - aecm->fixedDelay = -1; - - aecm->dfaCleanQDomain = 0; - aecm->dfaCleanQDomainOld = 0; - aecm->dfaNoisyQDomain = 0; - aecm->dfaNoisyQDomainOld = 0; - - memset(aecm->nearLogEnergy, 0, sizeof(aecm->nearLogEnergy)); - aecm->farLogEnergy = 0; - memset(aecm->echoAdaptLogEnergy, 0, sizeof(aecm->echoAdaptLogEnergy)); - memset(aecm->echoStoredLogEnergy, 0, sizeof(aecm->echoStoredLogEnergy)); - - // Initialize the echo channels with a stored shape. - if (samplingFreq == 8000) { - WebRtcAecm_InitEchoPathCore(aecm, kChannelStored8kHz); - } else { - WebRtcAecm_InitEchoPathCore(aecm, kChannelStored16kHz); - } - - memset(aecm->echoFilt, 0, sizeof(aecm->echoFilt)); - memset(aecm->nearFilt, 0, sizeof(aecm->nearFilt)); - aecm->noiseEstCtr = 0; - - aecm->cngMode = AecmTrue; - - memset(aecm->noiseEstTooLowCtr, 0, sizeof(aecm->noiseEstTooLowCtr)); - memset(aecm->noiseEstTooHighCtr, 0, sizeof(aecm->noiseEstTooHighCtr)); - // Shape the initial noise level to an approximate pink noise. - for (i = 0; i < (PART_LEN1 >> 1) - 1; i++) { - aecm->noiseEst[i] = (tmp32 << 8); - tmp16--; - tmp32 -= (int32_t)((tmp16 << 1) + 1); - } - for (; i < PART_LEN1; i++) { - aecm->noiseEst[i] = (tmp32 << 8); - } - - aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX; - aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN; - aecm->farEnergyMaxMin = 0; - aecm->farEnergyVAD = FAR_ENERGY_MIN; // This prevents false speech detection - // at the beginning. - aecm->farEnergyMSE = 0; - aecm->currentVADValue = 0; - aecm->vadUpdateCount = 0; - aecm->firstVAD = 1; - - aecm->startupState = 0; - aecm->supGain = SUPGAIN_DEFAULT; - aecm->supGainOld = SUPGAIN_DEFAULT; - - aecm->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; - aecm->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; - aecm->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; - aecm->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; - - // Assert a preprocessor definition at compile-time. It's an assumption - // used in assembly code, so check the assembly files before any change. - static_assert(PART_LEN % 16 == 0, "PART_LEN is not a multiple of 16"); - - // Initialize function pointers. - WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesC; - WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC; - WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC; - -#if defined(WEBRTC_HAS_NEON) - WebRtcAecm_InitNeon(); -#endif - -#if defined(MIPS32_LE) - WebRtcAecm_InitMips(); -#endif - return 0; -} - -// TODO(bjornv): This function is currently not used. Add support for these -// parameters from a higher level -int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag) { - aecm->nlpFlag = nlpFlag; - aecm->fixedDelay = delay; - - return 0; -} - -void WebRtcAecm_FreeCore(AecmCore* aecm) { - if (aecm == nullptr) { - return; - } - - WebRtc_FreeBuffer(aecm->farFrameBuf); - WebRtc_FreeBuffer(aecm->nearNoisyFrameBuf); - WebRtc_FreeBuffer(aecm->nearCleanFrameBuf); - WebRtc_FreeBuffer(aecm->outFrameBuf); - - WebRtc_FreeDelayEstimator(aecm->delay_estimator); - WebRtc_FreeDelayEstimatorFarend(aecm->delay_estimator_farend); - WebRtcSpl_FreeRealFFT(aecm->real_fft); - - free(aecm); -} - -int WebRtcAecm_ProcessFrame(AecmCore* aecm, - const int16_t* farend, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* out) { - int16_t outBlock_buf[PART_LEN + 8]; // Align buffer to 8-byte boundary. - int16_t* outBlock = (int16_t*)(((uintptr_t)outBlock_buf + 15) & ~15); - - int16_t farFrame[FRAME_LEN]; - const int16_t* out_ptr = nullptr; - int size = 0; - - // Buffer the current frame. - // Fetch an older one corresponding to the delay. - WebRtcAecm_BufferFarFrame(aecm, farend, FRAME_LEN); - WebRtcAecm_FetchFarFrame(aecm, farFrame, FRAME_LEN, aecm->knownDelay); - - // Buffer the synchronized far and near frames, - // to pass the smaller blocks individually. - WebRtc_WriteBuffer(aecm->farFrameBuf, farFrame, FRAME_LEN); - WebRtc_WriteBuffer(aecm->nearNoisyFrameBuf, nearendNoisy, FRAME_LEN); - if (nearendClean != nullptr) { - WebRtc_WriteBuffer(aecm->nearCleanFrameBuf, nearendClean, FRAME_LEN); - } - - // Process as many blocks as possible. - while (WebRtc_available_read(aecm->farFrameBuf) >= PART_LEN) { - int16_t far_block[PART_LEN]; - const int16_t* far_block_ptr = nullptr; - int16_t near_noisy_block[PART_LEN]; - const int16_t* near_noisy_block_ptr = nullptr; - - WebRtc_ReadBuffer(aecm->farFrameBuf, (void**)&far_block_ptr, far_block, - PART_LEN); - WebRtc_ReadBuffer(aecm->nearNoisyFrameBuf, (void**)&near_noisy_block_ptr, - near_noisy_block, PART_LEN); - if (nearendClean != nullptr) { - int16_t near_clean_block[PART_LEN]; - const int16_t* near_clean_block_ptr = nullptr; - - WebRtc_ReadBuffer(aecm->nearCleanFrameBuf, (void**)&near_clean_block_ptr, - near_clean_block, PART_LEN); - if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr, - near_clean_block_ptr, outBlock) == -1) { - return -1; - } - } else { - if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr, - nullptr, outBlock) == -1) { - return -1; - } - } - - WebRtc_WriteBuffer(aecm->outFrameBuf, outBlock, PART_LEN); - } - - // Stuff the out buffer if we have less than a frame to output. - // This should only happen for the first frame. - size = (int)WebRtc_available_read(aecm->outFrameBuf); - if (size < FRAME_LEN) { - WebRtc_MoveReadPtr(aecm->outFrameBuf, size - FRAME_LEN); - } - - // Obtain an output frame. - WebRtc_ReadBuffer(aecm->outFrameBuf, (void**)&out_ptr, out, FRAME_LEN); - if (out_ptr != out) { - // ReadBuffer() hasn't copied to `out` in this case. - memcpy(out, out_ptr, FRAME_LEN * sizeof(int16_t)); - } - - return 0; -} - -// WebRtcAecm_AsymFilt(...) -// -// Performs asymmetric filtering. -// -// Inputs: -// - filtOld : Previous filtered value. -// - inVal : New input value. -// - stepSizePos : Step size when we have a positive contribution. -// - stepSizeNeg : Step size when we have a negative contribution. -// -// Output: -// -// Return: - Filtered value. -// -int16_t WebRtcAecm_AsymFilt(const int16_t filtOld, - const int16_t inVal, - const int16_t stepSizePos, - const int16_t stepSizeNeg) { - int16_t retVal; - - if ((filtOld == WEBRTC_SPL_WORD16_MAX) | (filtOld == WEBRTC_SPL_WORD16_MIN)) { - return inVal; - } - retVal = filtOld; - if (filtOld > inVal) { - retVal -= (filtOld - inVal) >> stepSizeNeg; - } else { - retVal += (inVal - filtOld) >> stepSizePos; - } - - return retVal; -} - -// ExtractFractionPart(a, zeros) -// -// returns the fraction part of `a`, with `zeros` number of leading zeros, as an -// int16_t scaled to Q8. There is no sanity check of `a` in the sense that the -// number of zeros match. -static int16_t ExtractFractionPart(uint32_t a, int zeros) { - return (int16_t)(((a << zeros) & 0x7FFFFFFF) >> 23); -} - -// Calculates and returns the log of `energy` in Q8. The input `energy` is -// supposed to be in Q(`q_domain`). -static int16_t LogOfEnergyInQ8(uint32_t energy, int q_domain) { - static const int16_t kLogLowValue = PART_LEN_SHIFT << 7; - int16_t log_energy_q8 = kLogLowValue; - if (energy > 0) { - int zeros = WebRtcSpl_NormU32(energy); - int16_t frac = ExtractFractionPart(energy, zeros); - // log2 of `energy` in Q8. - log_energy_q8 += ((31 - zeros) << 8) + frac - (q_domain << 8); - } - return log_energy_q8; -} - -// WebRtcAecm_CalcEnergies(...) -// -// This function calculates the log of energies for nearend, farend and -// estimated echoes. There is also an update of energy decision levels, i.e. -// internal VAD. -// -// -// @param aecm [i/o] Handle of the AECM instance. -// @param far_spectrum [in] Pointer to farend spectrum. -// @param far_q [in] Q-domain of farend spectrum. -// @param nearEner [in] Near end energy for current block in -// Q(aecm->dfaQDomain). -// @param echoEst [out] Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16). -// -void WebRtcAecm_CalcEnergies(AecmCore* aecm, - const uint16_t* far_spectrum, - const int16_t far_q, - const uint32_t nearEner, - int32_t* echoEst) { - // Local variables - uint32_t tmpAdapt = 0; - uint32_t tmpStored = 0; - uint32_t tmpFar = 0; - - int i; - - int16_t tmp16; - int16_t increase_max_shifts = 4; - int16_t decrease_max_shifts = 11; - int16_t increase_min_shifts = 11; - int16_t decrease_min_shifts = 3; - - // Get log of near end energy and store in buffer - - // Shift buffer - memmove(aecm->nearLogEnergy + 1, aecm->nearLogEnergy, - sizeof(int16_t) * (MAX_BUF_LEN - 1)); - - // Logarithm of integrated magnitude spectrum (nearEner) - aecm->nearLogEnergy[0] = LogOfEnergyInQ8(nearEner, aecm->dfaNoisyQDomain); - - WebRtcAecm_CalcLinearEnergies(aecm, far_spectrum, echoEst, &tmpFar, &tmpAdapt, - &tmpStored); - - // Shift buffers - memmove(aecm->echoAdaptLogEnergy + 1, aecm->echoAdaptLogEnergy, - sizeof(int16_t) * (MAX_BUF_LEN - 1)); - memmove(aecm->echoStoredLogEnergy + 1, aecm->echoStoredLogEnergy, - sizeof(int16_t) * (MAX_BUF_LEN - 1)); - - // Logarithm of delayed far end energy - aecm->farLogEnergy = LogOfEnergyInQ8(tmpFar, far_q); - - // Logarithm of estimated echo energy through adapted channel - aecm->echoAdaptLogEnergy[0] = - LogOfEnergyInQ8(tmpAdapt, RESOLUTION_CHANNEL16 + far_q); - - // Logarithm of estimated echo energy through stored channel - aecm->echoStoredLogEnergy[0] = - LogOfEnergyInQ8(tmpStored, RESOLUTION_CHANNEL16 + far_q); - - // Update farend energy levels (min, max, vad, mse) - if (aecm->farLogEnergy > FAR_ENERGY_MIN) { - if (aecm->startupState == 0) { - increase_max_shifts = 2; - decrease_min_shifts = 2; - increase_min_shifts = 8; - } - - aecm->farEnergyMin = - WebRtcAecm_AsymFilt(aecm->farEnergyMin, aecm->farLogEnergy, - increase_min_shifts, decrease_min_shifts); - aecm->farEnergyMax = - WebRtcAecm_AsymFilt(aecm->farEnergyMax, aecm->farLogEnergy, - increase_max_shifts, decrease_max_shifts); - aecm->farEnergyMaxMin = (aecm->farEnergyMax - aecm->farEnergyMin); - - // Dynamic VAD region size - tmp16 = 2560 - aecm->farEnergyMin; - if (tmp16 > 0) { - tmp16 = (int16_t)((tmp16 * FAR_ENERGY_VAD_REGION) >> 9); - } else { - tmp16 = 0; - } - tmp16 += FAR_ENERGY_VAD_REGION; - - if ((aecm->startupState == 0) | (aecm->vadUpdateCount > 1024)) { - // In startup phase or VAD update halted - aecm->farEnergyVAD = aecm->farEnergyMin + tmp16; - } else { - if (aecm->farEnergyVAD > aecm->farLogEnergy) { - aecm->farEnergyVAD += - (aecm->farLogEnergy + tmp16 - aecm->farEnergyVAD) >> 6; - aecm->vadUpdateCount = 0; - } else { - aecm->vadUpdateCount++; - } - } - // Put MSE threshold higher than VAD - aecm->farEnergyMSE = aecm->farEnergyVAD + (1 << 8); - } - - // Update VAD variables - if (aecm->farLogEnergy > aecm->farEnergyVAD) { - if ((aecm->startupState == 0) | (aecm->farEnergyMaxMin > FAR_ENERGY_DIFF)) { - // We are in startup or have significant dynamics in input speech level - aecm->currentVADValue = 1; - } - } else { - aecm->currentVADValue = 0; - } - if ((aecm->currentVADValue) && (aecm->firstVAD)) { - aecm->firstVAD = 0; - if (aecm->echoAdaptLogEnergy[0] > aecm->nearLogEnergy[0]) { - // The estimated echo has higher energy than the near end signal. - // This means that the initialization was too aggressive. Scale - // down by a factor 8 - for (i = 0; i < PART_LEN1; i++) { - aecm->channelAdapt16[i] >>= 3; - } - // Compensate the adapted echo energy level accordingly. - aecm->echoAdaptLogEnergy[0] -= (3 << 8); - aecm->firstVAD = 1; - } - } -} - -// WebRtcAecm_CalcStepSize(...) -// -// This function calculates the step size used in channel estimation -// -// -// @param aecm [in] Handle of the AECM instance. -// @param mu [out] (Return value) Stepsize in log2(), i.e. number of -// shifts. -// -// -int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm) { - int32_t tmp32; - int16_t tmp16; - int16_t mu = MU_MAX; - - // Here we calculate the step size mu used in the - // following NLMS based Channel estimation algorithm - if (!aecm->currentVADValue) { - // Far end energy level too low, no channel update - mu = 0; - } else if (aecm->startupState > 0) { - if (aecm->farEnergyMin >= aecm->farEnergyMax) { - mu = MU_MIN; - } else { - tmp16 = (aecm->farLogEnergy - aecm->farEnergyMin); - tmp32 = tmp16 * MU_DIFF; - tmp32 = WebRtcSpl_DivW32W16(tmp32, aecm->farEnergyMaxMin); - mu = MU_MIN - 1 - (int16_t)(tmp32); - // The -1 is an alternative to rounding. This way we get a larger - // stepsize, so we in some sense compensate for truncation in NLMS - } - if (mu < MU_MAX) { - mu = MU_MAX; // Equivalent with maximum step size of 2^-MU_MAX - } - } - - return mu; -} - -// WebRtcAecm_UpdateChannel(...) -// -// This function performs channel estimation. NLMS and decision on channel -// storage. -// -// -// @param aecm [i/o] Handle of the AECM instance. -// @param far_spectrum [in] Absolute value of the farend signal in Q(far_q) -// @param far_q [in] Q-domain of the farend signal -// @param dfa [in] Absolute value of the nearend signal -// (Q[aecm->dfaQDomain]) -// @param mu [in] NLMS step size. -// @param echoEst [i/o] Estimated echo in Q(far_q+RESOLUTION_CHANNEL16). -// -void WebRtcAecm_UpdateChannel(AecmCore* aecm, - const uint16_t* far_spectrum, - const int16_t far_q, - const uint16_t* const dfa, - const int16_t mu, - int32_t* echoEst) { - uint32_t tmpU32no1, tmpU32no2; - int32_t tmp32no1, tmp32no2; - int32_t mseStored; - int32_t mseAdapt; - - int i; - - int16_t zerosFar, zerosNum, zerosCh, zerosDfa; - int16_t shiftChFar, shiftNum, shift2ResChan; - int16_t tmp16no1; - int16_t xfaQ, dfaQ; - - // This is the channel estimation algorithm. It is base on NLMS but has a - // variable step length, which was calculated above. - if (mu) { - for (i = 0; i < PART_LEN1; i++) { - // Determine norm of channel and farend to make sure we don't get overflow - // in multiplication - zerosCh = WebRtcSpl_NormU32(aecm->channelAdapt32[i]); - zerosFar = WebRtcSpl_NormU32((uint32_t)far_spectrum[i]); - if (zerosCh + zerosFar > 31) { - // Multiplication is safe - tmpU32no1 = - WEBRTC_SPL_UMUL_32_16(aecm->channelAdapt32[i], far_spectrum[i]); - shiftChFar = 0; - } else { - // We need to shift down before multiplication - shiftChFar = 32 - zerosCh - zerosFar; - // If zerosCh == zerosFar == 0, shiftChFar is 32. A - // right shift of 32 is undefined. To avoid that, we - // do this check. - tmpU32no1 = - dchecked_cast( - shiftChFar >= 32 ? 0 : aecm->channelAdapt32[i] >> shiftChFar) * - far_spectrum[i]; - } - // Determine Q-domain of numerator - zerosNum = WebRtcSpl_NormU32(tmpU32no1); - if (dfa[i]) { - zerosDfa = WebRtcSpl_NormU32((uint32_t)dfa[i]); - } else { - zerosDfa = 32; - } - tmp16no1 = zerosDfa - 2 + aecm->dfaNoisyQDomain - RESOLUTION_CHANNEL32 - - far_q + shiftChFar; - if (zerosNum > tmp16no1 + 1) { - xfaQ = tmp16no1; - dfaQ = zerosDfa - 2; - } else { - xfaQ = zerosNum - 2; - dfaQ = RESOLUTION_CHANNEL32 + far_q - aecm->dfaNoisyQDomain - - shiftChFar + xfaQ; - } - // Add in the same Q-domain - tmpU32no1 = WEBRTC_SPL_SHIFT_W32(tmpU32no1, xfaQ); - tmpU32no2 = WEBRTC_SPL_SHIFT_W32((uint32_t)dfa[i], dfaQ); - tmp32no1 = (int32_t)tmpU32no2 - (int32_t)tmpU32no1; - zerosNum = WebRtcSpl_NormW32(tmp32no1); - if ((tmp32no1) && (far_spectrum[i] > (CHANNEL_VAD << far_q))) { - // - // Update is needed - // - // This is what we would like to compute - // - // tmp32no1 = dfa[i] - (aecm->channelAdapt[i] * far_spectrum[i]) - // tmp32norm = (i + 1) - // aecm->channelAdapt[i] += (2^mu) * tmp32no1 - // / (tmp32norm * far_spectrum[i]) - // - - // Make sure we don't get overflow in multiplication. - if (zerosNum + zerosFar > 31) { - if (tmp32no1 > 0) { - tmp32no2 = - (int32_t)WEBRTC_SPL_UMUL_32_16(tmp32no1, far_spectrum[i]); - } else { - tmp32no2 = - -(int32_t)WEBRTC_SPL_UMUL_32_16(-tmp32no1, far_spectrum[i]); - } - shiftNum = 0; - } else { - shiftNum = 32 - (zerosNum + zerosFar); - if (tmp32no1 > 0) { - tmp32no2 = (tmp32no1 >> shiftNum) * far_spectrum[i]; - } else { - tmp32no2 = -((-tmp32no1 >> shiftNum) * far_spectrum[i]); - } - } - // Normalize with respect to frequency bin - tmp32no2 = WebRtcSpl_DivW32W16(tmp32no2, i + 1); - // Make sure we are in the right Q-domain - shift2ResChan = - shiftNum + shiftChFar - xfaQ - mu - ((30 - zerosFar) << 1); - if (WebRtcSpl_NormW32(tmp32no2) < shift2ResChan) { - tmp32no2 = WEBRTC_SPL_WORD32_MAX; - } else { - tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan); - } - aecm->channelAdapt32[i] = - WebRtcSpl_AddSatW32(aecm->channelAdapt32[i], tmp32no2); - if (aecm->channelAdapt32[i] < 0) { - // We can never have negative channel gain - aecm->channelAdapt32[i] = 0; - } - aecm->channelAdapt16[i] = (int16_t)(aecm->channelAdapt32[i] >> 16); - } - } - } - // END: Adaptive channel update - - // Determine if we should store or restore the channel - if ((aecm->startupState == 0) & (aecm->currentVADValue)) { - // During startup we store the channel every block, - // and we recalculate echo estimate - WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); - } else { - if (aecm->farLogEnergy < aecm->farEnergyMSE) { - aecm->mseChannelCount = 0; - } else { - aecm->mseChannelCount++; - } - // Enough data for validation. Store channel if we can. - if (aecm->mseChannelCount >= (MIN_MSE_COUNT + 10)) { - // We have enough data. - // Calculate MSE of "Adapt" and "Stored" versions. - // It is actually not MSE, but average absolute error. - mseStored = 0; - mseAdapt = 0; - for (i = 0; i < MIN_MSE_COUNT; i++) { - tmp32no1 = ((int32_t)aecm->echoStoredLogEnergy[i] - - (int32_t)aecm->nearLogEnergy[i]); - tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); - mseStored += tmp32no2; - - tmp32no1 = ((int32_t)aecm->echoAdaptLogEnergy[i] - - (int32_t)aecm->nearLogEnergy[i]); - tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); - mseAdapt += tmp32no2; - } - if (((mseStored << MSE_RESOLUTION) < (MIN_MSE_DIFF * mseAdapt)) & - ((aecm->mseStoredOld << MSE_RESOLUTION) < - (MIN_MSE_DIFF * aecm->mseAdaptOld))) { - // The stored channel has a significantly lower MSE than the adaptive - // one for two consecutive calculations. Reset the adaptive channel. - WebRtcAecm_ResetAdaptiveChannel(aecm); - } else if (((MIN_MSE_DIFF * mseStored) > (mseAdapt << MSE_RESOLUTION)) & - (mseAdapt < aecm->mseThreshold) & - (aecm->mseAdaptOld < aecm->mseThreshold)) { - // The adaptive channel has a significantly lower MSE than the stored - // one. The MSE for the adaptive channel has also been low for two - // consecutive calculations. Store the adaptive channel. - WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst); - - // Update threshold - if (aecm->mseThreshold == WEBRTC_SPL_WORD32_MAX) { - aecm->mseThreshold = (mseAdapt + aecm->mseAdaptOld); - } else { - int scaled_threshold = aecm->mseThreshold * 5 / 8; - aecm->mseThreshold += ((mseAdapt - scaled_threshold) * 205) >> 8; - } - } - - // Reset counter - aecm->mseChannelCount = 0; - - // Store the MSE values. - aecm->mseStoredOld = mseStored; - aecm->mseAdaptOld = mseAdapt; - } - } - // END: Determine if we should store or reset channel estimate. -} - -// CalcSuppressionGain(...) -// -// This function calculates the suppression gain that is used in the Wiener -// filter. -// -// -// @param aecm [i/n] Handle of the AECM instance. -// @param supGain [out] (Return value) Suppression gain with which to scale -// the noise -// level (Q14). -// -// -int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm) { - int32_t tmp32no1; - - int16_t supGain = SUPGAIN_DEFAULT; - int16_t tmp16no1; - int16_t dE = 0; - - // Determine suppression gain used in the Wiener filter. The gain is based on - // a mix of far end energy and echo estimation error. Adjust for the far end - // signal level. A low signal level indicates no far end signal, hence we set - // the suppression gain to 0 - if (!aecm->currentVADValue) { - supGain = 0; - } else { - // Adjust for possible double talk. If we have large variations in - // estimation error we likely have double talk (or poor channel). - tmp16no1 = (aecm->nearLogEnergy[0] - aecm->echoStoredLogEnergy[0] - - ENERGY_DEV_OFFSET); - dE = WEBRTC_SPL_ABS_W16(tmp16no1); - - if (dE < ENERGY_DEV_TOL) { - // Likely no double talk. The better estimation, the more we can suppress - // signal. Update counters - if (dE < SUPGAIN_EPC_DT) { - tmp32no1 = aecm->supGainErrParamDiffAB * dE; - tmp32no1 += (SUPGAIN_EPC_DT >> 1); - tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, SUPGAIN_EPC_DT); - supGain = aecm->supGainErrParamA - tmp16no1; - } else { - tmp32no1 = aecm->supGainErrParamDiffBD * (ENERGY_DEV_TOL - dE); - tmp32no1 += ((ENERGY_DEV_TOL - SUPGAIN_EPC_DT) >> 1); - tmp16no1 = (int16_t)WebRtcSpl_DivW32W16( - tmp32no1, (ENERGY_DEV_TOL - SUPGAIN_EPC_DT)); - supGain = aecm->supGainErrParamD + tmp16no1; - } - } else { - // Likely in double talk. Use default value - supGain = aecm->supGainErrParamD; - } - } - - if (supGain > aecm->supGainOld) { - tmp16no1 = supGain; - } else { - tmp16no1 = aecm->supGainOld; - } - aecm->supGainOld = supGain; - if (tmp16no1 < aecm->supGain) { - aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); - } else { - aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4); - } - - // END: Update suppression gain - - return aecm->supGain; -} - -void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, - const int16_t* const farend, - const int farLen) { - int writeLen = farLen, writePos = 0; - - // Check if the write position must be wrapped - while (aecm->farBufWritePos + writeLen > FAR_BUF_LEN) { - // Write to remaining buffer space before wrapping - writeLen = FAR_BUF_LEN - aecm->farBufWritePos; - memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, - sizeof(int16_t) * writeLen); - aecm->farBufWritePos = 0; - writePos = writeLen; - writeLen = farLen - writeLen; - } - - memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, - sizeof(int16_t) * writeLen); - aecm->farBufWritePos += writeLen; -} - -void WebRtcAecm_FetchFarFrame(AecmCore* const aecm, - int16_t* const farend, - const int farLen, - const int knownDelay) { - int readLen = farLen; - int readPos = 0; - int delayChange = knownDelay - aecm->lastKnownDelay; - - aecm->farBufReadPos -= delayChange; - - // Check if delay forces a read position wrap - while (aecm->farBufReadPos < 0) { - aecm->farBufReadPos += FAR_BUF_LEN; - } - while (aecm->farBufReadPos > FAR_BUF_LEN - 1) { - aecm->farBufReadPos -= FAR_BUF_LEN; - } - - aecm->lastKnownDelay = knownDelay; - - // Check if read position must be wrapped - while (aecm->farBufReadPos + readLen > FAR_BUF_LEN) { - // Read from remaining buffer space before wrapping - readLen = FAR_BUF_LEN - aecm->farBufReadPos; - memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, - sizeof(int16_t) * readLen); - aecm->farBufReadPos = 0; - readPos = readLen; - readLen = farLen - readLen; - } - memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, - sizeof(int16_t) * readLen); - aecm->farBufReadPos += readLen; -} - -} // namespace webrtc diff --git a/modules/audio_processing/aecm/aecm_core.h b/modules/audio_processing/aecm/aecm_core.h deleted file mode 100644 index a76660b9bac..00000000000 --- a/modules/audio_processing/aecm/aecm_core.h +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Performs echo control (suppression) with fft routines in fixed-point. - -#ifndef MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ -#define MODULES_AUDIO_PROCESSING_AECM_AECM_CORE_H_ - -#include - -#include "modules/audio_processing/aecm/aecm_defines.h" - -extern "C" { -#include "common_audio/ring_buffer.h" -} -struct RealFFT; - -namespace webrtc { - -#ifdef _MSC_VER // visual c++ -#define ALIGN8_BEG __declspec(align(8)) -#define ALIGN8_END -#else // gcc or icc -#define ALIGN8_BEG -#define ALIGN8_END __attribute__((aligned(8))) -#endif - -typedef struct { - int16_t real; - int16_t imag; -} ComplexInt16; - -typedef struct { - int farBufWritePos; - int farBufReadPos; - int knownDelay; - int lastKnownDelay; - int firstVAD; // Parameter to control poorly initialized channels - - RingBuffer* farFrameBuf; - RingBuffer* nearNoisyFrameBuf; - RingBuffer* nearCleanFrameBuf; - RingBuffer* outFrameBuf; - - int16_t farBuf[FAR_BUF_LEN]; - - int16_t mult; - uint32_t seed; - - // Delay estimation variables - void* delay_estimator_farend; - void* delay_estimator; - uint16_t currentDelay; - // Far end history variables - // TODO(bjornv): Replace `far_history` with ring_buffer. - uint16_t far_history[PART_LEN1 * MAX_DELAY]; - int far_history_pos; - int far_q_domains[MAX_DELAY]; - - int16_t nlpFlag; - int16_t fixedDelay; - - uint32_t totCount; - - int16_t dfaCleanQDomain; - int16_t dfaCleanQDomainOld; - int16_t dfaNoisyQDomain; - int16_t dfaNoisyQDomainOld; - - int16_t nearLogEnergy[MAX_BUF_LEN]; - int16_t farLogEnergy; - int16_t echoAdaptLogEnergy[MAX_BUF_LEN]; - int16_t echoStoredLogEnergy[MAX_BUF_LEN]; - - // The extra 16 or 32 bytes in the following buffers are for alignment based - // Neon code. - // It's designed this way since the current GCC compiler can't align a - // buffer in 16 or 32 byte boundaries properly. - int16_t channelStored_buf[PART_LEN1 + 8]; - int16_t channelAdapt16_buf[PART_LEN1 + 8]; - int32_t channelAdapt32_buf[PART_LEN1 + 8]; - int16_t xBuf_buf[PART_LEN2 + 16]; // farend - int16_t dBufClean_buf[PART_LEN2 + 16]; // nearend - int16_t dBufNoisy_buf[PART_LEN2 + 16]; // nearend - int16_t outBuf_buf[PART_LEN + 8]; - - // Pointers to the above buffers - int16_t* channelStored; - int16_t* channelAdapt16; - int32_t* channelAdapt32; - int16_t* xBuf; - int16_t* dBufClean; - int16_t* dBufNoisy; - int16_t* outBuf; - - int32_t echoFilt[PART_LEN1]; - int16_t nearFilt[PART_LEN1]; - int32_t noiseEst[PART_LEN1]; - int noiseEstTooLowCtr[PART_LEN1]; - int noiseEstTooHighCtr[PART_LEN1]; - int16_t noiseEstCtr; - int16_t cngMode; - - int32_t mseAdaptOld; - int32_t mseStoredOld; - int32_t mseThreshold; - - int16_t farEnergyMin; - int16_t farEnergyMax; - int16_t farEnergyMaxMin; - int16_t farEnergyVAD; - int16_t farEnergyMSE; - int currentVADValue; - int16_t vadUpdateCount; - - int16_t startupState; - int16_t mseChannelCount; - int16_t supGain; - int16_t supGainOld; - - int16_t supGainErrParamA; - int16_t supGainErrParamD; - int16_t supGainErrParamDiffAB; - int16_t supGainErrParamDiffBD; - - struct RealFFT* real_fft; - -#ifdef AEC_DEBUG - FILE* farFile; - FILE* nearFile; - FILE* outFile; -#endif -} AecmCore; - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_CreateCore() -// -// Allocates the memory needed by the AECM. The memory needs to be -// initialized separately using the WebRtcAecm_InitCore() function. -// Returns a pointer to the instance and a nullptr at failure. -AecmCore* WebRtcAecm_CreateCore(); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_InitCore(...) -// -// This function initializes the AECM instant created with -// WebRtcAecm_CreateCore() -// Input: -// - aecm : Pointer to the AECM instance -// - samplingFreq : Sampling Frequency -// -// Output: -// - aecm : Initialized instance -// -// Return value : 0 - Ok -// -1 - Error -// -int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_FreeCore(...) -// -// This function releases the memory allocated by WebRtcAecm_CreateCore() -// Input: -// - aecm : Pointer to the AECM instance -// -void WebRtcAecm_FreeCore(AecmCore* aecm); - -int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_InitEchoPathCore(...) -// -// This function resets the echo channel adaptation with the specified channel. -// Input: -// - aecm : Pointer to the AECM instance -// - echo_path : Pointer to the data that should initialize the echo -// path -// -// Output: -// - aecm : Initialized instance -// -void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_ProcessFrame(...) -// -// This function processes frames and sends blocks to -// WebRtcAecm_ProcessBlock(...) -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one frame of echo signal -// - nearendNoisy : In buffer containing one frame of nearend+echo signal -// without NS -// - nearendClean : In buffer containing one frame of nearend+echo signal -// with NS -// -// Output: -// - out : Out buffer, one frame of nearend signal : -// -// -int WebRtcAecm_ProcessFrame(AecmCore* aecm, - const int16_t* farend, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* out); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_ProcessBlock(...) -// -// This function is called for every block within one frame -// This function is called by WebRtcAecm_ProcessFrame(...) -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one block of echo signal -// - nearendNoisy : In buffer containing one frame of nearend+echo signal -// without NS -// - nearendClean : In buffer containing one frame of nearend+echo signal -// with NS -// -// Output: -// - out : Out buffer, one block of nearend signal : -// -// -int WebRtcAecm_ProcessBlock(AecmCore* aecm, - const int16_t* farend, - const int16_t* nearendNoisy, - const int16_t* noisyClean, - int16_t* out); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_BufferFarFrame() -// -// Inserts a frame of data into farend buffer. -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one frame of farend signal -// - farLen : Length of frame -// -void WebRtcAecm_BufferFarFrame(AecmCore* const aecm, - const int16_t* const farend, - int farLen); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_FetchFarFrame() -// -// Read the farend buffer to account for known delay -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one frame of farend signal -// - farLen : Length of frame -// - knownDelay : known delay -// -void WebRtcAecm_FetchFarFrame(AecmCore* const aecm, - int16_t* const farend, - int farLen, - int knownDelay); - -// All the functions below are intended to be private - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_UpdateFarHistory() -// -// Moves the pointer to the next entry and inserts `far_spectrum` and -// corresponding Q-domain in its buffer. -// -// Inputs: -// - self : Pointer to the delay estimation instance -// - far_spectrum : Pointer to the far end spectrum -// - far_q : Q-domain of far end spectrum -// -void WebRtcAecm_UpdateFarHistory(AecmCore* self, - uint16_t* far_spectrum, - int far_q); - -//////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_AlignedFarend() -// -// Returns a pointer to the far end spectrum aligned to current near end -// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been -// called before AlignedFarend(...). Otherwise, you get the pointer to the -// previous frame. The memory is only valid until the next call of -// WebRtc_DelayEstimatorProcessFix(...). -// -// Inputs: -// - self : Pointer to the AECM instance. -// - delay : Current delay estimate. -// -// Output: -// - far_q : The Q-domain of the aligned far end spectrum -// -// Return value: -// - far_spectrum : Pointer to the aligned far end spectrum -// NULL - Error -// -const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self, int* far_q, int delay); - -/////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_CalcSuppressionGain() -// -// This function calculates the suppression gain that is used in the -// Wiener filter. -// -// Inputs: -// - aecm : Pointer to the AECM instance. -// -// Return value: -// - supGain : Suppression gain with which to scale the noise -// level (Q14). -// -int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm); - -/////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_CalcEnergies() -// -// This function calculates the log of energies for nearend, farend and -// estimated echoes. There is also an update of energy decision levels, -// i.e. internal VAD. -// -// Inputs: -// - aecm : Pointer to the AECM instance. -// - far_spectrum : Pointer to farend spectrum. -// - far_q : Q-domain of farend spectrum. -// - nearEner : Near end energy for current block in -// Q(aecm->dfaQDomain). -// -// Output: -// - echoEst : Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16). -// -void WebRtcAecm_CalcEnergies(AecmCore* aecm, - const uint16_t* far_spectrum, - int16_t far_q, - uint32_t nearEner, - int32_t* echoEst); - -/////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_CalcStepSize() -// -// This function calculates the step size used in channel estimation -// -// Inputs: -// - aecm : Pointer to the AECM instance. -// -// Return value: -// - mu : Stepsize in log2(), i.e. number of shifts. -// -int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm); - -/////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_UpdateChannel(...) -// -// This function performs channel estimation. -// NLMS and decision on channel storage. -// -// Inputs: -// - aecm : Pointer to the AECM instance. -// - far_spectrum : Absolute value of the farend signal in Q(far_q) -// - far_q : Q-domain of the farend signal -// - dfa : Absolute value of the nearend signal -// (Q[aecm->dfaQDomain]) -// - mu : NLMS step size. -// Input/Output: -// - echoEst : Estimated echo in Q(far_q+RESOLUTION_CHANNEL16). -// -void WebRtcAecm_UpdateChannel(AecmCore* aecm, - const uint16_t* far_spectrum, - int16_t far_q, - const uint16_t* const dfa, - int16_t mu, - int32_t* echoEst); - -extern const int16_t WebRtcAecm_kCosTable[]; -extern const int16_t WebRtcAecm_kSinTable[]; - -/////////////////////////////////////////////////////////////////////////////// -// Some function pointers, for internal functions shared by ARM NEON and -// generic C code. -// -typedef void (*CalcLinearEnergies)(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echoEst, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored); -extern CalcLinearEnergies WebRtcAecm_CalcLinearEnergies; - -typedef void (*StoreAdaptiveChannel)(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est); -extern StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel; - -typedef void (*ResetAdaptiveChannel)(AecmCore* aecm); -extern ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel; - -// For the above function pointers, functions for generic platforms are declared -// and defined as static in file aecm_core.c, while those for ARM Neon platforms -// are declared below and defined in file aecm_core_neon.c. -#if defined(WEBRTC_HAS_NEON) -void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored); - -void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est); - -void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore* aecm); -#endif - -#if defined(MIPS32_LE) -void WebRtcAecm_CalcLinearEnergies_mips(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored); -#if defined(MIPS_DSP_R1_LE) -void WebRtcAecm_StoreAdaptiveChannel_mips(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est); - -void WebRtcAecm_ResetAdaptiveChannel_mips(AecmCore* aecm); -#endif -#endif - -} // namespace webrtc - -#endif diff --git a/modules/audio_processing/aecm/aecm_core_c.cc b/modules/audio_processing/aecm/aecm_core_c.cc deleted file mode 100644 index f1921b81658..00000000000 --- a/modules/audio_processing/aecm/aecm_core_c.cc +++ /dev/null @@ -1,671 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include - -#include "common_audio/signal_processing/include/signal_processing_library.h" -#include "common_audio/signal_processing/include/spl_inl.h" -#include "modules/audio_processing/aecm/aecm_core.h" -#include "modules/audio_processing/aecm/aecm_defines.h" -#include "modules/audio_processing/aecm/echo_control_mobile.h" -#include "modules/audio_processing/utility/delay_estimator_wrapper.h" -#include "rtc_base/checks.h" -#include "rtc_base/numerics/safe_conversions.h" -#include "rtc_base/sanitizer.h" - -extern "C" { -#include "common_audio/signal_processing/include/real_fft.h" -} - -namespace webrtc { - -namespace { - -// Square root of Hanning window in Q14. -const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = { - 0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, 3562, 3951, - 4337, 4720, 5101, 5478, 5853, 6224, 6591, 6954, 7313, 7668, 8019, - 8364, 8705, 9040, 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, - 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, 13773, 13985, 14189, - 14384, 14571, 14749, 14918, 15079, 15231, 15373, 15506, 15631, 15746, 15851, - 15947, 16034, 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384}; - -#ifdef AECM_WITH_ABS_APPROX -// Q15 alpha = 0.99439986968132 const Factor for magnitude approximation -static const uint16_t kAlpha1 = 32584; -// Q15 beta = 0.12967166976970 const Factor for magnitude approximation -static const uint16_t kBeta1 = 4249; -// Q15 alpha = 0.94234827210087 const Factor for magnitude approximation -static const uint16_t kAlpha2 = 30879; -// Q15 beta = 0.33787806009150 const Factor for magnitude approximation -static const uint16_t kBeta2 = 11072; -// Q15 alpha = 0.82247698684306 const Factor for magnitude approximation -static const uint16_t kAlpha3 = 26951; -// Q15 beta = 0.57762063060713 const Factor for magnitude approximation -static const uint16_t kBeta3 = 18927; -#endif - -const int16_t kNoiseEstQDomain = 15; -const int16_t kNoiseEstIncCount = 5; - -void ComfortNoise(AecmCore* aecm, - const uint16_t* dfa, - ComplexInt16* out, - const int16_t* lambda) { - int16_t i; - int16_t tmp16; - int32_t tmp32; - - int16_t randW16[PART_LEN]; - int16_t uReal[PART_LEN1]; - int16_t uImag[PART_LEN1]; - int32_t outLShift32; - int16_t noiseRShift16[PART_LEN1]; - - int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain; - int16_t minTrackShift; - - RTC_DCHECK_GE(shiftFromNearToNoise, 0); - RTC_DCHECK_LT(shiftFromNearToNoise, 16); - - if (aecm->noiseEstCtr < 100) { - // Track the minimum more quickly initially. - aecm->noiseEstCtr++; - minTrackShift = 6; - } else { - minTrackShift = 9; - } - - // Estimate noise power. - for (i = 0; i < PART_LEN1; i++) { - // Shift to the noise domain. - tmp32 = (int32_t)dfa[i]; - outLShift32 = tmp32 << shiftFromNearToNoise; - - if (outLShift32 < aecm->noiseEst[i]) { - // Reset "too low" counter - aecm->noiseEstTooLowCtr[i] = 0; - // Track the minimum. - if (aecm->noiseEst[i] < (1 << minTrackShift)) { - // For small values, decrease noiseEst[i] every - // `kNoiseEstIncCount` block. The regular approach below can not - // go further down due to truncation. - aecm->noiseEstTooHighCtr[i]++; - if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount) { - aecm->noiseEst[i]--; - aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter - } - } else { - aecm->noiseEst[i] -= - ((aecm->noiseEst[i] - outLShift32) >> minTrackShift); - } - } else { - // Reset "too high" counter - aecm->noiseEstTooHighCtr[i] = 0; - // Ramp slowly upwards until we hit the minimum again. - if ((aecm->noiseEst[i] >> 19) > 0) { - // Avoid overflow. - // Multiplication with 2049 will cause wrap around. Scale - // down first and then multiply - aecm->noiseEst[i] >>= 11; - aecm->noiseEst[i] *= 2049; - } else if ((aecm->noiseEst[i] >> 11) > 0) { - // Large enough for relative increase - aecm->noiseEst[i] *= 2049; - aecm->noiseEst[i] >>= 11; - } else { - // Make incremental increases based on size every - // `kNoiseEstIncCount` block - aecm->noiseEstTooLowCtr[i]++; - if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount) { - aecm->noiseEst[i] += (aecm->noiseEst[i] >> 9) + 1; - aecm->noiseEstTooLowCtr[i] = 0; // Reset counter - } - } - } - } - - for (i = 0; i < PART_LEN1; i++) { - tmp32 = aecm->noiseEst[i] >> shiftFromNearToNoise; - if (tmp32 > 32767) { - tmp32 = 32767; - aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; - } - noiseRShift16[i] = (int16_t)tmp32; - - tmp16 = ONE_Q14 - lambda[i]; - noiseRShift16[i] = (int16_t)((tmp16 * noiseRShift16[i]) >> 14); - } - - // Generate a uniform random array on [0 2^15-1]. - WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); - - // Generate noise according to estimated energy. - uReal[0] = 0; // Reject LF noise. - uImag[0] = 0; - for (i = 1; i < PART_LEN1; i++) { - // Get a random index for the cos and sin tables over [0 359]. - tmp16 = (int16_t)((359 * randW16[i - 1]) >> 15); - - // Tables are in Q13. - uReal[i] = - (int16_t)((noiseRShift16[i] * WebRtcAecm_kCosTable[tmp16]) >> 13); - uImag[i] = - (int16_t)((-noiseRShift16[i] * WebRtcAecm_kSinTable[tmp16]) >> 13); - } - uImag[PART_LEN] = 0; - - for (i = 0; i < PART_LEN1; i++) { - out[i].real = WebRtcSpl_AddSatW16(out[i].real, uReal[i]); - out[i].imag = WebRtcSpl_AddSatW16(out[i].imag, uImag[i]); - } -} - -void WindowAndFFT(AecmCore* aecm, - int16_t* fft, - const int16_t* time_signal, - ComplexInt16* freq_signal, - int time_signal_scaling) { - int i = 0; - - // FFT of signal - for (i = 0; i < PART_LEN; i++) { - // Window time domain signal and insert into real part of - // transformation array `fft` - int16_t scaled_time_signal = time_signal[i] * (1 << time_signal_scaling); - fft[i] = (int16_t)((scaled_time_signal * WebRtcAecm_kSqrtHanning[i]) >> 14); - scaled_time_signal = time_signal[i + PART_LEN] * (1 << time_signal_scaling); - fft[PART_LEN + i] = (int16_t)((scaled_time_signal * - WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> - 14); - } - - // Do forward FFT, then take only the first PART_LEN complex samples, - // and change signs of the imaginary parts. - WebRtcSpl_RealForwardFFT(aecm->real_fft, fft, (int16_t*)freq_signal); - for (i = 0; i < PART_LEN; i++) { - freq_signal[i].imag = -freq_signal[i].imag; - } -} - -void InverseFFTAndWindow(AecmCore* aecm, - int16_t* fft, - ComplexInt16* efw, - int16_t* output, - const int16_t* nearendClean) { - int i, j, outCFFT; - int32_t tmp32no1; - // Reuse `efw` for the inverse FFT output after transferring - // the contents to `fft`. - int16_t* ifft_out = (int16_t*)efw; - - // Synthesis - for (i = 1, j = 2; i < PART_LEN; i += 1, j += 2) { - fft[j] = efw[i].real; - fft[j + 1] = -efw[i].imag; - } - fft[0] = efw[0].real; - fft[1] = -efw[0].imag; - - fft[PART_LEN2] = efw[PART_LEN].real; - fft[PART_LEN2 + 1] = -efw[PART_LEN].imag; - - // Inverse FFT. Keep outCFFT to scale the samples in the next block. - outCFFT = WebRtcSpl_RealInverseFFT(aecm->real_fft, fft, ifft_out); - for (i = 0; i < PART_LEN; i++) { - ifft_out[i] = (int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - ifft_out[i], WebRtcAecm_kSqrtHanning[i], 14); - tmp32no1 = WEBRTC_SPL_SHIFT_W32((int32_t)ifft_out[i], - outCFFT - aecm->dfaCleanQDomain); - output[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, - tmp32no1 + aecm->outBuf[i], - WEBRTC_SPL_WORD16_MIN); - - tmp32no1 = - (ifft_out[PART_LEN + i] * WebRtcAecm_kSqrtHanning[PART_LEN - i]) >> 14; - tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, outCFFT - aecm->dfaCleanQDomain); - aecm->outBuf[i] = (int16_t)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, tmp32no1, - WEBRTC_SPL_WORD16_MIN); - } - - // Copy the current block to the old position - // (aecm->outBuf is shifted elsewhere) - memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(int16_t) * PART_LEN); - memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, - sizeof(int16_t) * PART_LEN); - if (nearendClean != nullptr) { - memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, - sizeof(int16_t) * PART_LEN); - } -} - -// Transforms a time domain signal into the frequency domain, outputting the -// complex valued signal, absolute value and sum of absolute values. -// -// time_signal [in] Pointer to time domain signal -// freq_signal_real [out] Pointer to real part of frequency domain array -// freq_signal_imag [out] Pointer to imaginary part of frequency domain -// array -// freq_signal_abs [out] Pointer to absolute value of frequency domain -// array -// freq_signal_sum_abs [out] Pointer to the sum of all absolute values in -// the frequency domain array -// return value The Q-domain of current frequency values -// -int TimeToFrequencyDomain(AecmCore* aecm, - const int16_t* time_signal, - ComplexInt16* freq_signal, - uint16_t* freq_signal_abs, - uint32_t* freq_signal_sum_abs) { - int i = 0; - int time_signal_scaling = 0; - - int32_t tmp32no1 = 0; - int32_t tmp32no2 = 0; - - // In fft_buf, +16 for 32-byte alignment. - int16_t fft_buf[PART_LEN4 + 16]; - int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); - - int16_t tmp16no1; -#ifndef WEBRTC_ARCH_ARM_V7 - int16_t tmp16no2; -#endif -#ifdef AECM_WITH_ABS_APPROX - int16_t max_value = 0; - int16_t min_value = 0; - uint16_t alpha = 0; - uint16_t beta = 0; -#endif - -#ifdef AECM_DYNAMIC_Q - tmp16no1 = WebRtcSpl_MaxAbsValueW16(time_signal, PART_LEN2); - time_signal_scaling = WebRtcSpl_NormW16(tmp16no1); -#endif - - WindowAndFFT(aecm, fft, time_signal, freq_signal, time_signal_scaling); - - // Extract imaginary and real part, calculate the magnitude for - // all frequency bins - freq_signal[0].imag = 0; - freq_signal[PART_LEN].imag = 0; - freq_signal_abs[0] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[0].real); - freq_signal_abs[PART_LEN] = - (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[PART_LEN].real); - (*freq_signal_sum_abs) = - (uint32_t)(freq_signal_abs[0]) + (uint32_t)(freq_signal_abs[PART_LEN]); - - for (i = 1; i < PART_LEN; i++) { - if (freq_signal[i].real == 0) { - freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - } else if (freq_signal[i].imag == 0) { - freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].real); - } else { - // Approximation for magnitude of complex fft output - // magn = sqrt(real^2 + imag^2) - // magn ~= alpha * max(`imag`,`real`) + beta * min(`imag`,`real`) - // - // The parameters alpha and beta are stored in Q15 - -#ifdef AECM_WITH_ABS_APPROX - tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); - tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - - if (tmp16no1 > tmp16no2) { - max_value = tmp16no1; - min_value = tmp16no2; - } else { - max_value = tmp16no2; - min_value = tmp16no1; - } - - // Magnitude in Q(-6) - if ((max_value >> 2) > min_value) { - alpha = kAlpha1; - beta = kBeta1; - } else if ((max_value >> 1) > min_value) { - alpha = kAlpha2; - beta = kBeta2; - } else { - alpha = kAlpha3; - beta = kBeta3; - } - tmp16no1 = (int16_t)((max_value * alpha) >> 15); - tmp16no2 = (int16_t)((min_value * beta) >> 15); - freq_signal_abs[i] = (uint16_t)tmp16no1 + (uint16_t)tmp16no2; -#else -#ifdef WEBRTC_ARCH_ARM_V7 - __asm __volatile( - "smulbb %[tmp32no1], %[real], %[real]\n\t" - "smlabb %[tmp32no2], %[imag], %[imag], %[tmp32no1]\n\t" - : [tmp32no1] "+&r"(tmp32no1), [tmp32no2] "=r"(tmp32no2) - : [real] "r"(freq_signal[i].real), [imag] "r"(freq_signal[i].imag)); -#else - tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); - tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - tmp32no1 = tmp16no1 * tmp16no1; - tmp32no2 = tmp16no2 * tmp16no2; - tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); -#endif // WEBRTC_ARCH_ARM_V7 - tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); - - freq_signal_abs[i] = (uint16_t)tmp32no1; -#endif // AECM_WITH_ABS_APPROX - } - (*freq_signal_sum_abs) += (uint32_t)freq_signal_abs[i]; - } - - return time_signal_scaling; -} - -} // namespace - -int RTC_NO_SANITIZE("signed-integer-overflow") // bugs.webrtc.org/8200 - WebRtcAecm_ProcessBlock(AecmCore* aecm, - const int16_t* farend, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* output) { - int i; - - uint32_t xfaSum; - uint32_t dfaNoisySum; - uint32_t dfaCleanSum; - uint32_t echoEst32Gained; - uint32_t tmpU32; - - int32_t tmp32no1; - - uint16_t xfa[PART_LEN1]; - uint16_t dfaNoisy[PART_LEN1]; - uint16_t dfaClean[PART_LEN1]; - uint16_t* ptrDfaClean = dfaClean; - const uint16_t* far_spectrum_ptr = nullptr; - - // 32 byte aligned buffers (with +8 or +16). - // TODO(kma): define fft with ComplexInt16. - int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe. - int32_t echoEst32_buf[PART_LEN1 + 8]; - int32_t dfw_buf[PART_LEN2 + 8]; - int32_t efw_buf[PART_LEN2 + 8]; - - int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); - int32_t* echoEst32 = (int32_t*)(((uintptr_t)echoEst32_buf + 31) & ~31); - ComplexInt16* dfw = (ComplexInt16*)(((uintptr_t)dfw_buf + 31) & ~31); - ComplexInt16* efw = (ComplexInt16*)(((uintptr_t)efw_buf + 31) & ~31); - - int16_t hnl[PART_LEN1]; - int16_t numPosCoef = 0; - int16_t nlpGain = ONE_Q14; - int delay; - int16_t tmp16no1; - int16_t tmp16no2; - int16_t mu; - int16_t supGain; - int16_t zeros32, zeros16; - int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf; - int far_q; - int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff; - - const int kMinPrefBand = 4; - const int kMaxPrefBand = 24; - int32_t avgHnl32 = 0; - - // Determine startup state. There are three states: - // (0) the first CONV_LEN blocks - // (1) another CONV_LEN blocks - // (2) the rest - - if (aecm->startupState < 2) { - aecm->startupState = - (aecm->totCount >= CONV_LEN) + (aecm->totCount >= CONV_LEN2); - } - // END: Determine startup state - - // Buffer near and far end signals - memcpy(aecm->xBuf + PART_LEN, farend, sizeof(int16_t) * PART_LEN); - memcpy(aecm->dBufNoisy + PART_LEN, nearendNoisy, sizeof(int16_t) * PART_LEN); - if (nearendClean != nullptr) { - memcpy(aecm->dBufClean + PART_LEN, nearendClean, - sizeof(int16_t) * PART_LEN); - } - - // Transform far end signal from time domain to frequency domain. - far_q = TimeToFrequencyDomain(aecm, aecm->xBuf, dfw, xfa, &xfaSum); - - // Transform noisy near end signal from time domain to frequency domain. - zerosDBufNoisy = - TimeToFrequencyDomain(aecm, aecm->dBufNoisy, dfw, dfaNoisy, &dfaNoisySum); - aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain; - aecm->dfaNoisyQDomain = (int16_t)zerosDBufNoisy; - - if (nearendClean == nullptr) { - ptrDfaClean = dfaNoisy; - aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld; - aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain; - dfaCleanSum = dfaNoisySum; - } else { - // Transform clean near end signal from time domain to frequency domain. - zerosDBufClean = TimeToFrequencyDomain(aecm, aecm->dBufClean, dfw, dfaClean, - &dfaCleanSum); - aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain; - aecm->dfaCleanQDomain = (int16_t)zerosDBufClean; - } - - // Get the delay - // Save far-end history and estimate delay - WebRtcAecm_UpdateFarHistory(aecm, xfa, far_q); - if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend, xfa, PART_LEN1, - far_q) == -1) { - return -1; - } - delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator, dfaNoisy, - PART_LEN1, zerosDBufNoisy); - if (delay == -1) { - return -1; - } else if (delay == -2) { - // If the delay is unknown, we assume zero. - // NOTE: this will have to be adjusted if we ever add lookahead. - delay = 0; - } - - if (aecm->fixedDelay >= 0) { - // Use fixed delay - delay = aecm->fixedDelay; - } - - // Get aligned far end spectrum - far_spectrum_ptr = WebRtcAecm_AlignedFarend(aecm, &far_q, delay); - zerosXBuf = (int16_t)far_q; - if (far_spectrum_ptr == nullptr) { - return -1; - } - - // Calculate log(energy) and update energy threshold levels - WebRtcAecm_CalcEnergies(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisySum, - echoEst32); - - // Calculate stepsize - mu = WebRtcAecm_CalcStepSize(aecm); - - // Update counters - aecm->totCount++; - - // This is the channel estimation algorithm. - // It is base on NLMS but has a variable step length, - // which was calculated above. - WebRtcAecm_UpdateChannel(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisy, mu, - echoEst32); - supGain = WebRtcAecm_CalcSuppressionGain(aecm); - - // Calculate Wiener filter hnl[] - for (i = 0; i < PART_LEN1; i++) { - // Far end signal through channel estimate in Q8 - // How much can we shift right to preserve resolution - tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; - aecm->echoFilt[i] += dchecked_cast((int64_t{tmp32no1} * 50) >> 8); - - zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; - zeros16 = WebRtcSpl_NormW16(supGain) + 1; - if (zeros32 + zeros16 > 16) { - // Multiplication is safe - // Result in - // Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+ - // aecm->xfaQDomainBuf[diff]) - echoEst32Gained = - WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], (uint16_t)supGain); - resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - } else { - tmp16no1 = 17 - zeros32 - zeros16; - resolutionDiff = - 14 + tmp16no1 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - if (zeros32 > tmp16no1) { - echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], - supGain >> tmp16no1); - } else { - // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) - echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; - } - } - - zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); - RTC_DCHECK_GE(zeros16, 0); // `zeros16` is a norm, hence non-negative. - dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; - if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { - tmp16no1 = aecm->nearFilt[i] * (1 << zeros16); - qDomainDiff = zeros16 - dfa_clean_q_domain_diff; - tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; - } else { - tmp16no1 = dfa_clean_q_domain_diff < 0 - ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff - : aecm->nearFilt[i] * (1 << dfa_clean_q_domain_diff); - qDomainDiff = 0; - tmp16no2 = ptrDfaClean[i]; - } - tmp32no1 = (int32_t)(tmp16no2 - tmp16no1); - tmp16no2 = (int16_t)(tmp32no1 >> 4); - tmp16no2 += tmp16no1; - zeros16 = WebRtcSpl_NormW16(tmp16no2); - if ((tmp16no2) & (-qDomainDiff > zeros16)) { - aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; - } else { - aecm->nearFilt[i] = qDomainDiff < 0 ? tmp16no2 * (1 << -qDomainDiff) - : tmp16no2 >> qDomainDiff; - } - - // Wiener filter coefficients, resulting hnl in Q14 - if (echoEst32Gained == 0) { - hnl[i] = ONE_Q14; - } else if (aecm->nearFilt[i] == 0) { - hnl[i] = 0; - } else { - // Multiply the suppression gain - // Rounding - echoEst32Gained += (uint32_t)(aecm->nearFilt[i] >> 1); - tmpU32 = - WebRtcSpl_DivU32U16(echoEst32Gained, (uint16_t)aecm->nearFilt[i]); - - // Current resolution is - // Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN- max(0,17-zeros16- zeros32)) - // Make sure we are in Q14 - tmp32no1 = (int32_t)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff); - if (tmp32no1 > ONE_Q14) { - hnl[i] = 0; - } else if (tmp32no1 < 0) { - hnl[i] = ONE_Q14; - } else { - // 1-echoEst/dfa - hnl[i] = ONE_Q14 - (int16_t)tmp32no1; - if (hnl[i] < 0) { - hnl[i] = 0; - } - } - } - if (hnl[i]) { - numPosCoef++; - } - } - // Only in wideband. Prevent the gain in upper band from being larger than - // in lower band. - if (aecm->mult == 2) { - // TODO(bjornv): Investigate if the scaling of hnl[i] below can cause - // speech distortion in double-talk. - for (i = 0; i < PART_LEN1; i++) { - hnl[i] = (int16_t)((hnl[i] * hnl[i]) >> 14); - } - - for (i = kMinPrefBand; i <= kMaxPrefBand; i++) { - avgHnl32 += (int32_t)hnl[i]; - } - RTC_DCHECK_GT(kMaxPrefBand - kMinPrefBand + 1, 0); - avgHnl32 /= (kMaxPrefBand - kMinPrefBand + 1); - - for (i = kMaxPrefBand; i < PART_LEN1; i++) { - if (hnl[i] > (int16_t)avgHnl32) { - hnl[i] = (int16_t)avgHnl32; - } - } - } - - // Calculate NLP gain, result is in Q14 - if (aecm->nlpFlag) { - for (i = 0; i < PART_LEN1; i++) { - // Truncate values close to zero and one. - if (hnl[i] > NLP_COMP_HIGH) { - hnl[i] = ONE_Q14; - } else if (hnl[i] < NLP_COMP_LOW) { - hnl[i] = 0; - } - - // Remove outliers - if (numPosCoef < 3) { - nlpGain = 0; - } else { - nlpGain = ONE_Q14; - } - - // NLP - if ((hnl[i] == ONE_Q14) && (nlpGain == ONE_Q14)) { - hnl[i] = ONE_Q14; - } else { - hnl[i] = (int16_t)((hnl[i] * nlpGain) >> 14); - } - - // multiply with Wiener coefficients - efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, - hnl[i], 14)); - efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, - hnl[i], 14)); - } - } else { - // multiply with Wiener coefficients - for (i = 0; i < PART_LEN1; i++) { - efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, - hnl[i], 14)); - efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, - hnl[i], 14)); - } - } - - if (aecm->cngMode == AecmTrue) { - ComfortNoise(aecm, ptrDfaClean, efw, hnl); - } - - InverseFFTAndWindow(aecm, fft, efw, output, nearendClean); - - return 0; -} - -} // namespace webrtc diff --git a/modules/audio_processing/aecm/aecm_core_mips.cc b/modules/audio_processing/aecm/aecm_core_mips.cc deleted file mode 100644 index 2d008b0b015..00000000000 --- a/modules/audio_processing/aecm/aecm_core_mips.cc +++ /dev/null @@ -1,1655 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/aecm/aecm_core.h" -#include "modules/audio_processing/aecm/echo_control_mobile.h" -#include "modules/audio_processing/utility/delay_estimator_wrapper.h" -#include "rtc_base/checks.h" -#include "rtc_base/numerics/safe_conversions.h" - -namespace webrtc { - -namespace { - -static const ALIGN8_BEG int16_t WebRtcAecm_kSqrtHanning[] ALIGN8_END = { - 0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, 3562, 3951, - 4337, 4720, 5101, 5478, 5853, 6224, 6591, 6954, 7313, 7668, 8019, - 8364, 8705, 9040, 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, - 11795, 12068, 12335, 12594, 12845, 13089, 13325, 13553, 13773, 13985, 14189, - 14384, 14571, 14749, 14918, 15079, 15231, 15373, 15506, 15631, 15746, 15851, - 15947, 16034, 16111, 16179, 16237, 16286, 16325, 16354, 16373, 16384}; - -static const int16_t kNoiseEstQDomain = 15; -static const int16_t kNoiseEstIncCount = 5; - -static int16_t coefTable[] = { - 0, 4, 256, 260, 128, 132, 384, 388, 64, 68, 320, 324, 192, 196, 448, - 452, 32, 36, 288, 292, 160, 164, 416, 420, 96, 100, 352, 356, 224, 228, - 480, 484, 16, 20, 272, 276, 144, 148, 400, 404, 80, 84, 336, 340, 208, - 212, 464, 468, 48, 52, 304, 308, 176, 180, 432, 436, 112, 116, 368, 372, - 240, 244, 496, 500, 8, 12, 264, 268, 136, 140, 392, 396, 72, 76, 328, - 332, 200, 204, 456, 460, 40, 44, 296, 300, 168, 172, 424, 428, 104, 108, - 360, 364, 232, 236, 488, 492, 24, 28, 280, 284, 152, 156, 408, 412, 88, - 92, 344, 348, 216, 220, 472, 476, 56, 60, 312, 316, 184, 188, 440, 444, - 120, 124, 376, 380, 248, 252, 504, 508}; - -static int16_t coefTable_ifft[] = { - 0, 512, 256, 508, 128, 252, 384, 380, 64, 124, 320, 444, 192, 188, 448, - 316, 32, 60, 288, 476, 160, 220, 416, 348, 96, 92, 352, 412, 224, 156, - 480, 284, 16, 28, 272, 492, 144, 236, 400, 364, 80, 108, 336, 428, 208, - 172, 464, 300, 48, 44, 304, 460, 176, 204, 432, 332, 112, 76, 368, 396, - 240, 140, 496, 268, 8, 12, 264, 500, 136, 244, 392, 372, 72, 116, 328, - 436, 200, 180, 456, 308, 40, 52, 296, 468, 168, 212, 424, 340, 104, 84, - 360, 404, 232, 148, 488, 276, 24, 20, 280, 484, 152, 228, 408, 356, 88, - 100, 344, 420, 216, 164, 472, 292, 56, 36, 312, 452, 184, 196, 440, 324, - 120, 68, 376, 388, 248, 132, 504, 260}; - -} // namespace - -static void ComfortNoise(AecmCore* aecm, - const uint16_t* dfa, - ComplexInt16* out, - const int16_t* lambda); - -static void WindowAndFFT(AecmCore* aecm, - int16_t* fft, - const int16_t* time_signal, - ComplexInt16* freq_signal, - int time_signal_scaling) { - int i, j; - int32_t tmp1, tmp2, tmp3, tmp4; - int16_t* pfrfi; - ComplexInt16* pfreq_signal; - int16_t f_coef, s_coef; - int32_t load_ptr, store_ptr1, store_ptr2, shift, shift1; - int32_t hann, hann1, coefs; - - memset(fft, 0, sizeof(int16_t) * PART_LEN4); - - // FFT of signal - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[shift], %[time_signal_scaling], -14 \n\t" - "addiu %[i], $zero, 64 \n\t" - "addiu %[load_ptr], %[time_signal], 0 \n\t" - "addiu %[hann], %[hanning], 0 \n\t" - "addiu %[hann1], %[hanning], 128 \n\t" - "addiu %[coefs], %[coefTable], 0 \n\t" - "bltz %[shift], 2f \n\t" - " negu %[shift1], %[shift] \n\t" - "1: " - "\n\t" - "lh %[tmp1], 0(%[load_ptr]) \n\t" - "lh %[tmp2], 0(%[hann]) \n\t" - "lh %[tmp3], 128(%[load_ptr]) \n\t" - "lh %[tmp4], 0(%[hann1]) \n\t" - "addiu %[i], %[i], -1 \n\t" - "mul %[tmp1], %[tmp1], %[tmp2] \n\t" - "mul %[tmp3], %[tmp3], %[tmp4] \n\t" - "lh %[f_coef], 0(%[coefs]) \n\t" - "lh %[s_coef], 2(%[coefs]) \n\t" - "addiu %[load_ptr], %[load_ptr], 2 \n\t" - "addiu %[hann], %[hann], 2 \n\t" - "addiu %[hann1], %[hann1], -2 \n\t" - "addu %[store_ptr1], %[fft], %[f_coef] \n\t" - "addu %[store_ptr2], %[fft], %[s_coef] \n\t" - "sllv %[tmp1], %[tmp1], %[shift] \n\t" - "sllv %[tmp3], %[tmp3], %[shift] \n\t" - "sh %[tmp1], 0(%[store_ptr1]) \n\t" - "sh %[tmp3], 0(%[store_ptr2]) \n\t" - "bgtz %[i], 1b \n\t" - " addiu %[coefs], %[coefs], 4 \n\t" - "b 3f \n\t" - " nop \n\t" - "2: " - "\n\t" - "lh %[tmp1], 0(%[load_ptr]) \n\t" - "lh %[tmp2], 0(%[hann]) \n\t" - "lh %[tmp3], 128(%[load_ptr]) \n\t" - "lh %[tmp4], 0(%[hann1]) \n\t" - "addiu %[i], %[i], -1 \n\t" - "mul %[tmp1], %[tmp1], %[tmp2] \n\t" - "mul %[tmp3], %[tmp3], %[tmp4] \n\t" - "lh %[f_coef], 0(%[coefs]) \n\t" - "lh %[s_coef], 2(%[coefs]) \n\t" - "addiu %[load_ptr], %[load_ptr], 2 \n\t" - "addiu %[hann], %[hann], 2 \n\t" - "addiu %[hann1], %[hann1], -2 \n\t" - "addu %[store_ptr1], %[fft], %[f_coef] \n\t" - "addu %[store_ptr2], %[fft], %[s_coef] \n\t" - "srav %[tmp1], %[tmp1], %[shift1] \n\t" - "srav %[tmp3], %[tmp3], %[shift1] \n\t" - "sh %[tmp1], 0(%[store_ptr1]) \n\t" - "sh %[tmp3], 0(%[store_ptr2]) \n\t" - "bgtz %[i], 2b \n\t" - " addiu %[coefs], %[coefs], 4 \n\t" - "3: " - "\n\t" - ".set pop \n\t" - : [load_ptr] "=&r"(load_ptr), [shift] "=&r"(shift), [hann] "=&r"(hann), - [hann1] "=&r"(hann1), [shift1] "=&r"(shift1), [coefs] "=&r"(coefs), - [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [tmp3] "=&r"(tmp3), - [tmp4] "=&r"(tmp4), [i] "=&r"(i), [f_coef] "=&r"(f_coef), - [s_coef] "=&r"(s_coef), [store_ptr1] "=&r"(store_ptr1), - [store_ptr2] "=&r"(store_ptr2) - : [time_signal] "r"(time_signal), [coefTable] "r"(coefTable), - [time_signal_scaling] "r"(time_signal_scaling), - [hanning] "r"(WebRtcAecm_kSqrtHanning), [fft] "r"(fft) - : "memory", "hi", "lo"); - - WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1); - pfrfi = fft; - pfreq_signal = freq_signal; - - __asm __volatile( - ".set push " - "\n\t" - ".set noreorder " - "\n\t" - "addiu %[j], $zero, 128 " - "\n\t" - "1: " - "\n\t" - "lh %[tmp1], 0(%[pfrfi]) " - "\n\t" - "lh %[tmp2], 2(%[pfrfi]) " - "\n\t" - "lh %[tmp3], 4(%[pfrfi]) " - "\n\t" - "lh %[tmp4], 6(%[pfrfi]) " - "\n\t" - "subu %[tmp2], $zero, %[tmp2] " - "\n\t" - "sh %[tmp1], 0(%[pfreq_signal]) " - "\n\t" - "sh %[tmp2], 2(%[pfreq_signal]) " - "\n\t" - "subu %[tmp4], $zero, %[tmp4] " - "\n\t" - "sh %[tmp3], 4(%[pfreq_signal]) " - "\n\t" - "sh %[tmp4], 6(%[pfreq_signal]) " - "\n\t" - "lh %[tmp1], 8(%[pfrfi]) " - "\n\t" - "lh %[tmp2], 10(%[pfrfi]) " - "\n\t" - "lh %[tmp3], 12(%[pfrfi]) " - "\n\t" - "lh %[tmp4], 14(%[pfrfi]) " - "\n\t" - "addiu %[j], %[j], -8 " - "\n\t" - "subu %[tmp2], $zero, %[tmp2] " - "\n\t" - "sh %[tmp1], 8(%[pfreq_signal]) " - "\n\t" - "sh %[tmp2], 10(%[pfreq_signal]) " - "\n\t" - "subu %[tmp4], $zero, %[tmp4] " - "\n\t" - "sh %[tmp3], 12(%[pfreq_signal]) " - "\n\t" - "sh %[tmp4], 14(%[pfreq_signal]) " - "\n\t" - "addiu %[pfreq_signal], %[pfreq_signal], 16 " - "\n\t" - "bgtz %[j], 1b " - "\n\t" - " addiu %[pfrfi], %[pfrfi], 16 " - "\n\t" - ".set pop " - "\n\t" - : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [tmp3] "=&r"(tmp3), - [j] "=&r"(j), [pfrfi] "+r"(pfrfi), [pfreq_signal] "+r"(pfreq_signal), - [tmp4] "=&r"(tmp4) - : - : "memory"); -} - -static void InverseFFTAndWindow(AecmCore* aecm, - int16_t* fft, - ComplexInt16* efw, - int16_t* output, - const int16_t* nearendClean) { - int i, outCFFT; - int32_t tmp1, tmp2, tmp3, tmp4, tmp_re, tmp_im; - int16_t* pcoefTable_ifft = coefTable_ifft; - int16_t* pfft = fft; - int16_t* ppfft = fft; - ComplexInt16* pefw = efw; - int32_t out_aecm; - int16_t* paecm_buf = aecm->outBuf; - const int16_t* p_kSqrtHanning = WebRtcAecm_kSqrtHanning; - const int16_t* pp_kSqrtHanning = &WebRtcAecm_kSqrtHanning[PART_LEN]; - int16_t* output1 = output; - - __asm __volatile( - ".set push " - "\n\t" - ".set noreorder " - "\n\t" - "addiu %[i], $zero, 64 " - "\n\t" - "1: " - "\n\t" - "lh %[tmp1], 0(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp2], 2(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp_re], 0(%[pefw]) " - "\n\t" - "lh %[tmp_im], 2(%[pefw]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp2] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp1] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "subu %[tmp_im], $zero, %[tmp_im] " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "lh %[tmp1], 4(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp2], 6(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp_re], 4(%[pefw]) " - "\n\t" - "lh %[tmp_im], 6(%[pefw]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp2] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp1] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "subu %[tmp_im], $zero, %[tmp_im] " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "lh %[tmp1], 8(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp2], 10(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp_re], 8(%[pefw]) " - "\n\t" - "lh %[tmp_im], 10(%[pefw]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp2] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp1] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "subu %[tmp_im], $zero, %[tmp_im] " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "lh %[tmp1], 12(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp2], 14(%[pcoefTable_ifft]) " - "\n\t" - "lh %[tmp_re], 12(%[pefw]) " - "\n\t" - "lh %[tmp_im], 14(%[pefw]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp2] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "addu %[pfft], %[fft], %[tmp1] " - "\n\t" - "sh %[tmp_re], 0(%[pfft]) " - "\n\t" - "subu %[tmp_im], $zero, %[tmp_im] " - "\n\t" - "sh %[tmp_im], 2(%[pfft]) " - "\n\t" - "addiu %[pcoefTable_ifft], %[pcoefTable_ifft], 16 " - "\n\t" - "addiu %[i], %[i], -4 " - "\n\t" - "bgtz %[i], 1b " - "\n\t" - " addiu %[pefw], %[pefw], 16 " - "\n\t" - ".set pop " - "\n\t" - : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [pfft] "+r"(pfft), [i] "=&r"(i), - [tmp_re] "=&r"(tmp_re), [tmp_im] "=&r"(tmp_im), [pefw] "+r"(pefw), - [pcoefTable_ifft] "+r"(pcoefTable_ifft), [fft] "+r"(fft) - : - : "memory"); - - fft[2] = efw[PART_LEN].real; - fft[3] = -efw[PART_LEN].imag; - - outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1); - pfft = fft; - - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[i], $zero, 128 \n\t" - "1: \n\t" - "lh %[tmp1], 0(%[ppfft]) \n\t" - "lh %[tmp2], 4(%[ppfft]) \n\t" - "lh %[tmp3], 8(%[ppfft]) \n\t" - "lh %[tmp4], 12(%[ppfft]) \n\t" - "addiu %[i], %[i], -4 \n\t" - "sh %[tmp1], 0(%[pfft]) \n\t" - "sh %[tmp2], 2(%[pfft]) \n\t" - "sh %[tmp3], 4(%[pfft]) \n\t" - "sh %[tmp4], 6(%[pfft]) \n\t" - "addiu %[ppfft], %[ppfft], 16 \n\t" - "bgtz %[i], 1b \n\t" - " addiu %[pfft], %[pfft], 8 \n\t" - ".set pop \n\t" - : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [pfft] "+r"(pfft), [i] "=&r"(i), - [tmp3] "=&r"(tmp3), [tmp4] "=&r"(tmp4), [ppfft] "+r"(ppfft) - : - : "memory"); - - pfft = fft; - out_aecm = (int32_t)(outCFFT - aecm->dfaCleanQDomain); - - __asm __volatile( - ".set push " - "\n\t" - ".set noreorder " - "\n\t" - "addiu %[i], $zero, 64 " - "\n\t" - "11: " - "\n\t" - "lh %[tmp1], 0(%[pfft]) " - "\n\t" - "lh %[tmp2], 0(%[p_kSqrtHanning]) " - "\n\t" - "addiu %[i], %[i], -2 " - "\n\t" - "mul %[tmp1], %[tmp1], %[tmp2] " - "\n\t" - "lh %[tmp3], 2(%[pfft]) " - "\n\t" - "lh %[tmp4], 2(%[p_kSqrtHanning]) " - "\n\t" - "mul %[tmp3], %[tmp3], %[tmp4] " - "\n\t" - "addiu %[tmp1], %[tmp1], 8192 " - "\n\t" - "sra %[tmp1], %[tmp1], 14 " - "\n\t" - "addiu %[tmp3], %[tmp3], 8192 " - "\n\t" - "sra %[tmp3], %[tmp3], 14 " - "\n\t" - "bgez %[out_aecm], 1f " - "\n\t" - " negu %[tmp2], %[out_aecm] " - "\n\t" - "srav %[tmp1], %[tmp1], %[tmp2] " - "\n\t" - "b 2f " - "\n\t" - " srav %[tmp3], %[tmp3], %[tmp2] " - "\n\t" - "1: " - "\n\t" - "sllv %[tmp1], %[tmp1], %[out_aecm] " - "\n\t" - "sllv %[tmp3], %[tmp3], %[out_aecm] " - "\n\t" - "2: " - "\n\t" - "lh %[tmp4], 0(%[paecm_buf]) " - "\n\t" - "lh %[tmp2], 2(%[paecm_buf]) " - "\n\t" - "addu %[tmp3], %[tmp3], %[tmp2] " - "\n\t" - "addu %[tmp1], %[tmp1], %[tmp4] " - "\n\t" -#if defined(MIPS_DSP_R1_LE) - "shll_s.w %[tmp1], %[tmp1], 16 " - "\n\t" - "sra %[tmp1], %[tmp1], 16 " - "\n\t" - "shll_s.w %[tmp3], %[tmp3], 16 " - "\n\t" - "sra %[tmp3], %[tmp3], 16 " - "\n\t" -#else // #if defined(MIPS_DSP_R1_LE) - "sra %[tmp4], %[tmp1], 31 " - "\n\t" - "sra %[tmp2], %[tmp1], 15 " - "\n\t" - "beq %[tmp4], %[tmp2], 3f " - "\n\t" - " ori %[tmp2], $zero, 0x7fff " - "\n\t" - "xor %[tmp1], %[tmp2], %[tmp4] " - "\n\t" - "3: " - "\n\t" - "sra %[tmp2], %[tmp3], 31 " - "\n\t" - "sra %[tmp4], %[tmp3], 15 " - "\n\t" - "beq %[tmp2], %[tmp4], 4f " - "\n\t" - " ori %[tmp4], $zero, 0x7fff " - "\n\t" - "xor %[tmp3], %[tmp4], %[tmp2] " - "\n\t" - "4: " - "\n\t" -#endif // #if defined(MIPS_DSP_R1_LE) - "sh %[tmp1], 0(%[pfft]) " - "\n\t" - "sh %[tmp1], 0(%[output1]) " - "\n\t" - "sh %[tmp3], 2(%[pfft]) " - "\n\t" - "sh %[tmp3], 2(%[output1]) " - "\n\t" - "lh %[tmp1], 128(%[pfft]) " - "\n\t" - "lh %[tmp2], 0(%[pp_kSqrtHanning]) " - "\n\t" - "mul %[tmp1], %[tmp1], %[tmp2] " - "\n\t" - "lh %[tmp3], 130(%[pfft]) " - "\n\t" - "lh %[tmp4], -2(%[pp_kSqrtHanning]) " - "\n\t" - "mul %[tmp3], %[tmp3], %[tmp4] " - "\n\t" - "sra %[tmp1], %[tmp1], 14 " - "\n\t" - "sra %[tmp3], %[tmp3], 14 " - "\n\t" - "bgez %[out_aecm], 5f " - "\n\t" - " negu %[tmp2], %[out_aecm] " - "\n\t" - "srav %[tmp3], %[tmp3], %[tmp2] " - "\n\t" - "b 6f " - "\n\t" - " srav %[tmp1], %[tmp1], %[tmp2] " - "\n\t" - "5: " - "\n\t" - "sllv %[tmp1], %[tmp1], %[out_aecm] " - "\n\t" - "sllv %[tmp3], %[tmp3], %[out_aecm] " - "\n\t" - "6: " - "\n\t" -#if defined(MIPS_DSP_R1_LE) - "shll_s.w %[tmp1], %[tmp1], 16 " - "\n\t" - "sra %[tmp1], %[tmp1], 16 " - "\n\t" - "shll_s.w %[tmp3], %[tmp3], 16 " - "\n\t" - "sra %[tmp3], %[tmp3], 16 " - "\n\t" -#else // #if defined(MIPS_DSP_R1_LE) - "sra %[tmp4], %[tmp1], 31 " - "\n\t" - "sra %[tmp2], %[tmp1], 15 " - "\n\t" - "beq %[tmp4], %[tmp2], 7f " - "\n\t" - " ori %[tmp2], $zero, 0x7fff " - "\n\t" - "xor %[tmp1], %[tmp2], %[tmp4] " - "\n\t" - "7: " - "\n\t" - "sra %[tmp2], %[tmp3], 31 " - "\n\t" - "sra %[tmp4], %[tmp3], 15 " - "\n\t" - "beq %[tmp2], %[tmp4], 8f " - "\n\t" - " ori %[tmp4], $zero, 0x7fff " - "\n\t" - "xor %[tmp3], %[tmp4], %[tmp2] " - "\n\t" - "8: " - "\n\t" -#endif // #if defined(MIPS_DSP_R1_LE) - "sh %[tmp1], 0(%[paecm_buf]) " - "\n\t" - "sh %[tmp3], 2(%[paecm_buf]) " - "\n\t" - "addiu %[output1], %[output1], 4 " - "\n\t" - "addiu %[paecm_buf], %[paecm_buf], 4 " - "\n\t" - "addiu %[pfft], %[pfft], 4 " - "\n\t" - "addiu %[p_kSqrtHanning], %[p_kSqrtHanning], 4 " - "\n\t" - "bgtz %[i], 11b " - "\n\t" - " addiu %[pp_kSqrtHanning], %[pp_kSqrtHanning], -4 " - "\n\t" - ".set pop " - "\n\t" - : [tmp1] "=&r"(tmp1), [tmp2] "=&r"(tmp2), [pfft] "+r"(pfft), - [output1] "+r"(output1), [tmp3] "=&r"(tmp3), [tmp4] "=&r"(tmp4), - [paecm_buf] "+r"(paecm_buf), [i] "=&r"(i), - [pp_kSqrtHanning] "+r"(pp_kSqrtHanning), - [p_kSqrtHanning] "+r"(p_kSqrtHanning) - : [out_aecm] "r"(out_aecm), [WebRtcAecm_kSqrtHanning] "r"( - WebRtcAecm_kSqrtHanning) - : "hi", "lo", "memory"); - - // Copy the current block to the old position - // (aecm->outBuf is shifted elsewhere) - memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(int16_t) * PART_LEN); - memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, - sizeof(int16_t) * PART_LEN); - if (nearendClean != NULL) { - memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, - sizeof(int16_t) * PART_LEN); - } -} - -void WebRtcAecm_CalcLinearEnergies_mips(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored) { - int i; - uint32_t par1 = (*far_energy); - uint32_t par2 = (*echo_energy_adapt); - uint32_t par3 = (*echo_energy_stored); - int16_t* ch_stored_p = &(aecm->channelStored[0]); - int16_t* ch_adapt_p = &(aecm->channelAdapt16[0]); - uint16_t* spectrum_p = (uint16_t*)(&(far_spectrum[0])); - int32_t* echo_p = &(echo_est[0]); - int32_t temp0, stored0, echo0, adept0, spectrum0; - int32_t stored1, adept1, spectrum1, echo1, temp1; - - // Get energy for the delayed far end signal and estimated - // echo using both stored and adapted channels. - for (i = 0; i < PART_LEN; i += 4) { - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "lh %[stored0], 0(%[ch_stored_p]) \n\t" - "lhu %[adept0], 0(%[ch_adapt_p]) \n\t" - "lhu %[spectrum0], 0(%[spectrum_p]) \n\t" - "lh %[stored1], 2(%[ch_stored_p]) \n\t" - "lhu %[adept1], 2(%[ch_adapt_p]) \n\t" - "lhu %[spectrum1], 2(%[spectrum_p]) \n\t" - "mul %[echo0], %[stored0], %[spectrum0] \n\t" - "mul %[temp0], %[adept0], %[spectrum0] \n\t" - "mul %[echo1], %[stored1], %[spectrum1] \n\t" - "mul %[temp1], %[adept1], %[spectrum1] \n\t" - "addu %[par1], %[par1], %[spectrum0] \n\t" - "addu %[par1], %[par1], %[spectrum1] \n\t" - "addiu %[echo_p], %[echo_p], 16 \n\t" - "addu %[par3], %[par3], %[echo0] \n\t" - "addu %[par2], %[par2], %[temp0] \n\t" - "addu %[par3], %[par3], %[echo1] \n\t" - "addu %[par2], %[par2], %[temp1] \n\t" - "usw %[echo0], -16(%[echo_p]) \n\t" - "usw %[echo1], -12(%[echo_p]) \n\t" - "lh %[stored0], 4(%[ch_stored_p]) \n\t" - "lhu %[adept0], 4(%[ch_adapt_p]) \n\t" - "lhu %[spectrum0], 4(%[spectrum_p]) \n\t" - "lh %[stored1], 6(%[ch_stored_p]) \n\t" - "lhu %[adept1], 6(%[ch_adapt_p]) \n\t" - "lhu %[spectrum1], 6(%[spectrum_p]) \n\t" - "mul %[echo0], %[stored0], %[spectrum0] \n\t" - "mul %[temp0], %[adept0], %[spectrum0] \n\t" - "mul %[echo1], %[stored1], %[spectrum1] \n\t" - "mul %[temp1], %[adept1], %[spectrum1] \n\t" - "addu %[par1], %[par1], %[spectrum0] \n\t" - "addu %[par1], %[par1], %[spectrum1] \n\t" - "addiu %[ch_stored_p], %[ch_stored_p], 8 \n\t" - "addiu %[ch_adapt_p], %[ch_adapt_p], 8 \n\t" - "addiu %[spectrum_p], %[spectrum_p], 8 \n\t" - "addu %[par3], %[par3], %[echo0] \n\t" - "addu %[par2], %[par2], %[temp0] \n\t" - "addu %[par3], %[par3], %[echo1] \n\t" - "addu %[par2], %[par2], %[temp1] \n\t" - "usw %[echo0], -8(%[echo_p]) \n\t" - "usw %[echo1], -4(%[echo_p]) \n\t" - ".set pop \n\t" - : [temp0] "=&r"(temp0), [stored0] "=&r"(stored0), - [adept0] "=&r"(adept0), [spectrum0] "=&r"(spectrum0), - [echo0] "=&r"(echo0), [echo_p] "+r"(echo_p), [par3] "+r"(par3), - [par1] "+r"(par1), [par2] "+r"(par2), [stored1] "=&r"(stored1), - [adept1] "=&r"(adept1), [echo1] "=&r"(echo1), - [spectrum1] "=&r"(spectrum1), [temp1] "=&r"(temp1), - [ch_stored_p] "+r"(ch_stored_p), [ch_adapt_p] "+r"(ch_adapt_p), - [spectrum_p] "+r"(spectrum_p) - : - : "hi", "lo", "memory"); - } - - echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], - far_spectrum[PART_LEN]); - par1 += (uint32_t)(far_spectrum[PART_LEN]); - par2 += aecm->channelAdapt16[PART_LEN] * far_spectrum[PART_LEN]; - par3 += (uint32_t)echo_est[PART_LEN]; - - (*far_energy) = par1; - (*echo_energy_adapt) = par2; - (*echo_energy_stored) = par3; -} - -#if defined(MIPS_DSP_R1_LE) -void WebRtcAecm_StoreAdaptiveChannel_mips(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est) { - int i; - int16_t* temp1; - uint16_t* temp8; - int32_t temp0, temp2, temp3, temp4, temp5, temp6; - int32_t* temp7 = &(echo_est[0]); - temp1 = &(aecm->channelStored[0]); - temp8 = (uint16_t*)(&far_spectrum[0]); - - // During startup we store the channel every block. - memcpy(aecm->channelStored, aecm->channelAdapt16, - sizeof(int16_t) * PART_LEN1); - // Recalculate echo estimate - for (i = 0; i < PART_LEN; i += 4) { - __asm __volatile( - "ulw %[temp0], 0(%[temp8]) \n\t" - "ulw %[temp2], 0(%[temp1]) \n\t" - "ulw %[temp4], 4(%[temp8]) \n\t" - "ulw %[temp5], 4(%[temp1]) \n\t" - "muleq_s.w.phl %[temp3], %[temp2], %[temp0] \n\t" - "muleq_s.w.phr %[temp0], %[temp2], %[temp0] \n\t" - "muleq_s.w.phl %[temp6], %[temp5], %[temp4] \n\t" - "muleq_s.w.phr %[temp4], %[temp5], %[temp4] \n\t" - "addiu %[temp7], %[temp7], 16 \n\t" - "addiu %[temp1], %[temp1], 8 \n\t" - "addiu %[temp8], %[temp8], 8 \n\t" - "sra %[temp3], %[temp3], 1 \n\t" - "sra %[temp0], %[temp0], 1 \n\t" - "sra %[temp6], %[temp6], 1 \n\t" - "sra %[temp4], %[temp4], 1 \n\t" - "usw %[temp3], -12(%[temp7]) \n\t" - "usw %[temp0], -16(%[temp7]) \n\t" - "usw %[temp6], -4(%[temp7]) \n\t" - "usw %[temp4], -8(%[temp7]) \n\t" - : [temp0] "=&r"(temp0), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), - [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [temp6] "=&r"(temp6), - [temp1] "+r"(temp1), [temp8] "+r"(temp8), [temp7] "+r"(temp7) - : - : "hi", "lo", "memory"); - } - echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]); -} - -void WebRtcAecm_ResetAdaptiveChannel_mips(AecmCore* aecm) { - int i; - int32_t* temp3; - int16_t* temp0; - int32_t temp1, temp2, temp4, temp5; - - temp0 = &(aecm->channelStored[0]); - temp3 = &(aecm->channelAdapt32[0]); - - // The stored channel has a significantly lower MSE than the adaptive one for - // two consecutive calculations. Reset the adaptive channel. - memcpy(aecm->channelAdapt16, aecm->channelStored, - sizeof(int16_t) * PART_LEN1); - - // Restore the W32 channel - for (i = 0; i < PART_LEN; i += 4) { - __asm __volatile( - "ulw %[temp1], 0(%[temp0]) \n\t" - "ulw %[temp4], 4(%[temp0]) \n\t" - "preceq.w.phl %[temp2], %[temp1] \n\t" - "preceq.w.phr %[temp1], %[temp1] \n\t" - "preceq.w.phl %[temp5], %[temp4] \n\t" - "preceq.w.phr %[temp4], %[temp4] \n\t" - "addiu %[temp0], %[temp0], 8 \n\t" - "usw %[temp2], 4(%[temp3]) \n\t" - "usw %[temp1], 0(%[temp3]) \n\t" - "usw %[temp5], 12(%[temp3]) \n\t" - "usw %[temp4], 8(%[temp3]) \n\t" - "addiu %[temp3], %[temp3], 16 \n\t" - : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp4] "=&r"(temp4), - [temp5] "=&r"(temp5), [temp3] "+r"(temp3), [temp0] "+r"(temp0) - : - : "memory"); - } - - aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16; -} -#endif // #if defined(MIPS_DSP_R1_LE) - -// Transforms a time domain signal into the frequency domain, outputting the -// complex valued signal, absolute value and sum of absolute values. -// -// time_signal [in] Pointer to time domain signal -// freq_signal_real [out] Pointer to real part of frequency domain array -// freq_signal_imag [out] Pointer to imaginary part of frequency domain -// array -// freq_signal_abs [out] Pointer to absolute value of frequency domain -// array -// freq_signal_sum_abs [out] Pointer to the sum of all absolute values in -// the frequency domain array -// return value The Q-domain of current frequency values -// -static int TimeToFrequencyDomain(AecmCore* aecm, - const int16_t* time_signal, - ComplexInt16* freq_signal, - uint16_t* freq_signal_abs, - uint32_t* freq_signal_sum_abs) { - int i = 0; - int time_signal_scaling = 0; - - // In fft_buf, +16 for 32-byte alignment. - int16_t fft_buf[PART_LEN4 + 16]; - int16_t* fft = (int16_t*)(((uintptr_t)fft_buf + 31) & ~31); - - int16_t tmp16no1; -#if !defined(MIPS_DSP_R2_LE) - int32_t tmp32no1; - int32_t tmp32no2; - int16_t tmp16no2; -#else - int32_t tmp32no10, tmp32no11, tmp32no12, tmp32no13; - int32_t tmp32no20, tmp32no21, tmp32no22, tmp32no23; - int16_t* freqp; - uint16_t* freqabsp; - uint32_t freqt0, freqt1, freqt2, freqt3; - uint32_t freqs; -#endif - -#ifdef AECM_DYNAMIC_Q - tmp16no1 = WebRtcSpl_MaxAbsValueW16(time_signal, PART_LEN2); - time_signal_scaling = WebRtcSpl_NormW16(tmp16no1); -#endif - - WindowAndFFT(aecm, fft, time_signal, freq_signal, time_signal_scaling); - - // Extract imaginary and real part, - // calculate the magnitude for all frequency bins - freq_signal[0].imag = 0; - freq_signal[PART_LEN].imag = 0; - freq_signal[PART_LEN].real = fft[PART_LEN2]; - freq_signal_abs[0] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[0].real); - freq_signal_abs[PART_LEN] = - (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[PART_LEN].real); - (*freq_signal_sum_abs) = - (uint32_t)(freq_signal_abs[0]) + (uint32_t)(freq_signal_abs[PART_LEN]); - -#if !defined(MIPS_DSP_R2_LE) - for (i = 1; i < PART_LEN; i++) { - if (freq_signal[i].real == 0) { - freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - } else if (freq_signal[i].imag == 0) { - freq_signal_abs[i] = (uint16_t)WEBRTC_SPL_ABS_W16(freq_signal[i].real); - } else { - // Approximation for magnitude of complex fft output - // magn = sqrt(real^2 + imag^2) - // magn ~= alpha * max(`imag`,`real`) + beta * min(`imag`,`real`) - // - // The parameters alpha and beta are stored in Q15 - tmp16no1 = WEBRTC_SPL_ABS_W16(freq_signal[i].real); - tmp16no2 = WEBRTC_SPL_ABS_W16(freq_signal[i].imag); - tmp32no1 = tmp16no1 * tmp16no1; - tmp32no2 = tmp16no2 * tmp16no2; - tmp32no2 = WebRtcSpl_AddSatW32(tmp32no1, tmp32no2); - tmp32no1 = WebRtcSpl_SqrtFloor(tmp32no2); - - freq_signal_abs[i] = (uint16_t)tmp32no1; - } - (*freq_signal_sum_abs) += (uint32_t)freq_signal_abs[i]; - } -#else // #if !defined(MIPS_DSP_R2_LE) - freqs = - (uint32_t)(freq_signal_abs[0]) + (uint32_t)(freq_signal_abs[PART_LEN]); - freqp = &(freq_signal[1].real); - - __asm __volatile( - "lw %[freqt0], 0(%[freqp]) \n\t" - "lw %[freqt1], 4(%[freqp]) \n\t" - "lw %[freqt2], 8(%[freqp]) \n\t" - "mult $ac0, $zero, $zero \n\t" - "mult $ac1, $zero, $zero \n\t" - "mult $ac2, $zero, $zero \n\t" - "dpaq_s.w.ph $ac0, %[freqt0], %[freqt0] \n\t" - "dpaq_s.w.ph $ac1, %[freqt1], %[freqt1] \n\t" - "dpaq_s.w.ph $ac2, %[freqt2], %[freqt2] \n\t" - "addiu %[freqp], %[freqp], 12 \n\t" - "extr.w %[tmp32no20], $ac0, 1 \n\t" - "extr.w %[tmp32no21], $ac1, 1 \n\t" - "extr.w %[tmp32no22], $ac2, 1 \n\t" - : [freqt0] "=&r"(freqt0), [freqt1] "=&r"(freqt1), [freqt2] "=&r"(freqt2), - [freqp] "+r"(freqp), [tmp32no20] "=r"(tmp32no20), - [tmp32no21] "=r"(tmp32no21), [tmp32no22] "=r"(tmp32no22) - : - : "memory", "hi", "lo", "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo"); - - tmp32no10 = WebRtcSpl_SqrtFloor(tmp32no20); - tmp32no11 = WebRtcSpl_SqrtFloor(tmp32no21); - tmp32no12 = WebRtcSpl_SqrtFloor(tmp32no22); - freq_signal_abs[1] = (uint16_t)tmp32no10; - freq_signal_abs[2] = (uint16_t)tmp32no11; - freq_signal_abs[3] = (uint16_t)tmp32no12; - freqs += (uint32_t)tmp32no10; - freqs += (uint32_t)tmp32no11; - freqs += (uint32_t)tmp32no12; - freqabsp = &(freq_signal_abs[4]); - for (i = 4; i < PART_LEN; i += 4) { - __asm __volatile( - "ulw %[freqt0], 0(%[freqp]) \n\t" - "ulw %[freqt1], 4(%[freqp]) \n\t" - "ulw %[freqt2], 8(%[freqp]) \n\t" - "ulw %[freqt3], 12(%[freqp]) \n\t" - "mult $ac0, $zero, $zero \n\t" - "mult $ac1, $zero, $zero \n\t" - "mult $ac2, $zero, $zero \n\t" - "mult $ac3, $zero, $zero \n\t" - "dpaq_s.w.ph $ac0, %[freqt0], %[freqt0] \n\t" - "dpaq_s.w.ph $ac1, %[freqt1], %[freqt1] \n\t" - "dpaq_s.w.ph $ac2, %[freqt2], %[freqt2] \n\t" - "dpaq_s.w.ph $ac3, %[freqt3], %[freqt3] \n\t" - "addiu %[freqp], %[freqp], 16 \n\t" - "addiu %[freqabsp], %[freqabsp], 8 \n\t" - "extr.w %[tmp32no20], $ac0, 1 \n\t" - "extr.w %[tmp32no21], $ac1, 1 \n\t" - "extr.w %[tmp32no22], $ac2, 1 \n\t" - "extr.w %[tmp32no23], $ac3, 1 \n\t" - : [freqt0] "=&r"(freqt0), [freqt1] "=&r"(freqt1), - [freqt2] "=&r"(freqt2), [freqt3] "=&r"(freqt3), - [tmp32no20] "=r"(tmp32no20), [tmp32no21] "=r"(tmp32no21), - [tmp32no22] "=r"(tmp32no22), [tmp32no23] "=r"(tmp32no23), - [freqabsp] "+r"(freqabsp), [freqp] "+r"(freqp) - : - : "memory", "hi", "lo", "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo", - "$ac3hi", "$ac3lo"); - - tmp32no10 = WebRtcSpl_SqrtFloor(tmp32no20); - tmp32no11 = WebRtcSpl_SqrtFloor(tmp32no21); - tmp32no12 = WebRtcSpl_SqrtFloor(tmp32no22); - tmp32no13 = WebRtcSpl_SqrtFloor(tmp32no23); - - __asm __volatile( - "sh %[tmp32no10], -8(%[freqabsp]) \n\t" - "sh %[tmp32no11], -6(%[freqabsp]) \n\t" - "sh %[tmp32no12], -4(%[freqabsp]) \n\t" - "sh %[tmp32no13], -2(%[freqabsp]) \n\t" - "addu %[freqs], %[freqs], %[tmp32no10] \n\t" - "addu %[freqs], %[freqs], %[tmp32no11] \n\t" - "addu %[freqs], %[freqs], %[tmp32no12] \n\t" - "addu %[freqs], %[freqs], %[tmp32no13] \n\t" - : [freqs] "+r"(freqs) - : [tmp32no10] "r"(tmp32no10), [tmp32no11] "r"(tmp32no11), - [tmp32no12] "r"(tmp32no12), [tmp32no13] "r"(tmp32no13), - [freqabsp] "r"(freqabsp) - : "memory"); - } - - (*freq_signal_sum_abs) = freqs; -#endif - - return time_signal_scaling; -} - -int WebRtcAecm_ProcessBlock(AecmCore* aecm, - const int16_t* farend, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* output) { - int i; - uint32_t xfaSum; - uint32_t dfaNoisySum; - uint32_t dfaCleanSum; - uint32_t echoEst32Gained; - uint32_t tmpU32; - int32_t tmp32no1; - - uint16_t xfa[PART_LEN1]; - uint16_t dfaNoisy[PART_LEN1]; - uint16_t dfaClean[PART_LEN1]; - uint16_t* ptrDfaClean = dfaClean; - const uint16_t* far_spectrum_ptr = NULL; - - // 32 byte aligned buffers (with +8 or +16). - int16_t fft_buf[PART_LEN4 + 2 + 16]; // +2 to make a loop safe. - int32_t echoEst32_buf[PART_LEN1 + 8]; - int32_t dfw_buf[PART_LEN2 + 8]; - int32_t efw_buf[PART_LEN2 + 8]; - - int16_t* fft = (int16_t*)(((uint32_t)fft_buf + 31) & ~31); - int32_t* echoEst32 = (int32_t*)(((uint32_t)echoEst32_buf + 31) & ~31); - ComplexInt16* dfw = (ComplexInt16*)(((uint32_t)dfw_buf + 31) & ~31); - ComplexInt16* efw = (ComplexInt16*)(((uint32_t)efw_buf + 31) & ~31); - - int16_t hnl[PART_LEN1]; - int16_t numPosCoef = 0; - int delay; - int16_t tmp16no1; - int16_t tmp16no2; - int16_t mu; - int16_t supGain; - int16_t zeros32, zeros16; - int16_t zerosDBufNoisy, zerosDBufClean, zerosXBuf; - int far_q; - int16_t resolutionDiff, qDomainDiff, dfa_clean_q_domain_diff; - - const int kMinPrefBand = 4; - const int kMaxPrefBand = 24; - int32_t avgHnl32 = 0; - - int32_t temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; - int16_t* ptr; - int16_t* ptr1; - int16_t* er_ptr; - int16_t* dr_ptr; - - ptr = &hnl[0]; - ptr1 = &hnl[0]; - er_ptr = &efw[0].real; - dr_ptr = &dfw[0].real; - - // Determine startup state. There are three states: - // (0) the first CONV_LEN blocks - // (1) another CONV_LEN blocks - // (2) the rest - - if (aecm->startupState < 2) { - aecm->startupState = - (aecm->totCount >= CONV_LEN) + (aecm->totCount >= CONV_LEN2); - } - // END: Determine startup state - - // Buffer near and far end signals - memcpy(aecm->xBuf + PART_LEN, farend, sizeof(int16_t) * PART_LEN); - memcpy(aecm->dBufNoisy + PART_LEN, nearendNoisy, sizeof(int16_t) * PART_LEN); - if (nearendClean != NULL) { - memcpy(aecm->dBufClean + PART_LEN, nearendClean, - sizeof(int16_t) * PART_LEN); - } - - // Transform far end signal from time domain to frequency domain. - far_q = TimeToFrequencyDomain(aecm, aecm->xBuf, dfw, xfa, &xfaSum); - - // Transform noisy near end signal from time domain to frequency domain. - zerosDBufNoisy = - TimeToFrequencyDomain(aecm, aecm->dBufNoisy, dfw, dfaNoisy, &dfaNoisySum); - aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain; - aecm->dfaNoisyQDomain = (int16_t)zerosDBufNoisy; - - if (nearendClean == NULL) { - ptrDfaClean = dfaNoisy; - aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld; - aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain; - dfaCleanSum = dfaNoisySum; - } else { - // Transform clean near end signal from time domain to frequency domain. - zerosDBufClean = TimeToFrequencyDomain(aecm, aecm->dBufClean, dfw, dfaClean, - &dfaCleanSum); - aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain; - aecm->dfaCleanQDomain = (int16_t)zerosDBufClean; - } - - // Get the delay - // Save far-end history and estimate delay - WebRtcAecm_UpdateFarHistory(aecm, xfa, far_q); - - if (WebRtc_AddFarSpectrumFix(aecm->delay_estimator_farend, xfa, PART_LEN1, - far_q) == -1) { - return -1; - } - delay = WebRtc_DelayEstimatorProcessFix(aecm->delay_estimator, dfaNoisy, - PART_LEN1, zerosDBufNoisy); - if (delay == -1) { - return -1; - } else if (delay == -2) { - // If the delay is unknown, we assume zero. - // NOTE: this will have to be adjusted if we ever add lookahead. - delay = 0; - } - - if (aecm->fixedDelay >= 0) { - // Use fixed delay - delay = aecm->fixedDelay; - } - - // Get aligned far end spectrum - far_spectrum_ptr = WebRtcAecm_AlignedFarend(aecm, &far_q, delay); - zerosXBuf = (int16_t)far_q; - - if (far_spectrum_ptr == NULL) { - return -1; - } - - // Calculate log(energy) and update energy threshold levels - WebRtcAecm_CalcEnergies(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisySum, - echoEst32); - // Calculate stepsize - mu = WebRtcAecm_CalcStepSize(aecm); - - // Update counters - aecm->totCount++; - - // This is the channel estimation algorithm. - // It is base on NLMS but has a variable step length, - // which was calculated above. - WebRtcAecm_UpdateChannel(aecm, far_spectrum_ptr, zerosXBuf, dfaNoisy, mu, - echoEst32); - - supGain = WebRtcAecm_CalcSuppressionGain(aecm); - - // Calculate Wiener filter hnl[] - for (i = 0; i < PART_LEN1; i++) { - // Far end signal through channel estimate in Q8 - // How much can we shift right to preserve resolution - tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; - aecm->echoFilt[i] += dchecked_cast((int64_t{tmp32no1} * 50) >> 8); - - zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; - zeros16 = WebRtcSpl_NormW16(supGain) + 1; - if (zeros32 + zeros16 > 16) { - // Multiplication is safe - // Result in - // Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+aecm->xfaQDomainBuf[diff]) - echoEst32Gained = - WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], (uint16_t)supGain); - resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - } else { - tmp16no1 = 17 - zeros32 - zeros16; - resolutionDiff = - 14 + tmp16no1 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - zerosXBuf); - if (zeros32 > tmp16no1) { - echoEst32Gained = WEBRTC_SPL_UMUL_32_16((uint32_t)aecm->echoFilt[i], - supGain >> tmp16no1); - } else { - // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) - echoEst32Gained = (aecm->echoFilt[i] >> tmp16no1) * supGain; - } - } - - zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); - RTC_DCHECK_GE(zeros16, 0); // `zeros16` is a norm, hence non-negative. - dfa_clean_q_domain_diff = aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld; - if (zeros16 < dfa_clean_q_domain_diff && aecm->nearFilt[i]) { - tmp16no1 = aecm->nearFilt[i] << zeros16; - qDomainDiff = zeros16 - dfa_clean_q_domain_diff; - tmp16no2 = ptrDfaClean[i] >> -qDomainDiff; - } else { - tmp16no1 = dfa_clean_q_domain_diff < 0 - ? aecm->nearFilt[i] >> -dfa_clean_q_domain_diff - : aecm->nearFilt[i] << dfa_clean_q_domain_diff; - qDomainDiff = 0; - tmp16no2 = ptrDfaClean[i]; - } - - tmp32no1 = (int32_t)(tmp16no2 - tmp16no1); - tmp16no2 = (int16_t)(tmp32no1 >> 4); - tmp16no2 += tmp16no1; - zeros16 = WebRtcSpl_NormW16(tmp16no2); - if ((tmp16no2) & (-qDomainDiff > zeros16)) { - aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; - } else { - aecm->nearFilt[i] = - qDomainDiff < 0 ? tmp16no2 << -qDomainDiff : tmp16no2 >> qDomainDiff; - } - - // Wiener filter coefficients, resulting hnl in Q14 - if (echoEst32Gained == 0) { - hnl[i] = ONE_Q14; - numPosCoef++; - } else if (aecm->nearFilt[i] == 0) { - hnl[i] = 0; - } else { - // Multiply the suppression gain - // Rounding - echoEst32Gained += (uint32_t)(aecm->nearFilt[i] >> 1); - tmpU32 = - WebRtcSpl_DivU32U16(echoEst32Gained, (uint16_t)aecm->nearFilt[i]); - - // Current resolution is - // Q-(RESOLUTION_CHANNEL + RESOLUTION_SUPGAIN - // - max(0, 17 - zeros16 - zeros32)) - // Make sure we are in Q14 - tmp32no1 = (int32_t)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff); - if (tmp32no1 > ONE_Q14) { - hnl[i] = 0; - } else if (tmp32no1 < 0) { - hnl[i] = ONE_Q14; - numPosCoef++; - } else { - // 1-echoEst/dfa - hnl[i] = ONE_Q14 - (int16_t)tmp32no1; - if (hnl[i] <= 0) { - hnl[i] = 0; - } else { - numPosCoef++; - } - } - } - } - - // Only in wideband. Prevent the gain in upper band from being larger than - // in lower band. - if (aecm->mult == 2) { - // TODO(bjornv): Investigate if the scaling of hnl[i] below can cause - // speech distortion in double-talk. - for (i = 0; i < (PART_LEN1 >> 3); i++) { - __asm __volatile( - "lh %[temp1], 0(%[ptr1]) \n\t" - "lh %[temp2], 2(%[ptr1]) \n\t" - "lh %[temp3], 4(%[ptr1]) \n\t" - "lh %[temp4], 6(%[ptr1]) \n\t" - "lh %[temp5], 8(%[ptr1]) \n\t" - "lh %[temp6], 10(%[ptr1]) \n\t" - "lh %[temp7], 12(%[ptr1]) \n\t" - "lh %[temp8], 14(%[ptr1]) \n\t" - "mul %[temp1], %[temp1], %[temp1] \n\t" - "mul %[temp2], %[temp2], %[temp2] \n\t" - "mul %[temp3], %[temp3], %[temp3] \n\t" - "mul %[temp4], %[temp4], %[temp4] \n\t" - "mul %[temp5], %[temp5], %[temp5] \n\t" - "mul %[temp6], %[temp6], %[temp6] \n\t" - "mul %[temp7], %[temp7], %[temp7] \n\t" - "mul %[temp8], %[temp8], %[temp8] \n\t" - "sra %[temp1], %[temp1], 14 \n\t" - "sra %[temp2], %[temp2], 14 \n\t" - "sra %[temp3], %[temp3], 14 \n\t" - "sra %[temp4], %[temp4], 14 \n\t" - "sra %[temp5], %[temp5], 14 \n\t" - "sra %[temp6], %[temp6], 14 \n\t" - "sra %[temp7], %[temp7], 14 \n\t" - "sra %[temp8], %[temp8], 14 \n\t" - "sh %[temp1], 0(%[ptr1]) \n\t" - "sh %[temp2], 2(%[ptr1]) \n\t" - "sh %[temp3], 4(%[ptr1]) \n\t" - "sh %[temp4], 6(%[ptr1]) \n\t" - "sh %[temp5], 8(%[ptr1]) \n\t" - "sh %[temp6], 10(%[ptr1]) \n\t" - "sh %[temp7], 12(%[ptr1]) \n\t" - "sh %[temp8], 14(%[ptr1]) \n\t" - "addiu %[ptr1], %[ptr1], 16 \n\t" - : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), - [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [temp6] "=&r"(temp6), - [temp7] "=&r"(temp7), [temp8] "=&r"(temp8), [ptr1] "+r"(ptr1) - : - : "memory", "hi", "lo"); - } - for (i = 0; i < (PART_LEN1 & 7); i++) { - __asm __volatile( - "lh %[temp1], 0(%[ptr1]) \n\t" - "mul %[temp1], %[temp1], %[temp1] \n\t" - "sra %[temp1], %[temp1], 14 \n\t" - "sh %[temp1], 0(%[ptr1]) \n\t" - "addiu %[ptr1], %[ptr1], 2 \n\t" - : [temp1] "=&r"(temp1), [ptr1] "+r"(ptr1) - : - : "memory", "hi", "lo"); - } - - for (i = kMinPrefBand; i <= kMaxPrefBand; i++) { - avgHnl32 += (int32_t)hnl[i]; - } - - RTC_DCHECK_GT(kMaxPrefBand - kMinPrefBand + 1, 0); - avgHnl32 /= (kMaxPrefBand - kMinPrefBand + 1); - - for (i = kMaxPrefBand; i < PART_LEN1; i++) { - if (hnl[i] > (int16_t)avgHnl32) { - hnl[i] = (int16_t)avgHnl32; - } - } - } - - // Calculate NLP gain, result is in Q14 - if (aecm->nlpFlag) { - if (numPosCoef < 3) { - for (i = 0; i < PART_LEN1; i++) { - efw[i].real = 0; - efw[i].imag = 0; - hnl[i] = 0; - } - } else { - for (i = 0; i < PART_LEN1; i++) { -#if defined(MIPS_DSP_R1_LE) - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "lh %[temp1], 0(%[ptr]) \n\t" - "lh %[temp2], 0(%[dr_ptr]) \n\t" - "slti %[temp4], %[temp1], 0x4001 \n\t" - "beqz %[temp4], 3f \n\t" - " lh %[temp3], 2(%[dr_ptr]) \n\t" - "slti %[temp5], %[temp1], 3277 \n\t" - "bnez %[temp5], 2f \n\t" - " addiu %[dr_ptr], %[dr_ptr], 4 \n\t" - "mul %[temp2], %[temp2], %[temp1] \n\t" - "mul %[temp3], %[temp3], %[temp1] \n\t" - "shra_r.w %[temp2], %[temp2], 14 \n\t" - "shra_r.w %[temp3], %[temp3], 14 \n\t" - "b 4f \n\t" - " nop \n\t" - "2: \n\t" - "addu %[temp1], $zero, $zero \n\t" - "addu %[temp2], $zero, $zero \n\t" - "addu %[temp3], $zero, $zero \n\t" - "b 1f \n\t" - " nop \n\t" - "3: \n\t" - "addiu %[temp1], $0, 0x4000 \n\t" - "1: \n\t" - "sh %[temp1], 0(%[ptr]) \n\t" - "4: \n\t" - "sh %[temp2], 0(%[er_ptr]) \n\t" - "sh %[temp3], 2(%[er_ptr]) \n\t" - "addiu %[ptr], %[ptr], 2 \n\t" - "addiu %[er_ptr], %[er_ptr], 4 \n\t" - ".set pop \n\t" - : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), - [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [ptr] "+r"(ptr), - [er_ptr] "+r"(er_ptr), [dr_ptr] "+r"(dr_ptr) - : - : "memory", "hi", "lo"); -#else - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "lh %[temp1], 0(%[ptr]) \n\t" - "lh %[temp2], 0(%[dr_ptr]) \n\t" - "slti %[temp4], %[temp1], 0x4001 \n\t" - "beqz %[temp4], 3f \n\t" - " lh %[temp3], 2(%[dr_ptr]) \n\t" - "slti %[temp5], %[temp1], 3277 \n\t" - "bnez %[temp5], 2f \n\t" - " addiu %[dr_ptr], %[dr_ptr], 4 \n\t" - "mul %[temp2], %[temp2], %[temp1] \n\t" - "mul %[temp3], %[temp3], %[temp1] \n\t" - "addiu %[temp2], %[temp2], 0x2000 \n\t" - "addiu %[temp3], %[temp3], 0x2000 \n\t" - "sra %[temp2], %[temp2], 14 \n\t" - "sra %[temp3], %[temp3], 14 \n\t" - "b 4f \n\t" - " nop \n\t" - "2: \n\t" - "addu %[temp1], $zero, $zero \n\t" - "addu %[temp2], $zero, $zero \n\t" - "addu %[temp3], $zero, $zero \n\t" - "b 1f \n\t" - " nop \n\t" - "3: \n\t" - "addiu %[temp1], $0, 0x4000 \n\t" - "1: \n\t" - "sh %[temp1], 0(%[ptr]) \n\t" - "4: \n\t" - "sh %[temp2], 0(%[er_ptr]) \n\t" - "sh %[temp3], 2(%[er_ptr]) \n\t" - "addiu %[ptr], %[ptr], 2 \n\t" - "addiu %[er_ptr], %[er_ptr], 4 \n\t" - ".set pop \n\t" - : [temp1] "=&r"(temp1), [temp2] "=&r"(temp2), [temp3] "=&r"(temp3), - [temp4] "=&r"(temp4), [temp5] "=&r"(temp5), [ptr] "+r"(ptr), - [er_ptr] "+r"(er_ptr), [dr_ptr] "+r"(dr_ptr) - : - : "memory", "hi", "lo"); -#endif - } - } - } else { - // multiply with Wiener coefficients - for (i = 0; i < PART_LEN1; i++) { - efw[i].real = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].real, - hnl[i], 14)); - efw[i].imag = (int16_t)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfw[i].imag, - hnl[i], 14)); - } - } - - if (aecm->cngMode == AecmTrue) { - ComfortNoise(aecm, ptrDfaClean, efw, hnl); - } - - InverseFFTAndWindow(aecm, fft, efw, output, nearendClean); - - return 0; -} - -// Generate comfort noise and add to output signal. -static void ComfortNoise(AecmCore* aecm, - const uint16_t* dfa, - ComplexInt16* out, - const int16_t* lambda) { - int16_t i; - int16_t tmp16, tmp161, tmp162, tmp163, nrsh1, nrsh2; - int32_t tmp32, tmp321, tnoise, tnoise1; - int32_t tmp322, tmp323, *tmp1; - int16_t* dfap; - int16_t* lambdap; - const int32_t c2049 = 2049; - const int32_t c359 = 359; - const int32_t c114 = ONE_Q14; - - int16_t randW16[PART_LEN]; - int16_t uReal[PART_LEN1]; - int16_t uImag[PART_LEN1]; - int32_t outLShift32; - - int16_t shiftFromNearToNoise = kNoiseEstQDomain - aecm->dfaCleanQDomain; - int16_t minTrackShift = 9; - - RTC_DCHECK_GE(shiftFromNearToNoise, 0); - RTC_DCHECK_LT(shiftFromNearToNoise, 16); - - if (aecm->noiseEstCtr < 100) { - // Track the minimum more quickly initially. - aecm->noiseEstCtr++; - minTrackShift = 6; - } - - // Generate a uniform random array on [0 2^15-1]. - WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); - int16_t* randW16p = (int16_t*)randW16; -#if defined(MIPS_DSP_R1_LE) - int16_t* kCosTablep = (int16_t*)WebRtcAecm_kCosTable; - int16_t* kSinTablep = (int16_t*)WebRtcAecm_kSinTable; -#endif // #if defined(MIPS_DSP_R1_LE) - tmp1 = (int32_t*)aecm->noiseEst + 1; - dfap = (int16_t*)dfa + 1; - lambdap = (int16_t*)lambda + 1; - // Estimate noise power. - for (i = 1; i < PART_LEN1; i += 2) { - // Shift to the noise domain. - __asm __volatile( - "lh %[tmp32], 0(%[dfap]) \n\t" - "lw %[tnoise], 0(%[tmp1]) \n\t" - "sllv %[outLShift32], %[tmp32], %[shiftFromNearToNoise] \n\t" - : [tmp32] "=&r"(tmp32), [outLShift32] "=r"(outLShift32), - [tnoise] "=&r"(tnoise) - : [tmp1] "r"(tmp1), [dfap] "r"(dfap), - [shiftFromNearToNoise] "r"(shiftFromNearToNoise) - : "memory"); - - if (outLShift32 < tnoise) { - // Reset "too low" counter - aecm->noiseEstTooLowCtr[i] = 0; - // Track the minimum. - if (tnoise < (1 << minTrackShift)) { - // For small values, decrease noiseEst[i] every - // `kNoiseEstIncCount` block. The regular approach below can not - // go further down due to truncation. - aecm->noiseEstTooHighCtr[i]++; - if (aecm->noiseEstTooHighCtr[i] >= kNoiseEstIncCount) { - tnoise--; - aecm->noiseEstTooHighCtr[i] = 0; // Reset the counter - } - } else { - __asm __volatile( - "subu %[tmp32], %[tnoise], %[outLShift32] \n\t" - "srav %[tmp32], %[tmp32], %[minTrackShift] \n\t" - "subu %[tnoise], %[tnoise], %[tmp32] \n\t" - : [tmp32] "=&r"(tmp32), [tnoise] "+r"(tnoise) - : [outLShift32] "r"(outLShift32), [minTrackShift] "r"( - minTrackShift)); - } - } else { - // Reset "too high" counter - aecm->noiseEstTooHighCtr[i] = 0; - // Ramp slowly upwards until we hit the minimum again. - if ((tnoise >> 19) <= 0) { - if ((tnoise >> 11) > 0) { - // Large enough for relative increase - __asm __volatile( - "mul %[tnoise], %[tnoise], %[c2049] \n\t" - "sra %[tnoise], %[tnoise], 11 \n\t" - : [tnoise] "+r"(tnoise) - : [c2049] "r"(c2049) - : "hi", "lo"); - } else { - // Make incremental increases based on size every - // `kNoiseEstIncCount` block - aecm->noiseEstTooLowCtr[i]++; - if (aecm->noiseEstTooLowCtr[i] >= kNoiseEstIncCount) { - __asm __volatile( - "sra %[tmp32], %[tnoise], 9 \n\t" - "addi %[tnoise], %[tnoise], 1 \n\t" - "addu %[tnoise], %[tnoise], %[tmp32] \n\t" - : [tnoise] "+r"(tnoise), [tmp32] "=&r"(tmp32) - :); - aecm->noiseEstTooLowCtr[i] = 0; // Reset counter - } - } - } else { - // Avoid overflow. - // Multiplication with 2049 will cause wrap around. Scale - // down first and then multiply - __asm __volatile( - "sra %[tnoise], %[tnoise], 11 \n\t" - "mul %[tnoise], %[tnoise], %[c2049] \n\t" - : [tnoise] "+r"(tnoise) - : [c2049] "r"(c2049) - : "hi", "lo"); - } - } - - // Shift to the noise domain. - __asm __volatile( - "lh %[tmp32], 2(%[dfap]) \n\t" - "lw %[tnoise1], 4(%[tmp1]) \n\t" - "addiu %[dfap], %[dfap], 4 \n\t" - "sllv %[outLShift32], %[tmp32], %[shiftFromNearToNoise] \n\t" - : [tmp32] "=&r"(tmp32), [dfap] "+r"(dfap), - [outLShift32] "=r"(outLShift32), [tnoise1] "=&r"(tnoise1) - : [tmp1] "r"(tmp1), [shiftFromNearToNoise] "r"(shiftFromNearToNoise) - : "memory"); - - if (outLShift32 < tnoise1) { - // Reset "too low" counter - aecm->noiseEstTooLowCtr[i + 1] = 0; - // Track the minimum. - if (tnoise1 < (1 << minTrackShift)) { - // For small values, decrease noiseEst[i] every - // `kNoiseEstIncCount` block. The regular approach below can not - // go further down due to truncation. - aecm->noiseEstTooHighCtr[i + 1]++; - if (aecm->noiseEstTooHighCtr[i + 1] >= kNoiseEstIncCount) { - tnoise1--; - aecm->noiseEstTooHighCtr[i + 1] = 0; // Reset the counter - } - } else { - __asm __volatile( - "subu %[tmp32], %[tnoise1], %[outLShift32] \n\t" - "srav %[tmp32], %[tmp32], %[minTrackShift] \n\t" - "subu %[tnoise1], %[tnoise1], %[tmp32] \n\t" - : [tmp32] "=&r"(tmp32), [tnoise1] "+r"(tnoise1) - : [outLShift32] "r"(outLShift32), [minTrackShift] "r"( - minTrackShift)); - } - } else { - // Reset "too high" counter - aecm->noiseEstTooHighCtr[i + 1] = 0; - // Ramp slowly upwards until we hit the minimum again. - if ((tnoise1 >> 19) <= 0) { - if ((tnoise1 >> 11) > 0) { - // Large enough for relative increase - __asm __volatile( - "mul %[tnoise1], %[tnoise1], %[c2049] \n\t" - "sra %[tnoise1], %[tnoise1], 11 \n\t" - : [tnoise1] "+r"(tnoise1) - : [c2049] "r"(c2049) - : "hi", "lo"); - } else { - // Make incremental increases based on size every - // `kNoiseEstIncCount` block - aecm->noiseEstTooLowCtr[i + 1]++; - if (aecm->noiseEstTooLowCtr[i + 1] >= kNoiseEstIncCount) { - __asm __volatile( - "sra %[tmp32], %[tnoise1], 9 \n\t" - "addi %[tnoise1], %[tnoise1], 1 \n\t" - "addu %[tnoise1], %[tnoise1], %[tmp32] \n\t" - : [tnoise1] "+r"(tnoise1), [tmp32] "=&r"(tmp32) - :); - aecm->noiseEstTooLowCtr[i + 1] = 0; // Reset counter - } - } - } else { - // Avoid overflow. - // Multiplication with 2049 will cause wrap around. Scale - // down first and then multiply - __asm __volatile( - "sra %[tnoise1], %[tnoise1], 11 \n\t" - "mul %[tnoise1], %[tnoise1], %[c2049] \n\t" - : [tnoise1] "+r"(tnoise1) - : [c2049] "r"(c2049) - : "hi", "lo"); - } - } - - __asm __volatile( - "lh %[tmp16], 0(%[lambdap]) \n\t" - "lh %[tmp161], 2(%[lambdap]) \n\t" - "sw %[tnoise], 0(%[tmp1]) \n\t" - "sw %[tnoise1], 4(%[tmp1]) \n\t" - "subu %[tmp16], %[c114], %[tmp16] \n\t" - "subu %[tmp161], %[c114], %[tmp161] \n\t" - "srav %[tmp32], %[tnoise], %[shiftFromNearToNoise] \n\t" - "srav %[tmp321], %[tnoise1], %[shiftFromNearToNoise] \n\t" - "addiu %[lambdap], %[lambdap], 4 \n\t" - "addiu %[tmp1], %[tmp1], 8 \n\t" - : [tmp16] "=&r"(tmp16), [tmp161] "=&r"(tmp161), [tmp1] "+r"(tmp1), - [tmp32] "=&r"(tmp32), [tmp321] "=&r"(tmp321), [lambdap] "+r"(lambdap) - : [tnoise] "r"(tnoise), [tnoise1] "r"(tnoise1), [c114] "r"(c114), - [shiftFromNearToNoise] "r"(shiftFromNearToNoise) - : "memory"); - - if (tmp32 > 32767) { - tmp32 = 32767; - aecm->noiseEst[i] = tmp32 << shiftFromNearToNoise; - } - if (tmp321 > 32767) { - tmp321 = 32767; - aecm->noiseEst[i + 1] = tmp321 << shiftFromNearToNoise; - } - - __asm __volatile( - "mul %[tmp32], %[tmp32], %[tmp16] \n\t" - "mul %[tmp321], %[tmp321], %[tmp161] \n\t" - "sra %[nrsh1], %[tmp32], 14 \n\t" - "sra %[nrsh2], %[tmp321], 14 \n\t" - : [nrsh1] "=&r"(nrsh1), [nrsh2] "=r"(nrsh2) - : [tmp16] "r"(tmp16), [tmp161] "r"(tmp161), [tmp32] "r"(tmp32), - [tmp321] "r"(tmp321) - : "memory", "hi", "lo"); - - __asm __volatile( - "lh %[tmp32], 0(%[randW16p]) \n\t" - "lh %[tmp321], 2(%[randW16p]) \n\t" - "addiu %[randW16p], %[randW16p], 4 \n\t" - "mul %[tmp32], %[tmp32], %[c359] \n\t" - "mul %[tmp321], %[tmp321], %[c359] \n\t" - "sra %[tmp16], %[tmp32], 15 \n\t" - "sra %[tmp161], %[tmp321], 15 \n\t" - : [randW16p] "+r"(randW16p), [tmp32] "=&r"(tmp32), [tmp16] "=r"(tmp16), - [tmp161] "=r"(tmp161), [tmp321] "=&r"(tmp321) - : [c359] "r"(c359) - : "memory", "hi", "lo"); - -#if !defined(MIPS_DSP_R1_LE) - tmp32 = WebRtcAecm_kCosTable[tmp16]; - tmp321 = WebRtcAecm_kSinTable[tmp16]; - tmp322 = WebRtcAecm_kCosTable[tmp161]; - tmp323 = WebRtcAecm_kSinTable[tmp161]; -#else - __asm __volatile( - "sll %[tmp16], %[tmp16], 1 \n\t" - "sll %[tmp161], %[tmp161], 1 \n\t" - "lhx %[tmp32], %[tmp16](%[kCosTablep]) \n\t" - "lhx %[tmp321], %[tmp16](%[kSinTablep]) \n\t" - "lhx %[tmp322], %[tmp161](%[kCosTablep]) \n\t" - "lhx %[tmp323], %[tmp161](%[kSinTablep]) \n\t" - : [tmp32] "=&r"(tmp32), [tmp321] "=&r"(tmp321), [tmp322] "=&r"(tmp322), - [tmp323] "=&r"(tmp323) - : [kCosTablep] "r"(kCosTablep), [tmp16] "r"(tmp16), - [tmp161] "r"(tmp161), [kSinTablep] "r"(kSinTablep) - : "memory"); -#endif - __asm __volatile( - "mul %[tmp32], %[tmp32], %[nrsh1] \n\t" - "negu %[tmp162], %[nrsh1] \n\t" - "mul %[tmp322], %[tmp322], %[nrsh2] \n\t" - "negu %[tmp163], %[nrsh2] \n\t" - "sra %[tmp32], %[tmp32], 13 \n\t" - "mul %[tmp321], %[tmp321], %[tmp162] \n\t" - "sra %[tmp322], %[tmp322], 13 \n\t" - "mul %[tmp323], %[tmp323], %[tmp163] \n\t" - "sra %[tmp321], %[tmp321], 13 \n\t" - "sra %[tmp323], %[tmp323], 13 \n\t" - : [tmp32] "+r"(tmp32), [tmp321] "+r"(tmp321), [tmp162] "=&r"(tmp162), - [tmp322] "+r"(tmp322), [tmp323] "+r"(tmp323), [tmp163] "=&r"(tmp163) - : [nrsh1] "r"(nrsh1), [nrsh2] "r"(nrsh2) - : "hi", "lo"); - // Tables are in Q13. - uReal[i] = (int16_t)tmp32; - uImag[i] = (int16_t)tmp321; - uReal[i + 1] = (int16_t)tmp322; - uImag[i + 1] = (int16_t)tmp323; - } - - int32_t tt, sgn; - tt = out[0].real; - sgn = ((int)tt) >> 31; - out[0].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - tt = out[0].imag; - sgn = ((int)tt) >> 31; - out[0].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - for (i = 1; i < PART_LEN; i++) { - tt = out[i].real + uReal[i]; - sgn = ((int)tt) >> 31; - out[i].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - tt = out[i].imag + uImag[i]; - sgn = ((int)tt) >> 31; - out[i].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - } - tt = out[PART_LEN].real + uReal[PART_LEN]; - sgn = ((int)tt) >> 31; - out[PART_LEN].real = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); - tt = out[PART_LEN].imag; - sgn = ((int)tt) >> 31; - out[PART_LEN].imag = sgn == (int16_t)(tt >> 15) ? (int16_t)tt : (16384 ^ sgn); -} - -} // namespace webrtc diff --git a/modules/audio_processing/aecm/aecm_core_neon.cc b/modules/audio_processing/aecm/aecm_core_neon.cc deleted file mode 100644 index e6507d32e1b..00000000000 --- a/modules/audio_processing/aecm/aecm_core_neon.cc +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "common_audio/signal_processing/include/real_fft.h" -#include "modules/audio_processing/aecm/aecm_core.h" -#include "rtc_base/checks.h" - -extern "C" { -#include "common_audio/signal_processing/include/signal_processing_library.h" -} - -namespace webrtc { - -namespace { - -// TODO(kma): Re-write the corresponding assembly file, the offset -// generating script and makefile, to replace these C functions. - -static inline void AddLanes(uint32_t* ptr, uint32x4_t v) { -#if defined(WEBRTC_ARCH_ARM64) - *(ptr) = vaddvq_u32(v); -#else - uint32x2_t tmp_v; - tmp_v = vadd_u32(vget_low_u32(v), vget_high_u32(v)); - tmp_v = vpadd_u32(tmp_v, tmp_v); - *(ptr) = vget_lane_u32(tmp_v, 0); -#endif -} - -} // namespace - -void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est, - uint32_t* far_energy, - uint32_t* echo_energy_adapt, - uint32_t* echo_energy_stored) { - int16_t* start_stored_p = aecm->channelStored; - int16_t* start_adapt_p = aecm->channelAdapt16; - int32_t* echo_est_p = echo_est; - const int16_t* end_stored_p = aecm->channelStored + PART_LEN; - const uint16_t* far_spectrum_p = far_spectrum; - int16x8_t store_v, adapt_v; - uint16x8_t spectrum_v; - uint32x4_t echo_est_v_low, echo_est_v_high; - uint32x4_t far_energy_v, echo_stored_v, echo_adapt_v; - - far_energy_v = vdupq_n_u32(0); - echo_adapt_v = vdupq_n_u32(0); - echo_stored_v = vdupq_n_u32(0); - - // Get energy for the delayed far end signal and estimated - // echo using both stored and adapted channels. - // The C code: - // for (i = 0; i < PART_LEN1; i++) { - // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - // far_spectrum[i]); - // (*far_energy) += (uint32_t)(far_spectrum[i]); - // *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i]; - // (*echo_energy_stored) += (uint32_t)echo_est[i]; - // } - while (start_stored_p < end_stored_p) { - spectrum_v = vld1q_u16(far_spectrum_p); - adapt_v = vld1q_s16(start_adapt_p); - store_v = vld1q_s16(start_stored_p); - - far_energy_v = vaddw_u16(far_energy_v, vget_low_u16(spectrum_v)); - far_energy_v = vaddw_u16(far_energy_v, vget_high_u16(spectrum_v)); - - echo_est_v_low = vmull_u16(vreinterpret_u16_s16(vget_low_s16(store_v)), - vget_low_u16(spectrum_v)); - echo_est_v_high = vmull_u16(vreinterpret_u16_s16(vget_high_s16(store_v)), - vget_high_u16(spectrum_v)); - vst1q_s32(echo_est_p, vreinterpretq_s32_u32(echo_est_v_low)); - vst1q_s32(echo_est_p + 4, vreinterpretq_s32_u32(echo_est_v_high)); - - echo_stored_v = vaddq_u32(echo_est_v_low, echo_stored_v); - echo_stored_v = vaddq_u32(echo_est_v_high, echo_stored_v); - - echo_adapt_v = - vmlal_u16(echo_adapt_v, vreinterpret_u16_s16(vget_low_s16(adapt_v)), - vget_low_u16(spectrum_v)); - echo_adapt_v = - vmlal_u16(echo_adapt_v, vreinterpret_u16_s16(vget_high_s16(adapt_v)), - vget_high_u16(spectrum_v)); - - start_stored_p += 8; - start_adapt_p += 8; - far_spectrum_p += 8; - echo_est_p += 8; - } - - AddLanes(far_energy, far_energy_v); - AddLanes(echo_energy_stored, echo_stored_v); - AddLanes(echo_energy_adapt, echo_adapt_v); - - echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], - far_spectrum[PART_LEN]); - *echo_energy_stored += (uint32_t)echo_est[PART_LEN]; - *far_energy += (uint32_t)far_spectrum[PART_LEN]; - *echo_energy_adapt += aecm->channelAdapt16[PART_LEN] * far_spectrum[PART_LEN]; -} - -void WebRtcAecm_StoreAdaptiveChannelNeon(AecmCore* aecm, - const uint16_t* far_spectrum, - int32_t* echo_est) { - RTC_DCHECK_EQ(0, (uintptr_t)echo_est % 32); - RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelStored % 16); - RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt16 % 16); - - // This is C code of following optimized code. - // During startup we store the channel every block. - // memcpy(aecm->channelStored, - // aecm->channelAdapt16, - // sizeof(int16_t) * PART_LEN1); - // Recalculate echo estimate - // for (i = 0; i < PART_LEN; i += 4) { - // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - // far_spectrum[i]); - // echo_est[i + 1] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1], - // far_spectrum[i + 1]); - // echo_est[i + 2] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2], - // far_spectrum[i + 2]); - // echo_est[i + 3] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3], - // far_spectrum[i + 3]); - // } - // echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - // far_spectrum[i]); - const uint16_t* far_spectrum_p = far_spectrum; - int16_t* start_adapt_p = aecm->channelAdapt16; - int16_t* start_stored_p = aecm->channelStored; - const int16_t* end_stored_p = aecm->channelStored + PART_LEN; - int32_t* echo_est_p = echo_est; - - uint16x8_t far_spectrum_v; - int16x8_t adapt_v; - uint32x4_t echo_est_v_low, echo_est_v_high; - - while (start_stored_p < end_stored_p) { - far_spectrum_v = vld1q_u16(far_spectrum_p); - adapt_v = vld1q_s16(start_adapt_p); - - vst1q_s16(start_stored_p, adapt_v); - - echo_est_v_low = vmull_u16(vget_low_u16(far_spectrum_v), - vget_low_u16(vreinterpretq_u16_s16(adapt_v))); - echo_est_v_high = vmull_u16(vget_high_u16(far_spectrum_v), - vget_high_u16(vreinterpretq_u16_s16(adapt_v))); - - vst1q_s32(echo_est_p, vreinterpretq_s32_u32(echo_est_v_low)); - vst1q_s32(echo_est_p + 4, vreinterpretq_s32_u32(echo_est_v_high)); - - far_spectrum_p += 8; - start_adapt_p += 8; - start_stored_p += 8; - echo_est_p += 8; - } - aecm->channelStored[PART_LEN] = aecm->channelAdapt16[PART_LEN]; - echo_est[PART_LEN] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[PART_LEN], - far_spectrum[PART_LEN]); -} - -void WebRtcAecm_ResetAdaptiveChannelNeon(AecmCore* aecm) { - RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelStored % 16); - RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt16 % 16); - RTC_DCHECK_EQ(0, (uintptr_t)aecm->channelAdapt32 % 32); - - // The C code of following optimized code. - // for (i = 0; i < PART_LEN1; i++) { - // aecm->channelAdapt16[i] = aecm->channelStored[i]; - // aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32( - // (int32_t)aecm->channelStored[i], 16); - // } - - int16_t* start_stored_p = aecm->channelStored; - int16_t* start_adapt16_p = aecm->channelAdapt16; - int32_t* start_adapt32_p = aecm->channelAdapt32; - const int16_t* end_stored_p = start_stored_p + PART_LEN; - - int16x8_t stored_v; - int32x4_t adapt32_v_low, adapt32_v_high; - - while (start_stored_p < end_stored_p) { - stored_v = vld1q_s16(start_stored_p); - vst1q_s16(start_adapt16_p, stored_v); - - adapt32_v_low = vshll_n_s16(vget_low_s16(stored_v), 16); - adapt32_v_high = vshll_n_s16(vget_high_s16(stored_v), 16); - - vst1q_s32(start_adapt32_p, adapt32_v_low); - vst1q_s32(start_adapt32_p + 4, adapt32_v_high); - - start_stored_p += 8; - start_adapt16_p += 8; - start_adapt32_p += 8; - } - aecm->channelAdapt16[PART_LEN] = aecm->channelStored[PART_LEN]; - aecm->channelAdapt32[PART_LEN] = (int32_t)aecm->channelStored[PART_LEN] << 16; -} - -} // namespace webrtc diff --git a/modules/audio_processing/aecm/aecm_defines.h b/modules/audio_processing/aecm/aecm_defines.h deleted file mode 100644 index 5805549e2c4..00000000000 --- a/modules/audio_processing/aecm/aecm_defines.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ -#define MODULES_AUDIO_PROCESSING_AECM_AECM_DEFINES_H_ - -#define AECM_DYNAMIC_Q /* Turn on/off dynamic Q-domain. */ - -/* Algorithm parameters */ -#define FRAME_LEN 80 /* Total frame length, 10 ms. */ - -#define PART_LEN 64 /* Length of partition. */ -#define PART_LEN_SHIFT 7 /* Length of (PART_LEN * 2) in base 2. */ - -#define PART_LEN1 (PART_LEN + 1) /* Unique fft coefficients. */ -#define PART_LEN2 (PART_LEN << 1) /* Length of partition * 2. */ -#define PART_LEN4 (PART_LEN << 2) /* Length of partition * 4. */ -#define FAR_BUF_LEN PART_LEN4 /* Length of buffers. */ -#define MAX_DELAY 100 - -/* Counter parameters */ -#define CONV_LEN 512 /* Convergence length used at startup. */ -#define CONV_LEN2 (CONV_LEN << 1) /* Used at startup. */ - -/* Energy parameters */ -#define MAX_BUF_LEN 64 /* History length of energy signals. */ -#define FAR_ENERGY_MIN 1025 /* Lowest Far energy level: At least 2 */ - /* in energy. */ -#define FAR_ENERGY_DIFF 929 /* Allowed difference between max */ - /* and min. */ -#define ENERGY_DEV_OFFSET 0 /* The energy error offset in Q8. */ -#define ENERGY_DEV_TOL 400 /* The energy estimation tolerance (Q8). */ -#define FAR_ENERGY_VAD_REGION 230 /* Far VAD tolerance region. */ - -/* Stepsize parameters */ -#define MU_MIN 10 /* Min stepsize 2^-MU_MIN (far end energy */ - /* dependent). */ -#define MU_MAX 1 /* Max stepsize 2^-MU_MAX (far end energy */ - /* dependent). */ -#define MU_DIFF 9 /* MU_MIN - MU_MAX */ - -/* Channel parameters */ -#define MIN_MSE_COUNT 20 /* Min number of consecutive blocks with enough */ - /* far end energy to compare channel estimates. */ -#define MIN_MSE_DIFF 29 /* The ratio between adapted and stored channel to */ - /* accept a new storage (0.8 in Q-MSE_RESOLUTION). */ -#define MSE_RESOLUTION 5 /* MSE parameter resolution. */ -#define RESOLUTION_CHANNEL16 12 /* W16 Channel in Q-RESOLUTION_CHANNEL16. */ -#define RESOLUTION_CHANNEL32 28 /* W32 Channel in Q-RESOLUTION_CHANNEL. */ -#define CHANNEL_VAD 16 /* Minimum energy in frequency band */ - /* to update channel. */ - -/* Suppression gain parameters: SUPGAIN parameters in Q-(RESOLUTION_SUPGAIN). */ -#define RESOLUTION_SUPGAIN 8 /* Channel in Q-(RESOLUTION_SUPGAIN). */ -#define SUPGAIN_DEFAULT (1 << RESOLUTION_SUPGAIN) /* Default. */ -#define SUPGAIN_ERROR_PARAM_A 3072 /* Estimation error parameter */ - /* (Maximum gain) (8 in Q8). */ -#define SUPGAIN_ERROR_PARAM_B 1536 /* Estimation error parameter */ - /* (Gain before going down). */ -#define SUPGAIN_ERROR_PARAM_D SUPGAIN_DEFAULT /* Estimation error parameter */ -/* (Should be the same as Default) (1 in Q8). */ -#define SUPGAIN_EPC_DT 200 /* SUPGAIN_ERROR_PARAM_C * ENERGY_DEV_TOL */ - -/* Defines for "check delay estimation" */ -#define CORR_WIDTH 31 /* Number of samples to correlate over. */ -#define CORR_MAX 16 /* Maximum correlation offset. */ -#define CORR_MAX_BUF 63 -#define CORR_DEV 4 -#define CORR_MAX_LEVEL 20 -#define CORR_MAX_LOW 4 -#define CORR_BUF_LEN (CORR_MAX << 1) + 1 -/* Note that CORR_WIDTH + 2*CORR_MAX <= MAX_BUF_LEN. */ - -#define ONE_Q14 (1 << 14) - -/* NLP defines */ -#define NLP_COMP_LOW 3277 /* 0.2 in Q14 */ -#define NLP_COMP_HIGH ONE_Q14 /* 1 in Q14 */ - -#endif diff --git a/modules/audio_processing/aecm/echo_control_mobile.cc b/modules/audio_processing/aecm/echo_control_mobile.cc deleted file mode 100644 index d09556de1fb..00000000000 --- a/modules/audio_processing/aecm/echo_control_mobile.cc +++ /dev/null @@ -1,601 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/aecm/echo_control_mobile.h" - -#include -#include -#include -#ifdef AEC_DEBUG -#include -#endif - -#include "modules/audio_processing/aecm/aecm_core.h" - -extern "C" { -#include "common_audio/ring_buffer.h" -#include "common_audio/signal_processing/include/signal_processing_library.h" -#include "modules/audio_processing/aecm/aecm_defines.h" -} - -namespace webrtc { - -namespace { - -#define BUF_SIZE_FRAMES 50 // buffer size (frames) -// Maximum length of resampled signal. Must be an integer multiple of frames -// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN -// The factor of 2 handles wb, and the + 1 is as a safety margin -#define MAX_RESAMP_LEN (5 * FRAME_LEN) - -const size_t kBufSizeSamp = - BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) -const int kSampMsNb = 8; // samples per ms in nb -// Target suppression levels for nlp modes -// log{0.001, 0.00001, 0.00000001} -const int kInitCheck = 42; - -typedef struct { - int sampFreq; - int scSampFreq; - short bufSizeStart; - int knownDelay; - - // Stores the last frame added to the farend buffer - short farendOld[2][FRAME_LEN]; - short initFlag; // indicates if AEC has been initialized - - // Variables used for averaging far end buffer size - short counter; - short sum; - short firstVal; - short checkBufSizeCtr; - - // Variables used for delay shifts - short msInSndCardBuf; - short filtDelay; - int timeForDelayChange; - int ECstartup; - int checkBuffSize; - int delayChange; - short lastDelayDiff; - - int16_t echoMode; - -#ifdef AEC_DEBUG - FILE* bufFile; - FILE* delayFile; - FILE* preCompFile; - FILE* postCompFile; -#endif // AEC_DEBUG - // Structures - RingBuffer* farendBuf; - - AecmCore* aecmCore; -} AecMobile; - -} // namespace - -// Estimates delay to set the position of the farend buffer read pointer -// (controlled by knownDelay) -static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf); - -// Stuffs the farend buffer if the estimated delay is too large -static int WebRtcAecm_DelayComp(AecMobile* aecm); - -void* WebRtcAecm_Create() { - // Allocate zero-filled memory. - AecMobile* aecm = static_cast(calloc(1, sizeof(AecMobile))); - - aecm->aecmCore = WebRtcAecm_CreateCore(); - if (!aecm->aecmCore) { - WebRtcAecm_Free(aecm); - return nullptr; - } - - aecm->farendBuf = WebRtc_CreateBuffer(kBufSizeSamp, sizeof(int16_t)); - if (!aecm->farendBuf) { - WebRtcAecm_Free(aecm); - return nullptr; - } - -#ifdef AEC_DEBUG - aecm->aecmCore->farFile = fopen("aecFar.pcm", "wb"); - aecm->aecmCore->nearFile = fopen("aecNear.pcm", "wb"); - aecm->aecmCore->outFile = fopen("aecOut.pcm", "wb"); - // aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb"); - - aecm->bufFile = fopen("aecBuf.dat", "wb"); - aecm->delayFile = fopen("aecDelay.dat", "wb"); - aecm->preCompFile = fopen("preComp.pcm", "wb"); - aecm->postCompFile = fopen("postComp.pcm", "wb"); -#endif // AEC_DEBUG - return aecm; -} - -void WebRtcAecm_Free(void* aecmInst) { - AecMobile* aecm = static_cast(aecmInst); - - if (aecm == nullptr) { - return; - } - -#ifdef AEC_DEBUG - fclose(aecm->aecmCore->farFile); - fclose(aecm->aecmCore->nearFile); - fclose(aecm->aecmCore->outFile); - // fclose(aecm->aecmCore->outLpFile); - - fclose(aecm->bufFile); - fclose(aecm->delayFile); - fclose(aecm->preCompFile); - fclose(aecm->postCompFile); -#endif // AEC_DEBUG - WebRtcAecm_FreeCore(aecm->aecmCore); - WebRtc_FreeBuffer(aecm->farendBuf); - free(aecm); -} - -int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq) { - AecMobile* aecm = static_cast(aecmInst); - AecmConfig aecConfig; - - if (aecm == nullptr) { - return -1; - } - - if (sampFreq != 8000 && sampFreq != 16000) { - return AECM_BAD_PARAMETER_ERROR; - } - aecm->sampFreq = sampFreq; - - // Initialize AECM core - if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1) { - return AECM_UNSPECIFIED_ERROR; - } - - // Initialize farend buffer - WebRtc_InitBuffer(aecm->farendBuf); - - aecm->initFlag = kInitCheck; // indicates that initialization has been done - - aecm->delayChange = 1; - - aecm->sum = 0; - aecm->counter = 0; - aecm->checkBuffSize = 1; - aecm->firstVal = 0; - - aecm->ECstartup = 1; - aecm->bufSizeStart = 0; - aecm->checkBufSizeCtr = 0; - aecm->filtDelay = 0; - aecm->timeForDelayChange = 0; - aecm->knownDelay = 0; - aecm->lastDelayDiff = 0; - - memset(&aecm->farendOld, 0, sizeof(aecm->farendOld)); - - // Default settings. - aecConfig.cngMode = AecmTrue; - aecConfig.echoMode = 3; - - if (WebRtcAecm_set_config(aecm, aecConfig) == -1) { - return AECM_UNSPECIFIED_ERROR; - } - - return 0; -} - -// Returns any error that is caused when buffering the -// farend signal. -int32_t WebRtcAecm_GetBufferFarendError(void* aecmInst, - const int16_t* farend, - size_t nrOfSamples) { - AecMobile* aecm = static_cast(aecmInst); - - if (aecm == nullptr) - return -1; - - if (farend == nullptr) - return AECM_NULL_POINTER_ERROR; - - if (aecm->initFlag != kInitCheck) - return AECM_UNINITIALIZED_ERROR; - - if (nrOfSamples != 80 && nrOfSamples != 160) - return AECM_BAD_PARAMETER_ERROR; - - return 0; -} - -int32_t WebRtcAecm_BufferFarend(void* aecmInst, - const int16_t* farend, - size_t nrOfSamples) { - AecMobile* aecm = static_cast(aecmInst); - - const int32_t err = - WebRtcAecm_GetBufferFarendError(aecmInst, farend, nrOfSamples); - - if (err != 0) - return err; - - // TODO(unknown): Is this really a good idea? - if (!aecm->ECstartup) { - WebRtcAecm_DelayComp(aecm); - } - - WebRtc_WriteBuffer(aecm->farendBuf, farend, nrOfSamples); - - return 0; -} - -int32_t WebRtcAecm_Process(void* aecmInst, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* out, - size_t nrOfSamples, - int16_t msInSndCardBuf) { - AecMobile* aecm = static_cast(aecmInst); - int32_t retVal = 0; - size_t i; - short nmbrOfFilledBuffers; - size_t nBlocks10ms; - size_t nFrames; -#ifdef AEC_DEBUG - short msInAECBuf; -#endif - - if (aecm == nullptr) { - return -1; - } - - if (nearendNoisy == nullptr) { - return AECM_NULL_POINTER_ERROR; - } - - if (out == nullptr) { - return AECM_NULL_POINTER_ERROR; - } - - if (aecm->initFlag != kInitCheck) { - return AECM_UNINITIALIZED_ERROR; - } - - if (nrOfSamples != 80 && nrOfSamples != 160) { - return AECM_BAD_PARAMETER_ERROR; - } - - if (msInSndCardBuf < 0) { - msInSndCardBuf = 0; - retVal = AECM_BAD_PARAMETER_WARNING; - } else if (msInSndCardBuf > 500) { - msInSndCardBuf = 500; - retVal = AECM_BAD_PARAMETER_WARNING; - } - msInSndCardBuf += 10; - aecm->msInSndCardBuf = msInSndCardBuf; - - nFrames = nrOfSamples / FRAME_LEN; - nBlocks10ms = nFrames / aecm->aecmCore->mult; - - if (aecm->ECstartup) { - if (nearendClean == nullptr) { - if (out != nearendNoisy) { - memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples); - } - } else if (out != nearendClean) { - memcpy(out, nearendClean, sizeof(short) * nrOfSamples); - } - - nmbrOfFilledBuffers = - (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; - // The AECM is in the start up mode - // AECM is disabled until the soundcard buffer and farend buffers are OK - - // Mechanism to ensure that the soundcard buffer is reasonably stable. - if (aecm->checkBuffSize) { - aecm->checkBufSizeCtr++; - // Before we fill up the far end buffer we require the amount of data on - // the sound card to be stable (+/-8 ms) compared to the first value. This - // comparison is made during the following 4 consecutive frames. If it - // seems to be stable then we start to fill up the far end buffer. - - if (aecm->counter == 0) { - aecm->firstVal = aecm->msInSndCardBuf; - aecm->sum = 0; - } - - if (abs(aecm->firstVal - aecm->msInSndCardBuf) < - WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb)) { - aecm->sum += aecm->msInSndCardBuf; - aecm->counter++; - } else { - aecm->counter = 0; - } - - if (aecm->counter * nBlocks10ms >= 6) { - // The farend buffer size is determined in blocks of 80 samples - // Use 75% of the average value of the soundcard buffer - aecm->bufSizeStart = WEBRTC_SPL_MIN( - (3 * aecm->sum * aecm->aecmCore->mult) / (aecm->counter * 40), - BUF_SIZE_FRAMES); - // buffersize has now been determined - aecm->checkBuffSize = 0; - } - - if (aecm->checkBufSizeCtr * nBlocks10ms > 50) { - // for really bad sound cards, don't disable echocanceller for more than - // 0.5 sec - aecm->bufSizeStart = WEBRTC_SPL_MIN( - (3 * aecm->msInSndCardBuf * aecm->aecmCore->mult) / 40, - BUF_SIZE_FRAMES); - aecm->checkBuffSize = 0; - } - } - - // if checkBuffSize changed in the if-statement above - if (!aecm->checkBuffSize) { - // soundcard buffer is now reasonably stable - // When the far end buffer is filled with approximately the same amount of - // data as the amount on the sound card we end the start up phase and - // start to cancel echoes. - - if (nmbrOfFilledBuffers == aecm->bufSizeStart) { - aecm->ECstartup = 0; // Enable the AECM - } else if (nmbrOfFilledBuffers > aecm->bufSizeStart) { - WebRtc_MoveReadPtr(aecm->farendBuf, - (int)WebRtc_available_read(aecm->farendBuf) - - (int)aecm->bufSizeStart * FRAME_LEN); - aecm->ECstartup = 0; - } - } - - } else { - // AECM is enabled - - // Note only 1 block supported for nb and 2 blocks for wb - for (i = 0; i < nFrames; i++) { - int16_t farend[FRAME_LEN]; - const int16_t* farend_ptr = nullptr; - - nmbrOfFilledBuffers = - (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN; - - // Check that there is data in the far end buffer - if (nmbrOfFilledBuffers > 0) { - // Get the next 80 samples from the farend buffer - WebRtc_ReadBuffer(aecm->farendBuf, (void**)&farend_ptr, farend, - FRAME_LEN); - - // Always store the last frame for use when we run out of data - memcpy(&(aecm->farendOld[i][0]), farend_ptr, FRAME_LEN * sizeof(short)); - } else { - // We have no data so we use the last played frame - memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short)); - farend_ptr = farend; - } - - // Call buffer delay estimator when all data is extracted, - // i,e. i = 0 for NB and i = 1 for WB - if ((i == 0 && aecm->sampFreq == 8000) || - (i == 1 && aecm->sampFreq == 16000)) { - WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf); - } - - // Call the AECM - /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i], - &out[FRAME_LEN * i], aecm->knownDelay);*/ - if (WebRtcAecm_ProcessFrame( - aecm->aecmCore, farend_ptr, &nearendNoisy[FRAME_LEN * i], - (nearendClean ? &nearendClean[FRAME_LEN * i] : nullptr), - &out[FRAME_LEN * i]) == -1) - return -1; - } - } - -#ifdef AEC_DEBUG - msInAECBuf = (short)WebRtc_available_read(aecm->farendBuf) / - (kSampMsNb * aecm->aecmCore->mult); - fwrite(&msInAECBuf, 2, 1, aecm->bufFile); - fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile); -#endif - - return retVal; -} - -int32_t WebRtcAecm_set_config(void* aecmInst, AecmConfig config) { - AecMobile* aecm = static_cast(aecmInst); - - if (aecm == nullptr) { - return -1; - } - - if (aecm->initFlag != kInitCheck) { - return AECM_UNINITIALIZED_ERROR; - } - - if (config.cngMode != AecmFalse && config.cngMode != AecmTrue) { - return AECM_BAD_PARAMETER_ERROR; - } - aecm->aecmCore->cngMode = config.cngMode; - - if (config.echoMode < 0 || config.echoMode > 4) { - return AECM_BAD_PARAMETER_ERROR; - } - aecm->echoMode = config.echoMode; - - if (aecm->echoMode == 0) { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3; - aecm->aecmCore->supGainErrParamDiffAB = - (SUPGAIN_ERROR_PARAM_A >> 3) - (SUPGAIN_ERROR_PARAM_B >> 3); - aecm->aecmCore->supGainErrParamDiffBD = - (SUPGAIN_ERROR_PARAM_B >> 3) - (SUPGAIN_ERROR_PARAM_D >> 3); - } else if (aecm->echoMode == 1) { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2; - aecm->aecmCore->supGainErrParamDiffAB = - (SUPGAIN_ERROR_PARAM_A >> 2) - (SUPGAIN_ERROR_PARAM_B >> 2); - aecm->aecmCore->supGainErrParamDiffBD = - (SUPGAIN_ERROR_PARAM_B >> 2) - (SUPGAIN_ERROR_PARAM_D >> 2); - } else if (aecm->echoMode == 2) { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1; - aecm->aecmCore->supGainErrParamDiffAB = - (SUPGAIN_ERROR_PARAM_A >> 1) - (SUPGAIN_ERROR_PARAM_B >> 1); - aecm->aecmCore->supGainErrParamDiffBD = - (SUPGAIN_ERROR_PARAM_B >> 1) - (SUPGAIN_ERROR_PARAM_D >> 1); - } else if (aecm->echoMode == 3) { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; - aecm->aecmCore->supGainErrParamDiffAB = - SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; - aecm->aecmCore->supGainErrParamDiffBD = - SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; - } else if (aecm->echoMode == 4) { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1; - aecm->aecmCore->supGainErrParamDiffAB = - (SUPGAIN_ERROR_PARAM_A << 1) - (SUPGAIN_ERROR_PARAM_B << 1); - aecm->aecmCore->supGainErrParamDiffBD = - (SUPGAIN_ERROR_PARAM_B << 1) - (SUPGAIN_ERROR_PARAM_D << 1); - } - - return 0; -} - -int32_t WebRtcAecm_InitEchoPath(void* aecmInst, - const void* echo_path, - size_t size_bytes) { - AecMobile* aecm = static_cast(aecmInst); - const int16_t* echo_path_ptr = static_cast(echo_path); - - if (aecmInst == nullptr) { - return -1; - } - if (echo_path == nullptr) { - return AECM_NULL_POINTER_ERROR; - } - if (size_bytes != WebRtcAecm_echo_path_size_bytes()) { - // Input channel size does not match the size of AECM - return AECM_BAD_PARAMETER_ERROR; - } - if (aecm->initFlag != kInitCheck) { - return AECM_UNINITIALIZED_ERROR; - } - - WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr); - - return 0; -} - -int32_t WebRtcAecm_GetEchoPath(void* aecmInst, - void* echo_path, - size_t size_bytes) { - AecMobile* aecm = static_cast(aecmInst); - int16_t* echo_path_ptr = static_cast(echo_path); - - if (aecmInst == nullptr) { - return -1; - } - if (echo_path == nullptr) { - return AECM_NULL_POINTER_ERROR; - } - if (size_bytes != WebRtcAecm_echo_path_size_bytes()) { - // Input channel size does not match the size of AECM - return AECM_BAD_PARAMETER_ERROR; - } - if (aecm->initFlag != kInitCheck) { - return AECM_UNINITIALIZED_ERROR; - } - - memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes); - return 0; -} - -size_t WebRtcAecm_echo_path_size_bytes() { - return (PART_LEN1 * sizeof(int16_t)); -} - -static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf) { - short delayNew, nSampSndCard; - short nSampFar = (short)WebRtc_available_read(aecm->farendBuf); - short diff; - - nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; - - delayNew = nSampSndCard - nSampFar; - - if (delayNew < FRAME_LEN) { - WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN); - delayNew += FRAME_LEN; - } - - aecm->filtDelay = - WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10); - - diff = aecm->filtDelay - aecm->knownDelay; - if (diff > 224) { - if (aecm->lastDelayDiff < 96) { - aecm->timeForDelayChange = 0; - } else { - aecm->timeForDelayChange++; - } - } else if (diff < 96 && aecm->knownDelay > 0) { - if (aecm->lastDelayDiff > 224) { - aecm->timeForDelayChange = 0; - } else { - aecm->timeForDelayChange++; - } - } else { - aecm->timeForDelayChange = 0; - } - aecm->lastDelayDiff = diff; - - if (aecm->timeForDelayChange > 25) { - aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0); - } - return 0; -} - -static int WebRtcAecm_DelayComp(AecMobile* aecm) { - int nSampFar = (int)WebRtc_available_read(aecm->farendBuf); - int nSampSndCard, delayNew, nSampAdd; - const int maxStuffSamp = 10 * FRAME_LEN; - - nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; - delayNew = nSampSndCard - nSampFar; - - if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult) { - // The difference of the buffer sizes is larger than the maximum - // allowed known delay. Compensate by stuffing the buffer. - nSampAdd = - (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar), FRAME_LEN)); - nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); - - WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd); - aecm->delayChange = 1; // the delay needs to be updated - } - - return 0; -} - -} // namespace webrtc diff --git a/modules/audio_processing/aecm/echo_control_mobile.h b/modules/audio_processing/aecm/echo_control_mobile.h deleted file mode 100644 index ee780524ded..00000000000 --- a/modules/audio_processing/aecm/echo_control_mobile.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ -#define MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ - -#include -#include - -namespace webrtc { - -enum { AecmFalse = 0, AecmTrue }; - -// Errors -#define AECM_UNSPECIFIED_ERROR 12000 -#define AECM_UNSUPPORTED_FUNCTION_ERROR 12001 -#define AECM_UNINITIALIZED_ERROR 12002 -#define AECM_NULL_POINTER_ERROR 12003 -#define AECM_BAD_PARAMETER_ERROR 12004 - -// Warnings -#define AECM_BAD_PARAMETER_WARNING 12100 - -typedef struct { - int16_t cngMode; // AECM_FALSE, AECM_TRUE (default) - int16_t echoMode; // 0, 1, 2, 3 (default), 4 -} AecmConfig; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Allocates the memory needed by the AECM. The memory needs to be - * initialized separately using the WebRtcAecm_Init() function. - * Returns a pointer to the instance and a nullptr at failure. - */ -void* WebRtcAecm_Create(); - -/* - * This function releases the memory allocated by WebRtcAecm_Create() - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - */ -void WebRtcAecm_Free(void* aecmInst); - -/* - * Initializes an AECM instance. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * int32_t sampFreq Sampling frequency of data - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 1200-12004,12100: error/warning - */ -int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq); - -/* - * Inserts an 80 or 160 sample block of data into the farend buffer. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * int16_t* farend In buffer containing one frame of - * farend signal - * int16_t nrOfSamples Number of samples in farend buffer - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 1200-12004,12100: error/warning - */ -int32_t WebRtcAecm_BufferFarend(void* aecmInst, - const int16_t* farend, - size_t nrOfSamples); - -/* - * Reports any errors that would arise when buffering a farend buffer. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * int16_t* farend In buffer containing one frame of - * farend signal - * int16_t nrOfSamples Number of samples in farend buffer - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 1200-12004,12100: error/warning - */ -int32_t WebRtcAecm_GetBufferFarendError(void* aecmInst, - const int16_t* farend, - size_t nrOfSamples); - -/* - * Runs the AECM on an 80 or 160 sample blocks of data. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * int16_t* nearendNoisy In buffer containing one frame of - * reference nearend+echo signal. If - * noise reduction is active, provide - * the noisy signal here. - * int16_t* nearendClean In buffer containing one frame of - * nearend+echo signal. If noise - * reduction is active, provide the - * clean signal here. Otherwise pass a - * NULL pointer. - * int16_t nrOfSamples Number of samples in nearend buffer - * int16_t msInSndCardBuf Delay estimate for sound card and - * system buffers - * - * Outputs Description - * ------------------------------------------------------------------- - * int16_t* out Out buffer, one frame of processed nearend - * int32_t return 0: OK - * 1200-12004,12100: error/warning - */ -int32_t WebRtcAecm_Process(void* aecmInst, - const int16_t* nearendNoisy, - const int16_t* nearendClean, - int16_t* out, - size_t nrOfSamples, - int16_t msInSndCardBuf); - -/* - * This function enables the user to set certain parameters on-the-fly - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * AecmConfig config Config instance that contains all - * properties to be set - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 1200-12004,12100: error/warning - */ -int32_t WebRtcAecm_set_config(void* aecmInst, AecmConfig config); - -/* - * This function enables the user to set the echo path on-the-fly. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * void* echo_path Pointer to the echo path to be set - * size_t size_bytes Size in bytes of the echo path - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 1200-12004,12100: error/warning - */ -int32_t WebRtcAecm_InitEchoPath(void* aecmInst, - const void* echo_path, - size_t size_bytes); - -/* - * This function enables the user to get the currently used echo path - * on-the-fly - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecmInst Pointer to the AECM instance - * void* echo_path Pointer to echo path - * size_t size_bytes Size in bytes of the echo path - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 1200-12004,12100: error/warning - */ -int32_t WebRtcAecm_GetEchoPath(void* aecmInst, - void* echo_path, - size_t size_bytes); - -/* - * This function enables the user to get the echo path size in bytes - * - * Outputs Description - * ------------------------------------------------------------------- - * size_t return Size in bytes - */ -size_t WebRtcAecm_echo_path_size_bytes(); - -#ifdef __cplusplus -} -#endif - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AECM_ECHO_CONTROL_MOBILE_H_ diff --git a/modules/audio_processing/agc/BUILD.gn b/modules/audio_processing/agc/BUILD.gn index 090fbd46043..498971fbe6e 100644 --- a/modules/audio_processing/agc/BUILD.gn +++ b/modules/audio_processing/agc/BUILD.gn @@ -24,12 +24,10 @@ rtc_library("agc") { "..:apm_logging", "..:audio_buffer", "..:audio_frame_view", - "../../../api:array_view", "../../../api:field_trials_view", "../../../api/audio:audio_processing", "../../../api/environment", "../../../common_audio", - "../../../common_audio:common_audio_c", "../../../rtc_base:checks", "../../../rtc_base:gtest_prod", "../../../rtc_base:logging", @@ -38,7 +36,6 @@ rtc_library("agc") { "../agc2:clipping_predictor", "../agc2:gain_map", "../agc2:input_volume_stats_reporter", - "../vad", ] } @@ -52,7 +49,6 @@ rtc_library("level_estimation") { "utility.h", ] deps = [ - "../../../api:array_view", "../../../rtc_base:checks", "../vad", ] @@ -75,12 +71,9 @@ rtc_library("legacy_agc") { ] deps = [ - "../../../common_audio", "../../../common_audio:common_audio_c", "../../../common_audio:common_audio_cc", - "../../../common_audio/third_party/ooura:fft_size_256", "../../../rtc_base:checks", - "../../../system_wrappers", ] if (rtc_build_with_neon) { @@ -107,18 +100,12 @@ if (rtc_include_tests) { ":gain_control_interface", ":level_estimation", "..:audio_buffer", - "..:mocks", - "../../../api:array_view", - "../../../api:field_trials", "../../../api/audio:audio_processing", "../../../api/environment", "../../../api/environment:environment_factory", "../../../rtc_base:checks", - "../../../rtc_base:random", - "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", "../../../rtc_base:stringutils", - "../../../system_wrappers:metrics", "../../../test:create_test_field_trials", "../../../test:fileutils", "../../../test:test_support", diff --git a/modules/audio_processing/agc/agc.cc b/modules/audio_processing/agc/agc.cc index 29e84b43806..fb5a9ed1cbc 100644 --- a/modules/audio_processing/agc/agc.cc +++ b/modules/audio_processing/agc/agc.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc/loudness_histogram.h" #include "modules/audio_processing/agc/utility.h" #include "rtc_base/checks.h" @@ -39,7 +39,7 @@ Agc::Agc() Agc::~Agc() = default; -void Agc::Process(ArrayView audio) { +void Agc::Process(std::span audio) { const int sample_rate_hz = audio.size() * kNum10msFramesInOneSecond; RTC_DCHECK_LE(sample_rate_hz, kMaxSampleRateHz); vad_.ProcessChunk(audio.data(), audio.size(), sample_rate_hz); diff --git a/modules/audio_processing/agc/agc.h b/modules/audio_processing/agc/agc.h index dfc769bce60..f5bd1925874 100644 --- a/modules/audio_processing/agc/agc.h +++ b/modules/audio_processing/agc/agc.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/vad/voice_activity_detector.h" namespace webrtc { @@ -28,7 +28,7 @@ class Agc { // `audio` must be mono; in a multi-channel stream, provide the first (usually // left) channel. - virtual void Process(ArrayView audio); + virtual void Process(std::span audio); // Retrieves the difference between the target RMS level and the current // signal RMS level in dB. Returns true if an update is available and false diff --git a/modules/audio_processing/agc/agc_manager_direct.cc b/modules/audio_processing/agc/agc_manager_direct.cc index fb7555f6b94..3d5f7fa3d68 100644 --- a/modules/audio_processing/agc/agc_manager_direct.cc +++ b/modules/audio_processing/agc/agc_manager_direct.cc @@ -19,8 +19,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" @@ -200,7 +200,7 @@ void MonoAgc::Initialize() { is_first_frame_ = true; } -void MonoAgc::Process(ArrayView audio, +void MonoAgc::Process(std::span audio, std::optional rms_error_override) { new_compression_to_set_ = std::nullopt; @@ -669,7 +669,7 @@ void AgcManagerDirect::Process(const AudioBuffer& audio_buffer, } } -std::optional AgcManagerDirect::GetDigitalComressionGain() { +std::optional AgcManagerDirect::GetDigitalCompressionGain() { return new_compressions_to_set_[channel_controlling_gain_]; } diff --git a/modules/audio_processing/agc/agc_manager_direct.h b/modules/audio_processing/agc/agc_manager_direct.h index 9df71250b3a..04b2e1b9a7b 100644 --- a/modules/audio_processing/agc/agc_manager_direct.h +++ b/modules/audio_processing/agc/agc_manager_direct.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/environment/environment.h" #include "modules/audio_processing/agc/agc.h" @@ -104,7 +104,7 @@ class AgcManagerDirect final { // If available, returns the latest digital compression gain that has been // chosen. - std::optional GetDigitalComressionGain(); + std::optional GetDigitalCompressionGain(); // Returns true if clipping prediction is enabled. bool clipping_predictor_enabled() const { return !!clipping_predictor_; } @@ -217,7 +217,7 @@ class MonoAgc { // the (digital) compression gain to be applied by `agc_`. Must be called // after `HandleClipping()`. If `rms_error_override` has a value, RMS error // from AGC is overridden by it. - void Process(ArrayView audio, + void Process(std::span audio, std::optional rms_error_override); // Returns the recommended input volume. Must be called after `Process()`. diff --git a/modules/audio_processing/agc/agc_manager_direct_unittest.cc b/modules/audio_processing/agc/agc_manager_direct_unittest.cc index afdf592a327..2b45b57dc06 100644 --- a/modules/audio_processing/agc/agc_manager_direct_unittest.cc +++ b/modules/audio_processing/agc/agc_manager_direct_unittest.cc @@ -381,7 +381,7 @@ class AgcManagerDirectTestHelper { manager.AnalyzePreProcess(audio_buffer); manager.Process(audio_buffer, speech_probability_override, speech_level_override); - std::optional digital_gain = manager.GetDigitalComressionGain(); + std::optional digital_gain = manager.GetDigitalCompressionGain(); if (digital_gain) { mock_gain_control.set_compression_gain_db(*digital_gain); } @@ -400,7 +400,7 @@ class AgcManagerDirectTestHelper { EXPECT_CALL(*mock_agc, Process(_)).WillOnce(Return()); manager.Process(audio_buffer, speech_probability_override, speech_level_override); - std::optional new_digital_gain = manager.GetDigitalComressionGain(); + std::optional new_digital_gain = manager.GetDigitalCompressionGain(); if (new_digital_gain) { mock_gain_control.set_compression_gain_db(*new_digital_gain); } @@ -927,7 +927,7 @@ TEST_P(AgcManagerDirectParametrizedTest, NoActionWhileMuted) { GetOverrideOrEmpty(kSpeechLevelDbfs)); std::optional new_digital_gain = - helper.manager.GetDigitalComressionGain(); + helper.manager.GetDigitalCompressionGain(); if (new_digital_gain) { helper.mock_gain_control.set_compression_gain_db(*new_digital_gain); } diff --git a/modules/audio_processing/agc/mock_agc.h b/modules/audio_processing/agc/mock_agc.h index f958d16e221..8086aa9f01d 100644 --- a/modules/audio_processing/agc/mock_agc.h +++ b/modules/audio_processing/agc/mock_agc.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_AGC_MOCK_AGC_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc/agc.h" #include "test/gmock.h" @@ -22,7 +22,7 @@ namespace webrtc { class MockAgc : public Agc { public: ~MockAgc() override {} - MOCK_METHOD(void, Process, (ArrayView audio), (override)); + MOCK_METHOD(void, Process, (std::span audio), (override)); MOCK_METHOD(bool, GetRmsErrorDb, (int* error), (override)); MOCK_METHOD(void, Reset, (), (override)); MOCK_METHOD(int, set_target_level_dbfs, (int level), (override)); diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn index 0de1c32bd7f..0e3ee94a5a9 100644 --- a/modules/audio_processing/agc2/BUILD.gn +++ b/modules/audio_processing/agc2/BUILD.gn @@ -28,7 +28,6 @@ rtc_library("speech_level_estimator") { deps = [ ":common", "..:apm_logging", - "../../../api:array_view", "../../../api:field_trials_view", "../../../api/audio:audio_processing", "../../../rtc_base:checks", @@ -94,10 +93,7 @@ rtc_library("biquad_filter") { "biquad_filter.cc", "biquad_filter.h", ] - deps = [ - "../../../api:array_view", - "../../../rtc_base:checks", - ] + deps = [ "../../../rtc_base:checks" ] } rtc_library("clipping_predictor") { @@ -149,10 +145,7 @@ rtc_library("fixed_digital") { deps = [ ":common", "..:apm_logging", - "..:audio_frame_view", - "../../../api:array_view", "../../../api/audio:audio_frame_api", - "../../../common_audio", "../../../rtc_base:checks", "../../../rtc_base:gtest_prod", "../../../rtc_base:safe_conversions", @@ -216,7 +209,6 @@ rtc_library("input_volume_controller") { ":input_volume_stats_reporter", "..:audio_buffer", "..:audio_frame_view", - "../../../api:array_view", "../../../api:field_trials_view", "../../../api/audio:audio_processing", "../../../rtc_base:checks", @@ -233,11 +225,9 @@ rtc_library("noise_level_estimator") { "noise_level_estimator.h", ] deps = [ - ":biquad_filter", "..:apm_logging", "../../../api/audio:audio_frame_api", "../../../rtc_base:checks", - "../../../system_wrappers", ] visibility = [ @@ -310,7 +300,6 @@ rtc_library("speech_level_estimator_unittest") { "..:apm_logging", "../../../api/audio:audio_processing", "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", ] } @@ -326,10 +315,8 @@ rtc_library("adaptive_digital_gain_controller_unittest") { ":common", ":test_utils", "..:apm_logging", - "..:audio_frame_view", "../../../api/audio:audio_processing", "../../../common_audio", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", ] } @@ -343,7 +330,6 @@ rtc_library("gain_applier_unittest") { ":gain_applier", ":test_utils", "../../../api/audio:audio_frame_api", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", ] } @@ -360,7 +346,6 @@ rtc_library("saturation_protector_unittest") { ":common", ":saturation_protector", "..:apm_logging", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", ] } @@ -370,8 +355,6 @@ rtc_library("biquad_filter_unittests") { sources = [ "biquad_filter_unittest.cc" ] deps = [ ":biquad_filter", - "../../../api:array_view", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", ] } @@ -396,13 +379,9 @@ rtc_library("fixed_digital_unittests") { ":fixed_digital", ":test_utils", "..:apm_logging", - "..:audio_frame_view", - "../../../api:array_view", "../../../api/audio:audio_frame_api", "../../../common_audio", "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", - "../../../system_wrappers:metrics", "../../../test:test_support", ] } @@ -420,18 +399,13 @@ rtc_library("input_volume_controller_unittests") { deps = [ ":clipping_predictor", - ":gain_map", ":input_volume_controller", "..:audio_buffer", "..:audio_frame_view", - "../../../api:array_view", "../../../api/audio:audio_processing", "../../../api/environment:environment_factory", "../../../rtc_base:checks", - "../../../rtc_base:random", - "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", - "../../../rtc_base:stringutils", "../../../system_wrappers:metrics", "../../../test:fileutils", "../../../test:test_support", @@ -451,7 +425,6 @@ rtc_library("noise_estimator_unittests") { "../../../api:function_view", "../../../api/audio:audio_frame_api", "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", ] } @@ -462,10 +435,8 @@ rtc_library("vad_wrapper_unittests") { deps = [ ":common", ":vad_wrapper", - "../../../api:array_view", "../../../api/audio:audio_frame_api", "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../rtc_base:safe_compare", "../../../test:test_support", ] @@ -500,7 +471,6 @@ rtc_library("input_volume_stats_reporter") { "../../../rtc_base:checks", "../../../rtc_base:gtest_prod", "../../../rtc_base:logging", - "../../../rtc_base:safe_minmax", "../../../rtc_base:stringutils", "../../../system_wrappers:metrics", "//third_party/abseil-cpp/absl/strings:string_view", diff --git a/modules/audio_processing/agc2/biquad_filter.cc b/modules/audio_processing/agc2/biquad_filter.cc index 9ff831a04eb..e64521c0b08 100644 --- a/modules/audio_processing/agc2/biquad_filter.cc +++ b/modules/audio_processing/agc2/biquad_filter.cc @@ -11,8 +11,8 @@ #include "modules/audio_processing/agc2/biquad_filter.h" #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -31,7 +31,7 @@ void BiQuadFilter::Reset() { state_ = {}; } -void BiQuadFilter::Process(ArrayView x, ArrayView y) { +void BiQuadFilter::Process(std::span x, std::span y) { RTC_DCHECK_EQ(x.size(), y.size()); const float config_a0 = config_.a[0]; const float config_a1 = config_.a[1]; diff --git a/modules/audio_processing/agc2/biquad_filter.h b/modules/audio_processing/agc2/biquad_filter.h index 766a750ea8b..86135004403 100644 --- a/modules/audio_processing/agc2/biquad_filter.h +++ b/modules/audio_processing/agc2/biquad_filter.h @@ -11,7 +11,7 @@ #ifndef MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ #define MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_ -#include "api/array_view.h" +#include namespace webrtc { @@ -41,7 +41,7 @@ class BiQuadFilter { // Filters `x` and writes the output in `y`, which must have the same length // of `x`. In-place processing is supported. - void Process(ArrayView x, ArrayView y); + void Process(std::span x, std::span y); private: Config config_; diff --git a/modules/audio_processing/agc2/biquad_filter_unittest.cc b/modules/audio_processing/agc2/biquad_filter_unittest.cc index bdfdfba009a..f0906c82b2d 100644 --- a/modules/audio_processing/agc2/biquad_filter_unittest.cc +++ b/modules/audio_processing/agc2/biquad_filter_unittest.cc @@ -17,7 +17,8 @@ // TODO(bugs.webrtc.org/8948): Add when the issue is fixed. // #include "test/fpe_observer.h" -#include "api/array_view.h" +#include + #include "test/gtest.h" namespace webrtc { @@ -58,11 +59,11 @@ constexpr FloatArraySequence kBiQuadOutputSeq = { {{24.84286614f, -62.18094158f, 57.91488056f, -106.65685933f, 13.38760103f, -36.60367134f, -94.44880104f, -3.59920354f}}}}; -// Fails for every pair from two equally sized ArrayView views +// Fails for every pair from two equally sized std::span views // such that their relative error is above a given threshold. If the expected // value of a pair is 0, `tolerance` is used to check the absolute error. -void ExpectNearRelative(ArrayView expected, - ArrayView computed, +void ExpectNearRelative(std::span expected, + std::span computed, const float tolerance) { // The relative error is undefined when the expected value is 0. // When that happens, check the absolute error instead. `safe_den` is used diff --git a/modules/audio_processing/agc2/fixed_digital_level_estimator.cc b/modules/audio_processing/agc2/fixed_digital_level_estimator.cc index a84bb2c0b02..99a4534d100 100644 --- a/modules/audio_processing/agc2/fixed_digital_level_estimator.cc +++ b/modules/audio_processing/agc2/fixed_digital_level_estimator.cc @@ -15,7 +15,6 @@ #include #include -#include "api/array_view.h" #include "api/audio/audio_frame.h" #include "api/audio/audio_view.h" #include "modules/audio_processing/agc2/agc2_common.h" diff --git a/modules/audio_processing/agc2/input_volume_controller.cc b/modules/audio_processing/agc2/input_volume_controller.cc index 34acde78d33..ea78a67a8a2 100644 --- a/modules/audio_processing/agc2/input_volume_controller.cc +++ b/modules/audio_processing/agc2/input_volume_controller.cc @@ -133,19 +133,6 @@ int GetSpeechLevelRmsErrorDb(float speech_level_dbfs, return rms_error_db; } - -int GetTargetRangeMaxDbfs(const InputVolumeController::Config& config, - const FieldTrialsView& field_trials) { - if (field_trials.IsEnabled("WebRTC-Agc2MaxSpeechLevelExperimental")) { - RTC_LOG(LS_INFO) << "AGC2 input volume controller using experimental " - "maximum speech level"; - return config.target_range_experimental_max_dbfs; - } else { - RTC_LOG(LS_INFO) - << "AGC2 input volume controller using default maximum speech level"; - return config.target_range_max_dbfs; - } -} } // namespace MonoInputVolumeController::MonoInputVolumeController( @@ -388,7 +375,7 @@ InputVolumeController::InputVolumeController( frames_since_clipped_(config.clipped_wait_frames), clipping_rate_log_counter_(0), clipping_rate_log_(0.0f), - target_range_max_dbfs_(GetTargetRangeMaxDbfs(config, field_trials)), + target_range_max_dbfs_(config.target_range_max_dbfs), target_range_min_dbfs_(config.target_range_min_dbfs), channel_controllers_(num_capture_channels) { RTC_LOG(LS_INFO) diff --git a/modules/audio_processing/agc2/input_volume_controller.h b/modules/audio_processing/agc2/input_volume_controller.h index dbb9f7f773d..ab7f125d105 100644 --- a/modules/audio_processing/agc2/input_volume_controller.h +++ b/modules/audio_processing/agc2/input_volume_controller.h @@ -56,8 +56,7 @@ class InputVolumeController final { // adjustments are done based on the speech level. For speech levels below // and above the range, the targets `target_range_min_dbfs` and // `target_range_max_dbfs` are used, respectively. - int target_range_max_dbfs = -30; - int target_range_experimental_max_dbfs = -12; + int target_range_max_dbfs = -12; int target_range_min_dbfs = -50; // Number of wait frames between the recommended input volume updates. int update_input_volume_wait_frames = 100; diff --git a/modules/audio_processing/agc2/limiter.cc b/modules/audio_processing/agc2/limiter.cc index ca3e359da8d..c564a837443 100644 --- a/modules/audio_processing/agc2/limiter.cc +++ b/modules/audio_processing/agc2/limiter.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "modules/audio_processing/agc2/agc2_common.h" #include "modules/audio_processing/agc2/interpolated_gain_curve.h" @@ -38,7 +38,7 @@ constexpr float kAttackFirstSubframeInterpolationPower = 8.0f; void InterpolateFirstSubframe(float last_factor, float current_factor, - ArrayView subframe) { + std::span subframe) { const int n = dchecked_cast(subframe.size()); constexpr float p = kAttackFirstSubframeInterpolationPower; for (int i = 0; i < n; ++i) { @@ -60,7 +60,7 @@ void ComputePerSampleSubframeFactors( if (is_attack) { InterpolateFirstSubframe( scaling_factors[0], scaling_factors[1], - per_sample_scaling_factors.subview(0, subframe_size)); + per_sample_scaling_factors.subspan(0, subframe_size)); } for (size_t i = is_attack ? 1 : 0; i < num_subframes; ++i) { diff --git a/modules/audio_processing/agc2/rnn_vad/BUILD.gn b/modules/audio_processing/agc2/rnn_vad/BUILD.gn index 075e2a8110b..0f581c69228 100644 --- a/modules/audio_processing/agc2/rnn_vad/BUILD.gn +++ b/modules/audio_processing/agc2/rnn_vad/BUILD.gn @@ -32,10 +32,7 @@ rtc_library("rnn_vad") { ":rnn_vad_spectral_features", "..:biquad_filter", "..:cpu_features", - "../../../../api:array_view", "../../../../rtc_base:checks", - "../../../../rtc_base:safe_compare", - "../../../../rtc_base:safe_conversions", "//third_party/rnnoise:rnn_vad", ] } @@ -47,7 +44,6 @@ rtc_library("rnn_vad_auto_correlation") { ] deps = [ ":rnn_vad_common", - "../../../../api:array_view", "../../../../rtc_base:checks", "../../utility:pffft_wrapper", ] @@ -71,7 +67,6 @@ rtc_library("rnn_vad_lp_residual") { "lp_residual.h", ] deps = [ - "../../../../api:array_view", "../../../../rtc_base:checks", "../../../../rtc_base:safe_compare", ] @@ -95,7 +90,6 @@ rtc_library("rnn_vad_layers") { ":rnn_vad_common", ":vector_math", "..:cpu_features", - "../../../../api:array_view", "../../../../api:function_view", "../../../../rtc_base:checks", "../../../../rtc_base:safe_conversions", @@ -111,7 +105,6 @@ rtc_source_set("vector_math") { sources = [ "vector_math.h" ] deps = [ "..:cpu_features", - "../../../../api:array_view", "../../../../rtc_base:checks", "../../../../rtc_base:safe_conversions", "../../../../rtc_base/system:arch", @@ -131,7 +124,6 @@ if (current_cpu == "x86" || current_cpu == "x64") { } deps = [ ":vector_math", - "../../../../api:array_view", "../../../../rtc_base:checks", "../../../../rtc_base:safe_conversions", ] @@ -157,7 +149,6 @@ rtc_library("rnn_vad_pitch") { ":rnn_vad_common", ":vector_math", "..:cpu_features", - "../../../../api:array_view", "../../../../rtc_base:checks", "../../../../rtc_base:gtest_prod", "../../../../rtc_base:safe_compare", @@ -171,18 +162,12 @@ rtc_library("rnn_vad_pitch") { rtc_source_set("rnn_vad_ring_buffer") { sources = [ "ring_buffer.h" ] - deps = [ - "../../../../api:array_view", - "../../../../rtc_base:checks", - ] + deps = [ "../../../../rtc_base:checks" ] } rtc_source_set("rnn_vad_sequence_buffer") { sources = [ "sequence_buffer.h" ] - deps = [ - "../../../../api:array_view", - "../../../../rtc_base:checks", - ] + deps = [ "../../../../rtc_base:checks" ] } rtc_library("rnn_vad_spectral_features") { @@ -196,7 +181,6 @@ rtc_library("rnn_vad_spectral_features") { ":rnn_vad_common", ":rnn_vad_ring_buffer", ":rnn_vad_symmetric_matrix_buffer", - "../../../../api:array_view", "../../../../rtc_base:checks", "../../../../rtc_base:safe_compare", "../../utility:pffft_wrapper", @@ -206,7 +190,6 @@ rtc_library("rnn_vad_spectral_features") { rtc_source_set("rnn_vad_symmetric_matrix_buffer") { sources = [ "symmetric_matrix_buffer.h" ] deps = [ - "../../../../api:array_view", "../../../../rtc_base:checks", "../../../../rtc_base:safe_compare", ] @@ -222,7 +205,6 @@ if (rtc_include_tests) { deps = [ ":rnn_vad", ":rnn_vad_common", - "../../../../api:array_view", "../../../../api:scoped_refptr", "../../../../rtc_base:checks", "../../../../rtc_base:safe_compare", @@ -290,7 +272,6 @@ if (rtc_include_tests) { ":vector_math", "..:cpu_features", "../..:audioproc_test_utils", - "../../../../api:array_view", "../../../../common_audio/", "../../../../rtc_base:checks", "../../../../rtc_base:logging", @@ -319,12 +300,10 @@ if (rtc_include_tests) { ":rnn_vad", ":rnn_vad_common", "..:cpu_features", - "../../../../api:array_view", "../../../../common_audio", "../../../../rtc_base:checks", "../../../../rtc_base:logging", "../../../../rtc_base:safe_compare", - "../../../../test:test_support", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] diff --git a/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc b/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc index b5f9f758a3f..971e00d866d 100644 --- a/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc +++ b/modules/audio_processing/agc2/rnn_vad/auto_correlation.cc @@ -11,8 +11,8 @@ #include "modules/audio_processing/agc2/rnn_vad/auto_correlation.h" #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/utility/pffft_wrapper.h" #include "rtc_base/checks.h" @@ -47,8 +47,8 @@ AutoCorrelationCalculator::~AutoCorrelationCalculator() = default; // inverted lag equal to 0 that corresponds to a lag equal to the maximum // pitch period. void AutoCorrelationCalculator::ComputeOnPitchBuffer( - ArrayView pitch_buf, - ArrayView auto_corr) { + std::span pitch_buf, + std::span auto_corr) { RTC_DCHECK_LT(auto_corr.size(), kMaxPitch12kHz); RTC_DCHECK_GT(pitch_buf.size(), kMaxPitch12kHz); constexpr int kFftFrameSize = 1 << kAutoCorrelationFftOrder; diff --git a/modules/audio_processing/agc2/rnn_vad/auto_correlation.h b/modules/audio_processing/agc2/rnn_vad/auto_correlation.h index 127b2598609..edda13f7213 100644 --- a/modules/audio_processing/agc2/rnn_vad/auto_correlation.h +++ b/modules/audio_processing/agc2/rnn_vad/auto_correlation.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_AUTO_CORRELATION_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/utility/pffft_wrapper.h" @@ -32,8 +32,8 @@ class AutoCorrelationCalculator { // Computes the auto-correlation coefficients for a target pitch interval. // `auto_corr` indexes are inverted lags. - void ComputeOnPitchBuffer(ArrayView pitch_buf, - ArrayView auto_corr); + void ComputeOnPitchBuffer(std::span pitch_buf, + std::span auto_corr); private: Pffft fft_; diff --git a/modules/audio_processing/agc2/rnn_vad/features_extraction.cc b/modules/audio_processing/agc2/rnn_vad/features_extraction.cc index 4efb8fe3726..53362ea86ef 100644 --- a/modules/audio_processing/agc2/rnn_vad/features_extraction.cc +++ b/modules/audio_processing/agc2/rnn_vad/features_extraction.cc @@ -11,8 +11,9 @@ #include "modules/audio_processing/agc2/rnn_vad/features_extraction.h" #include +#include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/biquad_filter.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" @@ -54,8 +55,8 @@ void FeaturesExtractor::Reset() { } bool FeaturesExtractor::CheckSilenceComputeFeatures( - ArrayView samples, - ArrayView feature_vector) { + std::span samples, + std::span feature_vector) { // Pre-processing. if (use_high_pass_filter_) { std::array samples_filtered; @@ -76,17 +77,17 @@ bool FeaturesExtractor::CheckSilenceComputeFeatures( feature_vector[kFeatureVectorSize - 2] = 0.01f * (pitch_period_48kHz_ - 300); // Extract lagged frames (according to the estimated pitch period). RTC_DCHECK_LE(pitch_period_48kHz_ / 2, kMaxPitch24kHz); - auto lagged_frame = pitch_buf_24kHz_view_.subview( + auto lagged_frame = pitch_buf_24kHz_view_.subspan( kMaxPitch24kHz - pitch_period_48kHz_ / 2, kFrameSize20ms24kHz); // Analyze reference and lagged frames checking if silence has been detected // and write the feature vector. return spectral_features_extractor_.CheckSilenceComputeFeatures( - reference_frame_view_, {lagged_frame.data(), kFrameSize20ms24kHz}, - {feature_vector.data() + kNumLowerBands, kNumBands - kNumLowerBands}, - {feature_vector.data(), kNumLowerBands}, - {feature_vector.data() + kNumBands, kNumLowerBands}, - {feature_vector.data() + kNumBands + kNumLowerBands, kNumLowerBands}, - {feature_vector.data() + kNumBands + 2 * kNumLowerBands, kNumLowerBands}, + reference_frame_view_, lagged_frame.first(), + feature_vector.subspan(), + feature_vector.first(), + feature_vector.subspan(), + feature_vector.subspan(), + feature_vector.subspan(), &feature_vector[kFeatureVectorSize - 1]); } diff --git a/modules/audio_processing/agc2/rnn_vad/features_extraction.h b/modules/audio_processing/agc2/rnn_vad/features_extraction.h index 2928a7fa009..f952045d6fd 100644 --- a/modules/audio_processing/agc2/rnn_vad/features_extraction.h +++ b/modules/audio_processing/agc2/rnn_vad/features_extraction.h @@ -11,9 +11,9 @@ #ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ #define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_FEATURES_EXTRACTION_H_ +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/biquad_filter.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" @@ -37,8 +37,8 @@ class FeaturesExtractor { // `feature_vector` is partially written and therefore must not be used to // feed the VAD RNN. bool CheckSilenceComputeFeatures( - ArrayView samples, - ArrayView feature_vector); + std::span samples, + std::span feature_vector); private: const bool use_high_pass_filter_; @@ -47,11 +47,11 @@ class FeaturesExtractor { BiQuadFilter hpf_; SequenceBuffer pitch_buf_24kHz_; - ArrayView pitch_buf_24kHz_view_; + std::span pitch_buf_24kHz_view_; std::vector lp_residual_; - ArrayView lp_residual_view_; + std::span lp_residual_view_; PitchEstimator pitch_estimator_; - ArrayView reference_frame_view_; + std::span reference_frame_view_; SpectralFeaturesExtractor spectral_features_extractor_; int pitch_period_48kHz_; }; diff --git a/modules/audio_processing/agc2/rnn_vad/features_extraction_unittest.cc b/modules/audio_processing/agc2/rnn_vad/features_extraction_unittest.cc index 9f5b4d37ff3..b63198d4485 100644 --- a/modules/audio_processing/agc2/rnn_vad/features_extraction_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/features_extraction_unittest.cc @@ -11,9 +11,9 @@ #include "modules/audio_processing/agc2/rnn_vad/features_extraction.h" #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "rtc_base/numerics/safe_compare.h" @@ -43,7 +43,7 @@ bool PitchIsValid(float pitch_hz) { pitch_period <= kMaxPitch24kHz; } -void CreatePureTone(float amplitude, float freq_hz, ArrayView dst) { +void CreatePureTone(float amplitude, float freq_hz, std::span dst) { for (int i = 0; SafeLt(i, dst.size()); ++i) { dst[i] = amplitude * std::sin(2.f * kPi * freq_hz * i / kSampleRate24kHz); } @@ -53,15 +53,15 @@ void CreatePureTone(float amplitude, float freq_hz, ArrayView dst) { // For every frame, the output is written into `feature_vector`. Returns true // if silence is detected in the last frame. bool FeedTestData(FeaturesExtractor& features_extractor, - ArrayView samples, - ArrayView feature_vector) { + std::span samples, + std::span feature_vector) { // TODO(bugs.webrtc.org/8948): Add when the issue is fixed. // FloatingPointExceptionObserver fpe_observer; bool is_silence = true; const int num_frames = samples.size() / kFrameSize10ms24kHz; for (int i = 0; i < num_frames; ++i) { is_silence = features_extractor.CheckSilenceComputeFeatures( - {samples.data() + i * kFrameSize10ms24kHz, kFrameSize10ms24kHz}, + samples.subspan(i * kFrameSize10ms24kHz).first(), feature_vector); } return is_silence; @@ -81,7 +81,7 @@ TEST(RnnVadTest, FeatureExtractionLowHighPitch) { std::vector samples(kNumTestDataSize); std::vector feature_vector(kFeatureVectorSize); ASSERT_EQ(kFeatureVectorSize, dchecked_cast(feature_vector.size())); - ArrayView feature_vector_view( + std::span feature_vector_view( feature_vector.data(), kFeatureVectorSize); // Extract the normalized scalar feature that is proportional to the estimated diff --git a/modules/audio_processing/agc2/rnn_vad/lp_residual.cc b/modules/audio_processing/agc2/rnn_vad/lp_residual.cc index 6568f93e4dc..59605526991 100644 --- a/modules/audio_processing/agc2/rnn_vad/lp_residual.cc +++ b/modules/audio_processing/agc2/rnn_vad/lp_residual.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_compare.h" @@ -26,8 +26,8 @@ namespace { // Computes auto-correlation coefficients for `x` and writes them in // `auto_corr`. The lag values are in {0, ..., max_lag - 1}, where max_lag // equals the size of `auto_corr`. -void ComputeAutoCorrelation(ArrayView x, - ArrayView auto_corr) { +void ComputeAutoCorrelation(std::span x, + std::span auto_corr) { constexpr int max_lag = auto_corr.size(); RTC_DCHECK_LT(max_lag, x.size()); for (int lag = 0; lag < max_lag; ++lag) { @@ -37,7 +37,7 @@ void ComputeAutoCorrelation(ArrayView x, } // Applies denoising to the auto-correlation coefficients. -void DenoiseAutoCorrelation(ArrayView auto_corr) { +void DenoiseAutoCorrelation(std::span auto_corr) { // Assume -40 dB white noise floor. auto_corr[0] *= 1.0001f; // Hard-coded values obtained as @@ -52,8 +52,8 @@ void DenoiseAutoCorrelation(ArrayView auto_corr) { // Computes the initial inverse filter coefficients given the auto-correlation // coefficients of an input frame. void ComputeInitialInverseFilterCoefficients( - ArrayView auto_corr, - ArrayView lpc_coeffs) { + std::span auto_corr, + std::span lpc_coeffs) { float error = auto_corr[0]; for (int i = 0; i < kNumLpcCoefficients - 1; ++i) { float reflection_coeff = 0.f; @@ -87,8 +87,8 @@ void ComputeInitialInverseFilterCoefficients( } // namespace void ComputeAndPostProcessLpcCoefficients( - ArrayView x, - ArrayView lpc_coeffs) { + std::span x, + std::span lpc_coeffs) { std::array auto_corr; ComputeAutoCorrelation(x, auto_corr); if (auto_corr[0] == 0.f) { // Empty frame. @@ -113,9 +113,9 @@ void ComputeAndPostProcessLpcCoefficients( static_assert(kNumLpcCoefficients == 5, "Update `lpc_coeffs(_pre)`."); } -void ComputeLpResidual(ArrayView lpc_coeffs, - ArrayView x, - ArrayView y) { +void ComputeLpResidual(std::span lpc_coeffs, + std::span x, + std::span y) { RTC_DCHECK_GT(x.size(), kNumLpcCoefficients); RTC_DCHECK_EQ(x.size(), y.size()); // The code below implements the following operation: @@ -124,14 +124,13 @@ void ComputeLpResidual(ArrayView lpc_coeffs, // Edge case: i < kNumLpcCoefficients. y[0] = x[0]; for (int i = 1; i < kNumLpcCoefficients; ++i) { - y[i] = - std::inner_product(x.crend() - i, x.crend(), lpc_coeffs.cbegin(), x[i]); + y[i] = std::inner_product(x.rend() - i, x.rend(), lpc_coeffs.begin(), x[i]); } // Regular case. - auto last = x.crend(); + auto last = x.rend(); for (int i = kNumLpcCoefficients; SafeLt(i, y.size()); ++i, --last) { y[i] = std::inner_product(last - kNumLpcCoefficients, last, - lpc_coeffs.cbegin(), x[i]); + lpc_coeffs.begin(), x[i]); } } diff --git a/modules/audio_processing/agc2/rnn_vad/lp_residual.h b/modules/audio_processing/agc2/rnn_vad/lp_residual.h index f29dfe03ad6..115163709b3 100644 --- a/modules/audio_processing/agc2/rnn_vad/lp_residual.h +++ b/modules/audio_processing/agc2/rnn_vad/lp_residual.h @@ -13,7 +13,7 @@ #include -#include "api/array_view.h" +#include namespace webrtc { namespace rnn_vad { @@ -24,15 +24,15 @@ constexpr int kNumLpcCoefficients = 5; // Given a frame `x`, computes a post-processed version of LPC coefficients // tailored for pitch estimation. void ComputeAndPostProcessLpcCoefficients( - ArrayView x, - ArrayView lpc_coeffs); + std::span x, + std::span lpc_coeffs); // Computes the LP residual for the input frame `x` and the LPC coefficients // `lpc_coeffs`. `y` and `x` can point to the same array for in-place // computation. -void ComputeLpResidual(ArrayView lpc_coeffs, - ArrayView x, - ArrayView y); +void ComputeLpResidual(std::span lpc_coeffs, + std::span x, + std::span y); } // namespace rnn_vad } // namespace webrtc diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search.cc index d6fe28be58a..cd734d2d3b4 100644 --- a/modules/audio_processing/agc2/rnn_vad/pitch_search.cc +++ b/modules/audio_processing/agc2/rnn_vad/pitch_search.cc @@ -11,8 +11,8 @@ #include "modules/audio_processing/agc2/rnn_vad/pitch_search.h" #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h" @@ -30,11 +30,11 @@ PitchEstimator::PitchEstimator(const AvailableCpuFeatures& cpu_features) PitchEstimator::~PitchEstimator() = default; int PitchEstimator::Estimate( - ArrayView pitch_buffer) { - ArrayView pitch_buffer_12kHz_view( + std::span pitch_buffer) { + std::span pitch_buffer_12kHz_view( pitch_buffer_12kHz_.data(), kBufSize12kHz); RTC_DCHECK_EQ(pitch_buffer_12kHz_.size(), pitch_buffer_12kHz_view.size()); - ArrayView auto_correlation_12kHz_view( + std::span auto_correlation_12kHz_view( auto_correlation_12kHz_.data(), kNumLags12kHz); RTC_DCHECK_EQ(auto_correlation_12kHz_.size(), auto_correlation_12kHz_view.size()); @@ -54,7 +54,7 @@ int PitchEstimator::Estimate( // Refine the initial pitch period estimation from 12 kHz to 48 kHz. // Pre-compute frame energies at 24 kHz. - ArrayView y_energy_24kHz_view( + std::span y_energy_24kHz_view( y_energy_24kHz_.data(), kRefineNumLags24kHz); RTC_DCHECK_EQ(y_energy_24kHz_.size(), y_energy_24kHz_view.size()); ComputeSlidingFrameSquareEnergies24kHz(pitch_buffer, y_energy_24kHz_view, diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search.h b/modules/audio_processing/agc2/rnn_vad/pitch_search.h index 28044248938..9f228bcf8f3 100644 --- a/modules/audio_processing/agc2/rnn_vad/pitch_search.h +++ b/modules/audio_processing/agc2/rnn_vad/pitch_search.h @@ -11,9 +11,9 @@ #ifndef MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ #define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_ +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/auto_correlation.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" @@ -31,7 +31,7 @@ class PitchEstimator { PitchEstimator& operator=(const PitchEstimator&) = delete; ~PitchEstimator(); // Returns the estimated pitch period at 48 kHz. - int Estimate(ArrayView pitch_buffer); + int Estimate(std::span pitch_buffer); private: FRIEND_TEST_ALL_PREFIXES(RnnVadTest, PitchSearchWithinTolerance); diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc index b7eb7f4a4ed..25f33fa71dd 100644 --- a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc +++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/vector_math.h" @@ -27,14 +27,14 @@ namespace rnn_vad { namespace { float ComputeAutoCorrelation(int inverted_lag, - ArrayView pitch_buffer, + std::span pitch_buffer, const VectorMath& vector_math) { RTC_DCHECK_LT(inverted_lag, kBufSize24kHz); RTC_DCHECK_LT(inverted_lag, kRefineNumLags24kHz); static_assert(kMaxPitch24kHz < kBufSize24kHz, ""); return vector_math.DotProduct( - pitch_buffer.subview(/*offset=*/kMaxPitch24kHz), - pitch_buffer.subview(inverted_lag, kFrameSize20ms24kHz)); + pitch_buffer.subspan(/*offset=*/kMaxPitch24kHz), + pitch_buffer.subspan(inverted_lag, kFrameSize20ms24kHz)); } // Given an auto-correlation coefficient `curr_auto_correlation` and its @@ -65,7 +65,7 @@ int GetPitchPseudoInterpolationOffset(float prev_auto_correlation, // output sample rate is twice as that of `lag`. int PitchPseudoInterpolationLagPitchBuf( int lag, - ArrayView pitch_buffer, + std::span pitch_buffer, const VectorMath& vector_math) { int offset = 0; // Cannot apply pseudo-interpolation at the boundaries. @@ -130,7 +130,11 @@ constexpr int kMaxPitchPeriods24kHz = // Collection of inverted lags. class InvertedLagsIndex { + using Storage = std::array; + public: + using const_iterator = Storage::const_iterator; + InvertedLagsIndex() : num_entries_(0) {} // Adds an inverted lag to the index. Cannot add more than // `kMaxPitchPeriods24kHz` values. @@ -141,6 +145,9 @@ class InvertedLagsIndex { const int* data() const { return inverted_lags_.data(); } int size() const { return num_entries_; } + const_iterator begin() const { return inverted_lags_.begin(); } + const_iterator end() const { return begin() + size(); } + private: std::array inverted_lags_; int num_entries_; @@ -151,8 +158,8 @@ class InvertedLagsIndex { // the inverted lags for the computed auto correlation values. void ComputeAutoCorrelation( Range inverted_lags, - ArrayView pitch_buffer, - ArrayView auto_correlation, + std::span pitch_buffer, + std::span auto_correlation, InvertedLagsIndex& inverted_lags_index, const VectorMath& vector_math) { // Check valid range. @@ -179,10 +186,10 @@ void ComputeAutoCorrelation( // Searches the strongest pitch period at 24 kHz and returns its inverted lag at // 48 kHz. int ComputePitchPeriod48kHz( - ArrayView /* pitch_buffer */, - ArrayView inverted_lags, - ArrayView auto_correlation, - ArrayView y_energy, + std::span /* pitch_buffer */, + std::span inverted_lags, + std::span auto_correlation, + std::span y_energy, const VectorMath& /* vector_math */) { static_assert(kMaxPitch24kHz > kInitialNumLags24kHz, ""); static_assert(kMaxPitch24kHz < kBufSize24kHz, ""); @@ -280,8 +287,8 @@ bool IsAlternativePitchStrongerThanInitial(PitchInfo last, } // namespace -void Decimate2x(ArrayView src, - ArrayView dst) { +void Decimate2x(std::span src, + std::span dst) { // TODO(bugs.webrtc.org/9076): Consider adding anti-aliasing filter. static_assert(2 * kBufSize12kHz == kBufSize24kHz, ""); for (int i = 0; i < kBufSize12kHz; ++i) { @@ -290,12 +297,12 @@ void Decimate2x(ArrayView src, } void ComputeSlidingFrameSquareEnergies24kHz( - ArrayView pitch_buffer, - ArrayView y_energy, + std::span pitch_buffer, + std::span y_energy, AvailableCpuFeatures cpu_features) { VectorMath vector_math(cpu_features); static_assert(kFrameSize20ms24kHz < kBufSize24kHz, ""); - const auto frame_20ms_view = pitch_buffer.subview(0, kFrameSize20ms24kHz); + const auto frame_20ms_view = pitch_buffer.subspan(0, kFrameSize20ms24kHz); float yy = vector_math.DotProduct(frame_20ms_view, frame_20ms_view); y_energy[0] = yy; static_assert(kMaxPitch24kHz - 1 + kFrameSize20ms24kHz < kBufSize24kHz, ""); @@ -310,8 +317,8 @@ void ComputeSlidingFrameSquareEnergies24kHz( } CandidatePitchPeriods ComputePitchPeriod12kHz( - ArrayView pitch_buffer, - ArrayView auto_correlation, + std::span pitch_buffer, + std::span auto_correlation, AvailableCpuFeatures cpu_features) { static_assert(kMaxPitch12kHz > kNumLags12kHz, ""); static_assert(kMaxPitch12kHz < kBufSize12kHz, ""); @@ -333,7 +340,7 @@ CandidatePitchPeriods ComputePitchPeriod12kHz( VectorMath vector_math(cpu_features); static_assert(kFrameSize20ms12kHz + 1 < kBufSize12kHz, ""); - const auto frame_view = pitch_buffer.subview(0, kFrameSize20ms12kHz + 1); + const auto frame_view = pitch_buffer.subspan(0, kFrameSize20ms12kHz + 1); float denominator = 1.f + vector_math.DotProduct(frame_view, frame_view); // Search best and second best pitches by looking at the scaled // auto-correlation. @@ -369,8 +376,8 @@ CandidatePitchPeriods ComputePitchPeriod12kHz( } int ComputePitchPeriod48kHz( - ArrayView pitch_buffer, - ArrayView y_energy, + std::span pitch_buffer, + std::span y_energy, CandidatePitchPeriods pitch_candidates, AvailableCpuFeatures cpu_features) { // Compute the auto-correlation terms only for neighbors of the two pitch @@ -407,8 +414,8 @@ int ComputePitchPeriod48kHz( } PitchInfo ComputeExtendedPitchPeriod48kHz( - ArrayView pitch_buffer, - ArrayView y_energy, + std::span pitch_buffer, + std::span y_energy, int initial_pitch_period_48kHz, PitchInfo last_pitch_48kHz, AvailableCpuFeatures cpu_features) { diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h index 5609d5a058f..9ff1d27085f 100644 --- a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h +++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" @@ -21,8 +21,8 @@ namespace webrtc { namespace rnn_vad { // Performs 2x decimation without any anti-aliasing filter. -void Decimate2x(ArrayView src, - ArrayView dst); +void Decimate2x(std::span src, + std::span dst); // Key concepts and keywords used below in this file. // @@ -62,8 +62,8 @@ void Decimate2x(ArrayView src, // Computes the sum of squared samples for every sliding frame `y` in the pitch // buffer. The indexes of `y_energy` are inverted lags. void ComputeSlidingFrameSquareEnergies24kHz( - ArrayView pitch_buffer, - ArrayView y_energy, + std::span pitch_buffer, + std::span y_energy, AvailableCpuFeatures cpu_features); // Top-2 pitch period candidates. Unit: number of samples - i.e., inverted lags. @@ -76,16 +76,16 @@ struct CandidatePitchPeriods { // pitch buffer and the auto-correlation values (having inverted lags as // indexes). CandidatePitchPeriods ComputePitchPeriod12kHz( - ArrayView pitch_buffer, - ArrayView auto_correlation, + std::span pitch_buffer, + std::span auto_correlation, AvailableCpuFeatures cpu_features); // Computes the pitch period at 48 kHz given a view on the 24 kHz pitch buffer, // the energies for the sliding frames `y` at 24 kHz and the pitch period // candidates at 24 kHz (encoded as inverted lag). int ComputePitchPeriod48kHz( - ArrayView pitch_buffer, - ArrayView y_energy, + std::span pitch_buffer, + std::span y_energy, CandidatePitchPeriods pitch_candidates_24kHz, AvailableCpuFeatures cpu_features); @@ -99,8 +99,8 @@ struct PitchInfo { // `y` at 24 kHz, the initial 48 kHz estimation (computed by // `ComputePitchPeriod48kHz()`) and the last estimated pitch. PitchInfo ComputeExtendedPitchPeriod48kHz( - ArrayView pitch_buffer, - ArrayView y_energy, + std::span pitch_buffer, + std::span y_energy, int initial_pitch_period_48kHz, PitchInfo last_pitch_48kHz, AvailableCpuFeatures cpu_features); diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal_unittest.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal_unittest.cc index 8235ccac3fc..3f39e4fadce 100644 --- a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal_unittest.cc @@ -11,10 +11,10 @@ #include "modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h" #include +#include #include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/test_utils.h" @@ -93,7 +93,7 @@ TEST(RnnVadTest, ComputePitchPeriod48kHzBitExactness) { PitchTestData test_data; std::vector y_energy(kRefineNumLags24kHz); - ArrayView y_energy_view(y_energy.data(), + std::span y_energy_view(y_energy.data(), kRefineNumLags24kHz); ComputeSlidingFrameSquareEnergies24kHz(test_data.PitchBuffer24kHzView(), y_energy_view, cpu_features); @@ -128,7 +128,7 @@ TEST_P(PitchCandidatesParametrization, PitchTestData test_data; std::vector y_energy(kRefineNumLags24kHz); - ArrayView y_energy_view(y_energy.data(), + std::span y_energy_view(y_energy.data(), kRefineNumLags24kHz); ComputeSlidingFrameSquareEnergies24kHz(test_data.PitchBuffer24kHzView(), y_energy_view, params.cpu_features); @@ -179,7 +179,7 @@ TEST_P(ExtendedPitchPeriodSearchParametrizaion, PitchTestData test_data; std::vector y_energy(kRefineNumLags24kHz); - ArrayView y_energy_view(y_energy.data(), + std::span y_energy_view(y_energy.data(), kRefineNumLags24kHz); ComputeSlidingFrameSquareEnergies24kHz(test_data.PitchBuffer24kHzView(), y_energy_view, params.cpu_features); diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_unittest.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search_unittest.cc index 4cddd566f70..633a9e54668 100644 --- a/modules/audio_processing/agc2/rnn_vad/pitch_search_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_unittest.cc @@ -11,7 +11,7 @@ #include "modules/audio_processing/agc2/rnn_vad/pitch_search.h" #include -#include +#include #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" @@ -28,7 +28,7 @@ namespace rnn_vad { TEST(RnnVadTest, PitchSearchWithinTolerance) { ChunksFileReader reader = CreateLpResidualAndPitchInfoReader(); const int num_frames = std::min(reader.num_chunks, 300); // Max 3 s. - std::vector lp_residual(kBufSize24kHz); + std::array lp_residual = {}; float expected_pitch_period, expected_pitch_strength; const AvailableCpuFeatures cpu_features = GetAvailableCpuFeatures(); PitchEstimator pitch_estimator(cpu_features); @@ -40,8 +40,7 @@ TEST(RnnVadTest, PitchSearchWithinTolerance) { ASSERT_TRUE(reader.reader->ReadChunk(lp_residual)); ASSERT_TRUE(reader.reader->ReadValue(expected_pitch_period)); ASSERT_TRUE(reader.reader->ReadValue(expected_pitch_strength)); - int pitch_period = - pitch_estimator.Estimate({lp_residual.data(), kBufSize24kHz}); + int pitch_period = pitch_estimator.Estimate(lp_residual); EXPECT_EQ(expected_pitch_period, pitch_period); EXPECT_NEAR(expected_pitch_strength, pitch_estimator.GetLastPitchStrengthForTesting(), 15e-6f); diff --git a/modules/audio_processing/agc2/rnn_vad/ring_buffer.h b/modules/audio_processing/agc2/rnn_vad/ring_buffer.h index 0f12b39805a..6c8e4db303a 100644 --- a/modules/audio_processing/agc2/rnn_vad/ring_buffer.h +++ b/modules/audio_processing/agc2/rnn_vad/ring_buffer.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -37,7 +37,7 @@ class RingBuffer { // Set the ring buffer values to zero. void Reset() { buffer_.fill(0); } // Replace the least recently pushed array in the buffer with `new_values`. - void Push(ArrayView new_values) { + void Push(std::span new_values) { std::memcpy(buffer_.data() + S * tail_, new_values.data(), S * sizeof(T)); tail_ += 1; if (tail_ == N) @@ -46,13 +46,13 @@ class RingBuffer { // Return an array view onto the array with a given delay. A view on the last // and least recently push array is returned when `delay` is 0 and N - 1 // respectively. - ArrayView GetArrayView(int delay) const { + std::span GetArrayView(int delay) const { RTC_DCHECK_LE(0, delay); RTC_DCHECK_LT(delay, N); int offset = tail_ - 1 - delay; if (offset < 0) offset += N; - return {buffer_.data() + S * offset, S}; + return std::span(buffer_.data() + S * offset, S); } private: diff --git a/modules/audio_processing/agc2/rnn_vad/ring_buffer_unittest.cc b/modules/audio_processing/agc2/rnn_vad/ring_buffer_unittest.cc index 7300121bdb5..14f51aba582 100644 --- a/modules/audio_processing/agc2/rnn_vad/ring_buffer_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/ring_buffer_unittest.cc @@ -13,22 +13,16 @@ #include #include #include +#include -#include "api/array_view.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace rnn_vad { namespace { -// Compare the elements of two given array views. -template -void ExpectEq(ArrayView a, ArrayView b) { - for (int i = 0; i < S; ++i) { - SCOPED_TRACE(i); - EXPECT_EQ(a[i], b[i]); - } -} +using ::testing::ElementsAreArray; // Test push/read sequences. template @@ -37,14 +31,14 @@ void TestRingBuffer() { SCOPED_TRACE(S); std::array prev_pushed_array; std::array pushed_array; - ArrayView pushed_array_view(pushed_array.data(), S); + std::span pushed_array_view(pushed_array.data(), S); // Init. RingBuffer ring_buf; ring_buf.GetArrayView(0); pushed_array.fill(0); ring_buf.Push(pushed_array_view); - ExpectEq(pushed_array_view, ring_buf.GetArrayView(0)); + EXPECT_THAT(ring_buf.GetArrayView(0), ElementsAreArray(pushed_array_view)); // Push N times and check most recent and second most recent. for (T v = 1; v <= static_cast(N); ++v) { @@ -52,10 +46,11 @@ void TestRingBuffer() { prev_pushed_array = pushed_array; pushed_array.fill(v); ring_buf.Push(pushed_array_view); - ExpectEq(pushed_array_view, ring_buf.GetArrayView(0)); + EXPECT_THAT(ring_buf.GetArrayView(0), ElementsAreArray(pushed_array_view)); if (N > 1) { pushed_array.fill(v - 1); - ExpectEq(pushed_array_view, ring_buf.GetArrayView(1)); + EXPECT_THAT(ring_buf.GetArrayView(1), + ElementsAreArray(pushed_array_view)); } } @@ -64,7 +59,8 @@ void TestRingBuffer() { SCOPED_TRACE(delay); T expected_value = N - static_cast(delay); pushed_array.fill(expected_value); - ExpectEq(pushed_array_view, ring_buf.GetArrayView(delay)); + EXPECT_THAT(ring_buf.GetArrayView(delay), + ElementsAreArray(pushed_array_view)); } } @@ -84,7 +80,7 @@ TEST(RnnVadTest, RingBufferArrayViews) { for (int j = i + 1; j < n; ++j) { SCOPED_TRACE(j); auto view_j = ring_buf.GetArrayView(j); - EXPECT_NE(view_i, view_j); + EXPECT_NE(view_i.data(), view_j.data()); } } ring_buf.Push(pushed_array); diff --git a/modules/audio_processing/agc2/rnn_vad/rnn.cc b/modules/audio_processing/agc2/rnn_vad/rnn.cc index 3b88554880e..62195cf85d1 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn.cc @@ -10,7 +10,8 @@ #include "modules/audio_processing/agc2/rnn_vad/rnn.h" -#include "api/array_view.h" +#include + #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/rnn_fc.h" @@ -79,15 +80,15 @@ void RnnVad::Reset() { } float RnnVad::ComputeVadProbability( - ArrayView feature_vector, + std::span feature_vector, bool is_silence) { if (is_silence) { Reset(); return 0.f; } input_.ComputeOutput(feature_vector); - hidden_.ComputeOutput(input_); - output_.ComputeOutput(hidden_); + hidden_.ComputeOutput(input_.output()); + output_.ComputeOutput(hidden_.output()); RTC_DCHECK_EQ(output_.size(), 1); return output_.data()[0]; } diff --git a/modules/audio_processing/agc2/rnn_vad/rnn.h b/modules/audio_processing/agc2/rnn_vad/rnn.h index 7b47d23e2d5..caa2d360ed6 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn.h +++ b/modules/audio_processing/agc2/rnn_vad/rnn.h @@ -13,8 +13,8 @@ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/rnn_fc.h" @@ -35,7 +35,7 @@ class RnnVad { // Observes `feature_vector` and `is_silence`, updates the RNN and returns the // current voice probability. float ComputeVadProbability( - ArrayView feature_vector, + std::span feature_vector, bool is_silence); private: diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc b/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc index 3850a256fdb..011c77372d2 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn_fc.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/function_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "rtc_base/checks.h" @@ -27,7 +27,7 @@ namespace webrtc { namespace rnn_vad { namespace { -std::vector GetScaledParams(ArrayView params) { +std::vector GetScaledParams(std::span params) { std::vector scaled_params(params.size()); std::transform(params.begin(), params.end(), scaled_params.begin(), [](int8_t x) -> float { @@ -39,7 +39,7 @@ std::vector GetScaledParams(ArrayView params) { // TODO(bugs.chromium.org/10480): Hard-code optimized layout and remove this // function to improve setup time. // Casts and scales `weights` and re-arranges the layout. -std::vector PreprocessWeights(ArrayView weights, +std::vector PreprocessWeights(std::span weights, int output_size) { if (output_size == 1) { return GetScaledParams(weights); @@ -72,8 +72,8 @@ FunctionView GetActivationFunction( FullyConnectedLayer::FullyConnectedLayer( const int input_size, const int output_size, - const ArrayView bias, - const ArrayView weights, + const std::span bias, + const std::span weights, ActivationFunction activation_function, const AvailableCpuFeatures& cpu_features, absl::string_view layer_name) @@ -95,13 +95,13 @@ FullyConnectedLayer::FullyConnectedLayer( FullyConnectedLayer::~FullyConnectedLayer() = default; -void FullyConnectedLayer::ComputeOutput(ArrayView input) { +void FullyConnectedLayer::ComputeOutput(std::span input) { RTC_DCHECK_EQ(input.size(), input_size_); - ArrayView weights(weights_); + std::span weights(weights_); for (int o = 0; o < output_size_; ++o) { output_[o] = activation_function_( bias_[o] + vector_math_.DotProduct( - input, weights.subview(o * input_size_, input_size_))); + input, weights.subspan(o * input_size_, input_size_))); } } diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_fc.h b/modules/audio_processing/agc2/rnn_vad/rnn_fc.h index 8f3e9b43bb4..bde2d67d8ba 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_fc.h +++ b/modules/audio_processing/agc2/rnn_vad/rnn_fc.h @@ -13,10 +13,10 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/function_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/vector_math.h" @@ -37,8 +37,8 @@ class FullyConnectedLayer { // Ctor. `output_size` cannot be greater than `kFullyConnectedLayerMaxUnits`. FullyConnectedLayer(int input_size, int output_size, - ArrayView bias, - ArrayView weights, + std::span bias, + std::span weights, ActivationFunction activation_function, const AvailableCpuFeatures& cpu_features, absl::string_view layer_name); @@ -53,8 +53,13 @@ class FullyConnectedLayer { // Returns the size of the output buffer. int size() const { return output_size_; } + // Returns the output buffer. + std::span output() const { + return std::span(output_.data(), output_size_); + } + // Computes the fully-connected layer output. - void ComputeOutput(ArrayView input); + void ComputeOutput(std::span input); private: const int input_size_; diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_fc_unittest.cc b/modules/audio_processing/agc2/rnn_vad/rnn_fc_unittest.cc index 8f86b278815..db85581c202 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_fc_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn_fc_unittest.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/test_utils.h" #include "modules/audio_processing/test/performance_timer.h" @@ -57,7 +56,7 @@ TEST_P(RnnFcParametrization, CheckFullyConnectedLayerOutput) { /*cpu_features=*/GetParam(), /*layer_name=*/"FC"); fc.ComputeOutput(kFullyConnectedInputVector); - ExpectNearAbsolute(kFullyConnectedExpectedOutput, fc, 1e-5f); + ExpectNearAbsolute(kFullyConnectedExpectedOutput, fc.output(), 1e-5f); } TEST_P(RnnFcParametrization, DISABLED_BenchmarkFullyConnectedLayer) { diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc b/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc index d679b614a8b..a31b48acf18 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn_gru.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/vector_math.h" #include "rtc_base/checks.h" @@ -31,7 +31,7 @@ namespace { constexpr int kNumGruGates = 3; // Update, reset, output. -std::vector PreprocessGruTensor(ArrayView tensor_src, +std::vector PreprocessGruTensor(std::span tensor_src, int output_size) { // Transpose, cast and scale. // `n` is the size of the first dimension of the 3-dim tensor `weights`. @@ -64,12 +64,12 @@ std::vector PreprocessGruTensor(ArrayView tensor_src, void ComputeUpdateResetGate(int input_size, int output_size, const VectorMath& vector_math, - ArrayView input, - ArrayView state, - ArrayView bias, - ArrayView weights, - ArrayView recurrent_weights, - ArrayView gate) { + std::span input, + std::span state, + std::span bias, + std::span weights, + std::span recurrent_weights, + std::span gate) { RTC_DCHECK_EQ(input.size(), input_size); RTC_DCHECK_EQ(state.size(), output_size); RTC_DCHECK_EQ(bias.size(), output_size); @@ -79,9 +79,9 @@ void ComputeUpdateResetGate(int input_size, for (int o = 0; o < output_size; ++o) { float x = bias[o]; x += vector_math.DotProduct(input, - weights.subview(o * input_size, input_size)); + weights.subspan(o * input_size, input_size)); x += vector_math.DotProduct( - state, recurrent_weights.subview(o * output_size, output_size)); + state, recurrent_weights.subspan(o * output_size, output_size)); gate[o] = ::rnnoise::SigmoidApproximated(x); } } @@ -100,13 +100,13 @@ void ComputeUpdateResetGate(int input_size, void ComputeStateGate(int input_size, int output_size, const VectorMath& vector_math, - ArrayView input, - ArrayView update, - ArrayView reset, - ArrayView bias, - ArrayView weights, - ArrayView recurrent_weights, - ArrayView state) { + std::span input, + std::span update, + std::span reset, + std::span bias, + std::span weights, + std::span recurrent_weights, + std::span state) { RTC_DCHECK_EQ(input.size(), input_size); RTC_DCHECK_GE(update.size(), output_size); // `update` is over-allocated. RTC_DCHECK_GE(reset.size(), output_size); // `reset` is over-allocated. @@ -121,10 +121,10 @@ void ComputeStateGate(int input_size, for (int o = 0; o < output_size; ++o) { float x = bias[o]; x += vector_math.DotProduct(input, - weights.subview(o * input_size, input_size)); + weights.subspan(o * input_size, input_size)); x += vector_math.DotProduct( {reset_x_state.data(), static_cast(output_size)}, - recurrent_weights.subview(o * output_size, output_size)); + recurrent_weights.subspan(o * output_size, output_size)); state[o] = update[o] * state[o] + (1.f - update[o]) * std::max(0.f, x); } } @@ -134,9 +134,9 @@ void ComputeStateGate(int input_size, GatedRecurrentLayer::GatedRecurrentLayer( const int input_size, const int output_size, - const ArrayView bias, - const ArrayView weights, - const ArrayView recurrent_weights, + const std::span bias, + const std::span weights, + const std::span recurrent_weights, const AvailableCpuFeatures& cpu_features, absl::string_view layer_name) : input_size_(input_size), @@ -167,39 +167,39 @@ void GatedRecurrentLayer::Reset() { state_.fill(0.f); } -void GatedRecurrentLayer::ComputeOutput(ArrayView input) { +void GatedRecurrentLayer::ComputeOutput(std::span input) { RTC_DCHECK_EQ(input.size(), input_size_); // The tensors below are organized as a sequence of flattened tensors for the // `update`, `reset` and `state` gates. - ArrayView bias(bias_); - ArrayView weights(weights_); - ArrayView recurrent_weights(recurrent_weights_); + std::span bias(bias_); + std::span weights(weights_); + std::span recurrent_weights(recurrent_weights_); // Strides to access to the flattened tensors for a specific gate. const int stride_weights = input_size_ * output_size_; const int stride_recurrent_weights = output_size_ * output_size_; - ArrayView state(state_.data(), output_size_); + std::span state(state_.data(), output_size_); // Update gate. std::array update; ComputeUpdateResetGate( input_size_, output_size_, vector_math_, input, state, - bias.subview(0, output_size_), weights.subview(0, stride_weights), - recurrent_weights.subview(0, stride_recurrent_weights), update); + bias.subspan(0, output_size_), weights.subspan(0, stride_weights), + recurrent_weights.subspan(0, stride_recurrent_weights), update); // Reset gate. std::array reset; ComputeUpdateResetGate(input_size_, output_size_, vector_math_, input, state, - bias.subview(output_size_, output_size_), - weights.subview(stride_weights, stride_weights), - recurrent_weights.subview(stride_recurrent_weights, + bias.subspan(output_size_, output_size_), + weights.subspan(stride_weights, stride_weights), + recurrent_weights.subspan(stride_recurrent_weights, stride_recurrent_weights), reset); // State gate. ComputeStateGate(input_size_, output_size_, vector_math_, input, update, - reset, bias.subview(2 * output_size_, output_size_), - weights.subview(2 * stride_weights, stride_weights), - recurrent_weights.subview(2 * stride_recurrent_weights, + reset, bias.subspan(2 * output_size_, output_size_), + weights.subspan(2 * stride_weights, stride_weights), + recurrent_weights.subspan(2 * stride_recurrent_weights, stride_recurrent_weights), state); } diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_gru.h b/modules/audio_processing/agc2/rnn_vad/rnn_gru.h index 5712ee4fa97..502802dbd48 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_gru.h +++ b/modules/audio_processing/agc2/rnn_vad/rnn_gru.h @@ -13,10 +13,10 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/vector_math.h" @@ -33,9 +33,9 @@ class GatedRecurrentLayer { // Ctor. `output_size` cannot be greater than `kGruLayerMaxUnits`. GatedRecurrentLayer(int input_size, int output_size, - ArrayView bias, - ArrayView weights, - ArrayView recurrent_weights, + std::span bias, + std::span weights, + std::span recurrent_weights, const AvailableCpuFeatures& cpu_features, absl::string_view layer_name); GatedRecurrentLayer(const GatedRecurrentLayer&) = delete; @@ -49,10 +49,15 @@ class GatedRecurrentLayer { // Returns the size of the output buffer. int size() const { return output_size_; } + // Returns the output buffer. + std::span output() const { + return std::span(state_.data(), output_size_); + } + // Resets the GRU state. void Reset(); // Computes the recurrent layer output and updates the status. - void ComputeOutput(ArrayView input); + void ComputeOutput(std::span input); private: const int input_size_; diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_gru_unittest.cc b/modules/audio_processing/agc2/rnn_vad/rnn_gru_unittest.cc index d49a1163547..044bb3b12bf 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_gru_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn_gru_unittest.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/test_utils.h" #include "modules/audio_processing/test/performance_timer.h" @@ -31,8 +31,8 @@ namespace rnn_vad { namespace { void TestGatedRecurrentLayer(GatedRecurrentLayer& gru, - ArrayView input_sequence, - ArrayView expected_output_sequence) { + std::span input_sequence, + std::span expected_output_sequence) { const int input_sequence_length = CheckedDivExact( dchecked_cast(input_sequence.size()), gru.input_size()); const int output_sequence_length = CheckedDivExact( @@ -44,10 +44,10 @@ void TestGatedRecurrentLayer(GatedRecurrentLayer& gru, for (int i = 0; i < input_sequence_length; ++i) { SCOPED_TRACE(i); gru.ComputeOutput( - input_sequence.subview(i * gru.input_size(), gru.input_size())); + input_sequence.subspan(i * gru.input_size(), gru.input_size())); const auto expected_output = - expected_output_sequence.subview(i * gru.size(), gru.size()); - ExpectNearAbsolute(expected_output, gru, 3e-6f); + expected_output_sequence.subspan(i * gru.size(), gru.size()); + ExpectNearAbsolute(expected_output, gru.output(), 3e-6f); } } @@ -138,7 +138,7 @@ TEST_P(RnnGruParametrization, DISABLED_BenchmarkGatedRecurrentLayer) { /*cpu_features=*/GetParam(), /*layer_name=*/"GRU"); - ArrayView input_sequence(gru_input_sequence); + std::span input_sequence(gru_input_sequence); ASSERT_EQ(input_sequence.size() % kInputLayerOutputSize, static_cast(0)); const int input_sequence_length = @@ -150,7 +150,7 @@ TEST_P(RnnGruParametrization, DISABLED_BenchmarkGatedRecurrentLayer) { perf_timer.StartTimer(); for (int i = 0; i < input_sequence_length; ++i) { gru.ComputeOutput( - input_sequence.subview(i * gru.input_size(), gru.input_size())); + input_sequence.subspan(i * gru.input_size(), gru.input_size())); } perf_timer.StopTimer(); } diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_unittest.cc b/modules/audio_processing/agc2/rnn_vad/rnn_unittest.cc index a944e2b9333..b5a8a7969be 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn_unittest.cc @@ -12,7 +12,6 @@ #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "test/gtest.h" diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_vad_unittest.cc b/modules/audio_processing/agc2/rnn_vad/rnn_vad_unittest.cc index 5015957f69a..7ab9ce1b8a6 100644 --- a/modules/audio_processing/agc2/rnn_vad/rnn_vad_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/rnn_vad_unittest.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "common_audio/resampler/push_sinc_resampler.h" @@ -74,9 +75,9 @@ TEST_P(RnnVadProbabilityParametrization, RnnVadProbabilityWithinTolerance) { const int num_frames = samples_reader->size() / kFrameSize10ms48kHz; // Init buffers. - std::vector samples_48k(kFrameSize10ms48kHz); - std::vector samples_24k(kFrameSize10ms24kHz); - std::vector feature_vector(kFeatureVectorSize); + std::array samples_48k = {}; + std::array samples_24k = {}; + std::array feature_vector = {}; std::vector computed_vad_prob(num_frames); std::vector expected_vad_prob(num_frames); @@ -90,10 +91,9 @@ TEST_P(RnnVadProbabilityParametrization, RnnVadProbabilityWithinTolerance) { decimator.Resample(samples_48k.data(), samples_48k.size(), samples_24k.data(), samples_24k.size()); bool is_silence = features_extractor.CheckSilenceComputeFeatures( - {samples_24k.data(), kFrameSize10ms24kHz}, - {feature_vector.data(), kFeatureVectorSize}); - computed_vad_prob[i] = rnn_vad.ComputeVadProbability( - {feature_vector.data(), kFeatureVectorSize}, is_silence); + samples_24k, feature_vector); + computed_vad_prob[i] = + rnn_vad.ComputeVadProbability(feature_vector, is_silence); EXPECT_NEAR(computed_vad_prob[i], expected_vad_prob[i], 1e-3f); cumulative_error += std::abs(computed_vad_prob[i] - expected_vad_prob[i]); } @@ -141,8 +141,9 @@ TEST_P(RnnVadProbabilityParametrization, DISABLED_RnnVadPerformance) { perf_timer.StartTimer(); for (int i = 0; i < num_frames; ++i) { bool is_silence = features_extractor.CheckSilenceComputeFeatures( - {&prefetched_decimated_samples[i * kFrameSize10ms24kHz], - kFrameSize10ms24kHz}, + std::span( + &prefetched_decimated_samples[i * kFrameSize10ms24kHz], + kFrameSize10ms24kHz), feature_vector); rnn_vad.ComputeVadProbability(feature_vector, is_silence); } diff --git a/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h b/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h index c88683a2da2..9526d6e6bba 100644 --- a/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h +++ b/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h @@ -13,10 +13,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -50,16 +50,18 @@ class SequenceBuffer { // Sets the sequence buffer values to zero. void Reset() { std::fill(buffer_.begin(), buffer_.end(), 0); } // Returns a view on the whole buffer. - ArrayView GetBufferView() const { return {buffer_.data(), S}; } + std::span GetBufferView() const { + return std::span(buffer_.data(), S); + } // Returns a view on the M most recent values of the buffer. - ArrayView GetMostRecentValuesView() const { + std::span GetMostRecentValuesView() const { static_assert(M <= S, "The number of most recent values cannot be larger than the " "sequence buffer size."); - return {buffer_.data() + S - M, M}; + return std::span(buffer_.data() + S - M, M); } // Shifts left the buffer by N items and add new N items at the end. - void Push(ArrayView new_values) { + void Push(std::span new_values) { // Make space for the new values. if (S > N) std::memmove(buffer_.data(), buffer_.data() + N, (S - N) * sizeof(T)); diff --git a/modules/audio_processing/agc2/rnn_vad/sequence_buffer_unittest.cc b/modules/audio_processing/agc2/rnn_vad/sequence_buffer_unittest.cc index c43f99dc08b..ff87e3bd9d6 100644 --- a/modules/audio_processing/agc2/rnn_vad/sequence_buffer_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/sequence_buffer_unittest.cc @@ -37,12 +37,12 @@ void TestSequenceBufferPushOp() { SCOPED_TRACE(i); seq_buf.Push(chunk); // Still in the buffer. - const auto* m = std::max_element(seq_buf_view.begin(), seq_buf_view.end()); + auto m = std::max_element(seq_buf_view.begin(), seq_buf_view.end()); EXPECT_EQ(1, *m); } // Gone after another push. seq_buf.Push(chunk); - const auto* m = std::max_element(seq_buf_view.begin(), seq_buf_view.end()); + auto m = std::max_element(seq_buf_view.begin(), seq_buf_view.end()); EXPECT_EQ(0, *m); // Check that the last item moves left by N positions after a push op. diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features.cc index 1712c7d933a..ea4dcaf9bb7 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features.cc @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/ring_buffer.h" #include "modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h" @@ -34,7 +34,7 @@ constexpr float kSilenceThreshold = 0.04f; // Computes the new cepstral difference stats and pushes them into the passed // symmetric matrix buffer. void UpdateCepstralDifferenceStats( - ArrayView new_cepstral_coeffs, + std::span new_cepstral_coeffs, const RingBuffer& ring_buf, SymmetricMatrixBuffer* sym_matrix_buf) { RTC_DCHECK(sym_matrix_buf); @@ -71,7 +71,7 @@ std::array ComputeScaledHalfVorbisWindow( // applied. The Fourier coefficient corresponding to the Nyquist frequency is // set to zero (it is never used and this allows to simplify the code). void ComputeWindowedForwardFft( - ArrayView frame, + std::span frame, const std::array& half_window, Pffft::FloatBuffer* fft_input_buffer, Pffft::FloatBuffer* fft_output_buffer, @@ -109,13 +109,13 @@ void SpectralFeaturesExtractor::Reset() { } bool SpectralFeaturesExtractor::CheckSilenceComputeFeatures( - ArrayView reference_frame, - ArrayView lagged_frame, - ArrayView higher_bands_cepstrum, - ArrayView average, - ArrayView first_derivative, - ArrayView second_derivative, - ArrayView bands_cross_corr, + std::span reference_frame, + std::span lagged_frame, + std::span higher_bands_cepstrum, + std::span average, + std::span first_derivative, + std::span second_derivative, + std::span bands_cross_corr, float* variability) { // Compute the Opus band energies for the reference frame. ComputeWindowedForwardFft(reference_frame, half_window_, fft_buffer_.get(), @@ -161,9 +161,9 @@ bool SpectralFeaturesExtractor::CheckSilenceComputeFeatures( } void SpectralFeaturesExtractor::ComputeAvgAndDerivatives( - ArrayView average, - ArrayView first_derivative, - ArrayView second_derivative) const { + std::span average, + std::span first_derivative, + std::span second_derivative) const { auto curr = cepstral_coeffs_ring_buf_.GetArrayView(0); auto prev1 = cepstral_coeffs_ring_buf_.GetArrayView(1); auto prev2 = cepstral_coeffs_ring_buf_.GetArrayView(2); @@ -181,7 +181,7 @@ void SpectralFeaturesExtractor::ComputeAvgAndDerivatives( } void SpectralFeaturesExtractor::ComputeNormalizedCepstralCorrelation( - ArrayView bands_cross_corr) { + std::span bands_cross_corr) { spectral_correlator_.ComputeCrossCorrelation( reference_frame_fft_->GetConstView(), lagged_frame_fft_->GetConstView(), bands_cross_corr_); diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features.h b/modules/audio_processing/agc2/rnn_vad/spectral_features.h index 6994e451d2c..fe15f4b70ab 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features.h +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/ring_buffer.h" #include "modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h" @@ -39,22 +39,22 @@ class SpectralFeaturesExtractor { // detects silence and computes features. If silence is detected, the output // is neither computed nor written. bool CheckSilenceComputeFeatures( - ArrayView reference_frame, - ArrayView lagged_frame, - ArrayView higher_bands_cepstrum, - ArrayView average, - ArrayView first_derivative, - ArrayView second_derivative, - ArrayView bands_cross_corr, + std::span reference_frame, + std::span lagged_frame, + std::span higher_bands_cepstrum, + std::span average, + std::span first_derivative, + std::span second_derivative, + std::span bands_cross_corr, float* variability); private: void ComputeAvgAndDerivatives( - ArrayView average, - ArrayView first_derivative, - ArrayView second_derivative) const; + std::span average, + std::span first_derivative, + std::span second_derivative) const; void ComputeNormalizedCepstralCorrelation( - ArrayView bands_cross_corr); + std::span bands_cross_corr); float ComputeVariability() const; const std::array half_window_; diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc index 7cbdcecb911..d370ac7586e 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_compare.h" @@ -95,15 +95,15 @@ SpectralCorrelator::SpectralCorrelator() SpectralCorrelator::~SpectralCorrelator() = default; void SpectralCorrelator::ComputeAutoCorrelation( - ArrayView x, - ArrayView auto_corr) const { + std::span x, + std::span auto_corr) const { ComputeCrossCorrelation(x, x, auto_corr); } void SpectralCorrelator::ComputeCrossCorrelation( - ArrayView x, - ArrayView y, - ArrayView cross_corr) const { + std::span x, + std::span y, + std::span cross_corr) const { RTC_DCHECK_EQ(x.size(), kFrameSize20ms24kHz); RTC_DCHECK_EQ(x.size(), y.size()); RTC_DCHECK_EQ(x[1], 0.f) << "The Nyquist coefficient must be zeroed."; @@ -126,8 +126,8 @@ void SpectralCorrelator::ComputeCrossCorrelation( } void ComputeSmoothedLogMagnitudeSpectrum( - ArrayView bands_energy, - ArrayView log_bands_energy) { + std::span bands_energy, + std::span log_bands_energy) { RTC_DCHECK_LE(bands_energy.size(), kNumBands); constexpr float kOneByHundred = 1e-2f; constexpr float kLogOneByHundred = -2.f; @@ -161,9 +161,9 @@ std::array ComputeDctTable() { return dct_table; } -void ComputeDct(ArrayView in, - ArrayView dct_table, - ArrayView out) { +void ComputeDct(std::span in, + std::span dct_table, + std::span out) { // DCT scaling factor - i.e., sqrt(2 / kNumBands). constexpr float kDctScalingFactor = 0.301511345f; constexpr float kDctScalingFactorError = diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h index 4d9fd52b972..4434291dffd 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" namespace webrtc { @@ -55,8 +55,8 @@ class SpectralCorrelator { // - be encoded as vectors of interleaved real-complex FFT coefficients // where x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). void ComputeAutoCorrelation( - ArrayView x, - ArrayView auto_corr) const; + std::span x, + std::span auto_corr) const; // Computes the band-wise spectral cross-correlations. // `x` and `y` must: @@ -64,9 +64,9 @@ class SpectralCorrelator { // - be encoded as vectors of interleaved real-complex FFT coefficients where // x[1] = y[1] = 0 (the Nyquist frequency coefficient is omitted). void ComputeCrossCorrelation( - ArrayView x, - ArrayView y, - ArrayView cross_corr) const; + std::span x, + std::span y, + std::span cross_corr) const; private: const std::vector weights_; // Weights for each Fourier coefficient. @@ -77,8 +77,8 @@ class SpectralCorrelator { // computes the log magnitude spectrum applying smoothing both over time and // over frequency. Declared here for unit testing. void ComputeSmoothedLogMagnitudeSpectrum( - ArrayView bands_energy, - ArrayView log_bands_energy); + std::span bands_energy, + std::span log_bands_energy); // TODO(bugs.webrtc.org/10480): Move to anonymous namespace in // spectral_features.cc. Creates a DCT table for arrays having size equal to @@ -90,9 +90,9 @@ std::array ComputeDctTable(); // In-place computation is not allowed and `out` can be smaller than `in` in // order to only compute the first DCT coefficients. Declared here for unit // testing. -void ComputeDct(ArrayView in, - ArrayView dct_table, - ArrayView out); +void ComputeDct(std::span in, + std::span dct_table, + std::span out); } // namespace rnn_vad } // namespace webrtc diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc index 668a4eef2da..63bf3cf4916 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_internal_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/test_utils.h" #include "modules/audio_processing/utility/pffft_wrapper.h" @@ -73,7 +73,7 @@ TEST(RnnVadTest, DISABLED_TestOpusScaleWeights) { int i = 0; for (int band_size : GetOpusScaleNumBins24kHz20ms()) { SCOPED_TRACE(band_size); - ArrayView band_weights(weights.data() + i, band_size); + std::span band_weights(weights.data() + i, band_size); float prev = -1.f; for (float weight : band_weights) { EXPECT_LT(prev, weight); diff --git a/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc b/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc index 3f416bf6425..6a01ab02d1b 100644 --- a/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/spectral_features_unittest.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "modules/audio_processing/agc2/rnn_vad/test_utils.h" #include "rtc_base/numerics/safe_compare.h" @@ -28,41 +28,41 @@ namespace { constexpr int kTestFeatureVectorSize = kNumBands + 3 * kNumLowerBands + 1; // Writes non-zero sample values. -void WriteTestData(ArrayView samples) { +void WriteTestData(std::span samples) { for (int i = 0; SafeLt(i, samples.size()); ++i) { samples[i] = i % 100; } } -ArrayView GetHigherBandsSpectrum( - std::array* feature_vector) { - return {feature_vector->data() + kNumLowerBands, kNumBands - kNumLowerBands}; +std::span GetHigherBandsSpectrum( + std::span feature_vector) { + return feature_vector.subspan(); } -ArrayView GetAverage( - std::array* feature_vector) { - return {feature_vector->data(), kNumLowerBands}; +std::span GetAverage( + std::span feature_vector) { + return feature_vector.first(); } -ArrayView GetFirstDerivative( - std::array* feature_vector) { - return {feature_vector->data() + kNumBands, kNumLowerBands}; +std::span GetFirstDerivative( + std::span feature_vector) { + return feature_vector.subspan(); } -ArrayView GetSecondDerivative( - std::array* feature_vector) { - return {feature_vector->data() + kNumBands + kNumLowerBands, kNumLowerBands}; +std::span GetSecondDerivative( + std::span feature_vector) { + return feature_vector.subspan(); } -ArrayView GetCepstralCrossCorrelation( - std::array* feature_vector) { - return {feature_vector->data() + kNumBands + 2 * kNumLowerBands, - kNumLowerBands}; +std::span GetCepstralCrossCorrelation( + std::span feature_vector) { + return feature_vector + .subspan(); } float* GetCepstralVariability( - std::array* feature_vector) { - return feature_vector->data() + kNumBands + 3 * kNumLowerBands; + std::span feature_vector) { + return &feature_vector[kNumBands + 3 * kNumLowerBands]; } constexpr float kInitialFeatureVal = -9999.f; @@ -73,7 +73,7 @@ TEST(RnnVadTest, SpectralFeaturesWithAndWithoutSilence) { // Initialize. SpectralFeaturesExtractor sfe; std::array samples; - ArrayView samples_view(samples); + std::span samples_view(samples); bool is_silence; std::array feature_vector; @@ -86,11 +86,11 @@ TEST(RnnVadTest, SpectralFeaturesWithAndWithoutSilence) { // With silence. std::fill(samples.begin(), samples.end(), 0.f); is_silence = sfe.CheckSilenceComputeFeatures( - samples_view, samples_view, GetHigherBandsSpectrum(&feature_vector), - GetAverage(&feature_vector), GetFirstDerivative(&feature_vector), - GetSecondDerivative(&feature_vector), - GetCepstralCrossCorrelation(&feature_vector), - GetCepstralVariability(&feature_vector)); + samples_view, samples_view, GetHigherBandsSpectrum(feature_vector), + GetAverage(feature_vector), GetFirstDerivative(feature_vector), + GetSecondDerivative(feature_vector), + GetCepstralCrossCorrelation(feature_vector), + GetCepstralVariability(feature_vector)); // Silence is expected, the output won't be overwritten. EXPECT_TRUE(is_silence); EXPECT_TRUE(std::all_of(feature_vector.begin(), feature_vector.end(), @@ -99,11 +99,11 @@ TEST(RnnVadTest, SpectralFeaturesWithAndWithoutSilence) { // With no silence. WriteTestData(samples); is_silence = sfe.CheckSilenceComputeFeatures( - samples_view, samples_view, GetHigherBandsSpectrum(&feature_vector), - GetAverage(&feature_vector), GetFirstDerivative(&feature_vector), - GetSecondDerivative(&feature_vector), - GetCepstralCrossCorrelation(&feature_vector), - GetCepstralVariability(&feature_vector)); + samples_view, samples_view, GetHigherBandsSpectrum(feature_vector), + GetAverage(feature_vector), GetFirstDerivative(feature_vector), + GetSecondDerivative(feature_vector), + GetCepstralCrossCorrelation(feature_vector), + GetCepstralVariability(feature_vector)); // Silence is not expected, the output will be overwritten. EXPECT_FALSE(is_silence); EXPECT_FALSE(std::all_of(feature_vector.begin(), feature_vector.end(), @@ -118,29 +118,28 @@ TEST(RnnVadTest, CepstralFeaturesConstantAverageZeroDerivative) { // Initialize. SpectralFeaturesExtractor sfe; std::array samples; - ArrayView samples_view(samples); + std::span samples_view(samples); WriteTestData(samples); // Fill the spectral features with test data. std::array feature_vector; for (int i = 0; i < kCepstralCoeffsHistorySize; ++i) { sfe.CheckSilenceComputeFeatures( - samples_view, samples_view, GetHigherBandsSpectrum(&feature_vector), - GetAverage(&feature_vector), GetFirstDerivative(&feature_vector), - GetSecondDerivative(&feature_vector), - GetCepstralCrossCorrelation(&feature_vector), - GetCepstralVariability(&feature_vector)); + samples_view, samples_view, GetHigherBandsSpectrum(feature_vector), + GetAverage(feature_vector), GetFirstDerivative(feature_vector), + GetSecondDerivative(feature_vector), + GetCepstralCrossCorrelation(feature_vector), + GetCepstralVariability(feature_vector)); } // Feed the test data one last time but using a different output vector. std::array feature_vector_last; sfe.CheckSilenceComputeFeatures( - samples_view, samples_view, GetHigherBandsSpectrum(&feature_vector_last), - GetAverage(&feature_vector_last), - GetFirstDerivative(&feature_vector_last), - GetSecondDerivative(&feature_vector_last), - GetCepstralCrossCorrelation(&feature_vector_last), - GetCepstralVariability(&feature_vector_last)); + samples_view, samples_view, GetHigherBandsSpectrum(feature_vector_last), + GetAverage(feature_vector_last), GetFirstDerivative(feature_vector_last), + GetSecondDerivative(feature_vector_last), + GetCepstralCrossCorrelation(feature_vector_last), + GetCepstralVariability(feature_vector_last)); // Average is unchanged. ExpectEqualFloatArray({feature_vector.data(), kNumLowerBands}, diff --git a/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h index bd1ace8f5e8..ad189db06d5 100644 --- a/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h +++ b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_compare.h" @@ -52,7 +52,7 @@ class SymmetricMatrixBuffer { // most recent one in the ring buffer, whereas the last element in `values` // must correspond to the comparison between the most recent item and the // oldest one in the ring buffer. - void Push(ArrayView values) { + void Push(std::span values) { // Move the lower-right sub-matrix of size (S-2) x (S-2) one row up and one // column left. std::memmove(buf_.data(), buf_.data() + S, (buf_.size() - S) * sizeof(T)); diff --git a/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer_unittest.cc b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer_unittest.cc index b041acba586..8c45d01ef75 100644 --- a/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer_unittest.cc +++ b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer_unittest.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include "modules/audio_processing/agc2/rnn_vad/ring_buffer.h" @@ -61,7 +62,7 @@ TEST(RnnVadTest, SymmetricMatrixBufferUseCase) { for (int t = 1; t <= 100; ++t) { // Evolution steps. SCOPED_TRACE(t); const int t_removed = ring_buf.GetArrayView(kRingBufSize - 1)[0]; - ring_buf.Push({&t, 1}); + ring_buf.Push(std::span(&t, 1)); // The head of the ring buffer is `t`. ASSERT_EQ(t, ring_buf.GetArrayView(0)[0]); // Create the comparisons between `t` and the older elements in the ring @@ -77,7 +78,7 @@ TEST(RnnVadTest, SymmetricMatrixBufferUseCase) { new_comparions[i].second = t; } // Push the new comparisons in the symmetric matrix buffer. - sym_matrix_buf.Push({new_comparions.data(), new_comparions.size()}); + sym_matrix_buf.Push(new_comparions); // Tests. CheckSymmetry(&sym_matrix_buf); // Check that the pairs resulting from the content in the ring buffer are diff --git a/modules/audio_processing/agc2/rnn_vad/test_utils.cc b/modules/audio_processing/agc2/rnn_vad/test_utils.cc index de3045005d7..251a425b812 100644 --- a/modules/audio_processing/agc2/rnn_vad/test_utils.cc +++ b/modules/audio_processing/agc2/rnn_vad/test_utils.cc @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_compare.h" @@ -49,7 +49,7 @@ class FloatFileReader : public FileReader { ~FloatFileReader() override = default; int size() const override { return size_; } - bool ReadChunk(ArrayView dst) override { + bool ReadChunk(std::span dst) override { const std::streamsize bytes_to_read = dst.size() * sizeof(T); if (std::is_same::value) { is_.read(reinterpret_cast(dst.data()), bytes_to_read); @@ -77,8 +77,8 @@ class FloatFileReader : public FileReader { using test::ResourcePath; -void ExpectEqualFloatArray(ArrayView expected, - ArrayView computed) { +void ExpectEqualFloatArray(std::span expected, + std::span computed) { ASSERT_EQ(expected.size(), computed.size()); for (int i = 0; SafeLt(i, expected.size()); ++i) { SCOPED_TRACE(i); @@ -86,8 +86,8 @@ void ExpectEqualFloatArray(ArrayView expected, } } -void ExpectNearAbsolute(ArrayView expected, - ArrayView computed, +void ExpectNearAbsolute(std::span expected, + std::span computed, float tolerance) { ASSERT_EQ(expected.size(), computed.size()); for (int i = 0; SafeLt(i, expected.size()); ++i) { diff --git a/modules/audio_processing/agc2/rnn_vad/test_utils.h b/modules/audio_processing/agc2/rnn_vad/test_utils.h index 4345b04dd1f..f8b4ff8c8cd 100644 --- a/modules/audio_processing/agc2/rnn_vad/test_utils.h +++ b/modules/audio_processing/agc2/rnn_vad/test_utils.h @@ -16,10 +16,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/agc2/rnn_vad/common.h" namespace webrtc { @@ -27,15 +27,15 @@ namespace rnn_vad { constexpr float kFloatMin = std::numeric_limits::min(); -// Fails for every pair from two equally sized webrtc::ArrayView views +// Fails for every pair from two equally sized std::span views // such that the values in the pair do not match. -void ExpectEqualFloatArray(ArrayView expected, - ArrayView computed); +void ExpectEqualFloatArray(std::span expected, + std::span computed); -// Fails for every pair from two equally sized webrtc::ArrayView views +// Fails for every pair from two equally sized std::span views // such that their absolute error is above a given threshold. -void ExpectNearAbsolute(ArrayView expected, - ArrayView computed, +void ExpectNearAbsolute(std::span expected, + std::span computed, float tolerance); // File reader interface. @@ -49,7 +49,7 @@ class FileReader { // values are correctly read. If the number of remaining bytes in the file is // not sufficient to read `dst.size()` float values, `dst` is partially // modified and false is returned. - virtual bool ReadChunk(ArrayView dst) = 0; + virtual bool ReadChunk(std::span dst) = 0; // Reads a single float value, advances the internal file position according // to the number of read bytes and returns true if the value is correctly // read. If the number of remaining bytes in the file is not sufficient to @@ -90,13 +90,13 @@ class PitchTestData { public: PitchTestData(); ~PitchTestData(); - ArrayView PitchBuffer24kHzView() const { + std::span PitchBuffer24kHzView() const { return pitch_buffer_24k_; } - ArrayView SquareEnergies24kHzView() const { + std::span SquareEnergies24kHzView() const { return square_energies_24k_; } - ArrayView AutoCorrelation12kHzView() const { + std::span AutoCorrelation12kHzView() const { return auto_correlation_12k_; } @@ -114,7 +114,7 @@ class FileWriter { FileWriter(const FileWriter&) = delete; FileWriter& operator=(const FileWriter&) = delete; ~FileWriter() = default; - void WriteChunk(ArrayView value) { + void WriteChunk(std::span value) { const std::streamsize bytes_to_write = value.size() * sizeof(float); os_.write(reinterpret_cast(value.data()), bytes_to_write); } diff --git a/modules/audio_processing/agc2/rnn_vad/vector_math.h b/modules/audio_processing/agc2/rnn_vad/vector_math.h index 0cf4e34efb7..0bfe5af3f37 100644 --- a/modules/audio_processing/agc2/rnn_vad/vector_math.h +++ b/modules/audio_processing/agc2/rnn_vad/vector_math.h @@ -22,8 +22,8 @@ #endif #include +#include -#include "api/array_view.h" #include "modules/audio_processing/agc2/cpu_features.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" @@ -40,7 +40,7 @@ class VectorMath { : cpu_features_(cpu_features) {} // Computes the dot product between two equally sized vectors. - float DotProduct(ArrayView x, ArrayView y) const { + float DotProduct(std::span x, std::span y) const { RTC_DCHECK_EQ(x.size(), y.size()); #if defined(WEBRTC_ARCH_X86_FAMILY) if (cpu_features_.avx2) { @@ -101,8 +101,8 @@ class VectorMath { } private: - float DotProductAvx2(ArrayView x, - ArrayView y) const; + float DotProductAvx2(std::span x, + std::span y) const; const AvailableCpuFeatures cpu_features_; }; diff --git a/modules/audio_processing/agc2/rnn_vad/vector_math_avx2.cc b/modules/audio_processing/agc2/rnn_vad/vector_math_avx2.cc index 71466dab204..98c4fdcadf0 100644 --- a/modules/audio_processing/agc2/rnn_vad/vector_math_avx2.cc +++ b/modules/audio_processing/agc2/rnn_vad/vector_math_avx2.cc @@ -10,7 +10,8 @@ #include -#include "api/array_view.h" +#include + #include "modules/audio_processing/agc2/rnn_vad/vector_math.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" @@ -18,8 +19,8 @@ namespace webrtc { namespace rnn_vad { -float VectorMath::DotProductAvx2(ArrayView x, - ArrayView y) const { +float VectorMath::DotProductAvx2(std::span x, + std::span y) const { RTC_DCHECK(cpu_features_.avx2); RTC_DCHECK_EQ(x.size(), y.size()); __m256 accumulator = _mm256_setzero_ps(); diff --git a/modules/audio_processing/agc2/vad_wrapper.cc b/modules/audio_processing/agc2/vad_wrapper.cc index 0d77e7fb257..0d77181a163 100644 --- a/modules/audio_processing/agc2/vad_wrapper.cc +++ b/modules/audio_processing/agc2/vad_wrapper.cc @@ -42,7 +42,7 @@ class MonoVadImpl : public VoiceActivityDetectorWrapper::MonoVad { RTC_DCHECK_EQ(frame.size(), rnn_vad::kFrameSize10ms24kHz); std::array feature_vector; const bool is_silence = features_extractor_.CheckSilenceComputeFeatures( - /*samples=*/{frame.data(), rnn_vad::kFrameSize10ms24kHz}, + /*samples=*/frame.first(), feature_vector); return rnn_vad_.ComputeVadProbability(feature_vector, is_silence); } diff --git a/modules/audio_processing/agc2/vad_wrapper_unittest.cc b/modules/audio_processing/agc2/vad_wrapper_unittest.cc index 7d515914566..e1593549259 100644 --- a/modules/audio_processing/agc2/vad_wrapper_unittest.cc +++ b/modules/audio_processing/agc2/vad_wrapper_unittest.cc @@ -12,11 +12,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/audio/audio_view.h" #include "modules/audio_processing/agc2/agc2_common.h" #include "rtc_base/checks.h" @@ -43,7 +43,7 @@ class MockVad : public VoiceActivityDetectorWrapper::MonoVad { public: MOCK_METHOD(int, SampleRateHz, (), (const, override)); MOCK_METHOD(void, Reset, (), (override)); - MOCK_METHOD(float, Analyze, (ArrayView frame), (override)); + MOCK_METHOD(float, Analyze, (std::span frame), (override)); }; // Checks that the ctor and `Initialize()` read the sample rate of the wrapped @@ -159,7 +159,7 @@ TEST_P(VadResamplingParametrization, CheckResampledFrameSize) { .Times(AnyNumber()) .WillRepeatedly(Return(vad_sample_rate_hz())); EXPECT_CALL(*vad, Reset).Times(1); - EXPECT_CALL(*vad, Analyze(Truly([this](ArrayView frame) { + EXPECT_CALL(*vad, Analyze(Truly([this](std::span frame) { return SafeEq(frame.size(), CheckedDivExact(vad_sample_rate_hz(), kNumFramesPerSecond)); }))).Times(1); diff --git a/modules/audio_processing/audio_buffer.cc b/modules/audio_processing/audio_buffer.cc index bf559cf3209..f4ad8ad2c82 100644 --- a/modules/audio_processing/audio_buffer.cc +++ b/modules/audio_processing/audio_buffer.cc @@ -10,7 +10,6 @@ #include "modules/audio_processing/audio_buffer.h" -#include #include #include #include @@ -39,6 +38,17 @@ size_t NumBandsFromFramesPerChannel(size_t num_frames) { return 1; } +size_t GetNumberOfInternalChannelsInBuffer( + AudioProcessing::Config::Pipeline::DownmixMethod downmix_method, + size_t input_num_channels, + size_t buffer_num_channels) { + RTC_DCHECK_LE(buffer_num_channels, input_num_channels); + return downmix_method == + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive + ? input_num_channels + : buffer_num_channels; +} + } // namespace AudioBuffer::AudioBuffer(size_t input_rate, @@ -53,41 +63,63 @@ AudioBuffer::AudioBuffer(size_t input_rate, buffer_num_channels, output_rate) {} -AudioBuffer::AudioBuffer(size_t input_rate, - size_t input_num_channels, - size_t buffer_rate, - size_t buffer_num_channels, - size_t output_rate) - : input_num_frames_(static_cast(input_rate) / 100), +AudioBuffer::AudioBuffer( + size_t input_rate, + size_t input_num_channels, + size_t buffer_rate, + size_t buffer_num_channels, + size_t output_rate, + AudioProcessing::Config::Pipeline::DownmixMethod downmix_method, + AudioProcessing::Config::Pipeline::DownmixMethod downmix_method_stereo) + : downmix_method_(downmix_method), + downmix_method_stereo_(downmix_method_stereo), + input_num_frames_(static_cast(input_rate) / 100), input_num_channels_(input_num_channels), buffer_num_frames_(static_cast(buffer_rate) / 100), buffer_num_channels_(buffer_num_channels), + buffer_internal_num_channels_( + GetNumberOfInternalChannelsInBuffer(downmix_method_, + input_num_channels, + buffer_num_channels)), output_num_frames_(static_cast(output_rate) / 100), output_num_channels_(0), num_channels_(buffer_num_channels), num_bands_(NumBandsFromFramesPerChannel(buffer_num_frames_)), num_split_frames_(CheckedDivExact(buffer_num_frames_, num_bands_)), - data_( - new ChannelBuffer(buffer_num_frames_, buffer_num_channels_)) { + data_(new ChannelBuffer(buffer_num_frames_, + buffer_internal_num_channels_)), + downmix_by_averaging_( + downmix_method == + AudioProcessing::Config::Pipeline::DownmixMethod::kAverageChannels), + channel_for_downmixing_(0), + capture_mixer_(buffer_num_frames_) { RTC_DCHECK_GT(input_num_frames_, 0); RTC_DCHECK_GT(buffer_num_frames_, 0); RTC_DCHECK_GT(output_num_frames_, 0); RTC_DCHECK_GT(input_num_channels_, 0); RTC_DCHECK_GT(buffer_num_channels_, 0); RTC_DCHECK_LE(buffer_num_channels_, input_num_channels_); + RTC_DCHECK(downmix_by_averaging_ || + input_num_channels_ > channel_for_downmixing_); + RTC_DCHECK_GT(buffer_internal_num_channels_, 0); + RTC_DCHECK_LE(buffer_internal_num_channels_, input_num_channels_); + RTC_DCHECK_GE(buffer_internal_num_channels_, buffer_num_channels_); + + RTC_DCHECK(buffer_num_channels == 1 || + buffer_num_channels == input_num_channels); const bool input_resampling_needed = input_num_frames_ != buffer_num_frames_; const bool output_resampling_needed = output_num_frames_ != buffer_num_frames_; if (input_resampling_needed) { - for (size_t i = 0; i < buffer_num_channels_; ++i) { + for (size_t i = 0; i < buffer_internal_num_channels_; ++i) { input_resamplers_.push_back(std::unique_ptr( new PushSincResampler(input_num_frames_, buffer_num_frames_))); } } if (output_resampling_needed) { - for (size_t i = 0; i < buffer_num_channels_; ++i) { + for (size_t i = 0; i < buffer_internal_num_channels_; ++i) { output_resamplers_.push_back(std::unique_ptr( new PushSincResampler(buffer_num_frames_, output_num_frames_))); } @@ -95,33 +127,52 @@ AudioBuffer::AudioBuffer(size_t input_rate, if (num_bands_ > 1) { split_data_.reset(new ChannelBuffer( - buffer_num_frames_, buffer_num_channels_, num_bands_)); + buffer_num_frames_, buffer_internal_num_channels_, num_bands_)); splitting_filter_.reset(new SplittingFilter( - buffer_num_channels_, num_bands_, buffer_num_frames_)); + buffer_internal_num_channels_, num_bands_, buffer_num_frames_)); } } AudioBuffer::~AudioBuffer() {} -void AudioBuffer::set_downmixing_to_specific_channel(size_t channel) { - downmix_by_averaging_ = false; - RTC_DCHECK_GT(input_num_channels_, channel); - channel_for_downmixing_ = std::min(channel, input_num_channels_ - 1); -} - -void AudioBuffer::set_downmixing_by_averaging() { - downmix_by_averaging_ = true; -} - void AudioBuffer::CopyFrom(const float* const* stacked_data, const StreamConfig& stream_config) { RTC_DCHECK_EQ(stream_config.num_frames(), input_num_frames_); RTC_DCHECK_EQ(stream_config.num_channels(), input_num_channels_); RestoreNumChannels(); - const bool downmix_needed = input_num_channels_ > 1 && num_channels_ == 1; const bool resampling_needed = input_num_frames_ != buffer_num_frames_; + const bool use_adaptive_downmixing = + (downmix_method_ == + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive || + downmix_method_stereo_ == + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive) && + buffer_internal_num_channels_ == 2; + if (use_adaptive_downmixing) { + if (resampling_needed) { + for (size_t ch = 0; ch < input_num_channels_; ++ch) { + input_resamplers_[ch]->Resample(stacked_data[ch], input_num_frames_, + data_->channels()[ch], + buffer_num_frames_); + FloatToFloatS16(data_->channels()[ch], buffer_num_frames_, + data_->channels()[ch]); + } + } else { + for (size_t ch = 0; ch < input_num_channels_; ++ch) { + FloatToFloatS16(stacked_data[ch], buffer_num_frames_, + data_->channels()[ch]); + } + } + + capture_mixer_.Mix(buffer_num_channels_, {&channels()[0][0], num_frames()}, + {&channels()[1][0], num_frames()}); + set_num_channels(buffer_num_channels_); + return; + } + + const bool downmix_needed = input_num_channels_ > 1 && num_channels_ == 1; + if (downmix_needed) { RTC_DCHECK_GE(kMaxSamplesPerChannel10ms, input_num_frames_); @@ -240,6 +291,47 @@ void AudioBuffer::CopyFrom(const int16_t* const interleaved_data, const bool resampling_required = input_num_frames_ != buffer_num_frames_; const int16_t* interleaved = interleaved_data; + + const bool use_adaptive_downmixing = + ((downmix_method_ == + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive || + downmix_method_stereo_ == + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive) && + input_num_channels_ == 2); + RTC_DCHECK(!use_adaptive_downmixing || buffer_internal_num_channels_ == 2); + + if (use_adaptive_downmixing) { + auto deinterleave_channel = [](size_t channel, size_t num_channels, + size_t samples_per_channel, const int16_t* x, + float* y) { + for (size_t j = 0, k = channel; j < samples_per_channel; + ++j, k += num_channels) { + y[j] = x[k]; + } + }; + + if (resampling_required) { + std::array float_buffer; + for (size_t i = 0; i < input_num_channels_; ++i) { + deinterleave_channel(i, input_num_channels_, input_num_frames_, + interleaved, float_buffer.data()); + input_resamplers_[i]->Resample(float_buffer.data(), input_num_frames_, + data_->channels()[i], + buffer_num_frames_); + } + } else { + for (size_t i = 0; i < input_num_channels_; ++i) { + deinterleave_channel(i, input_num_channels_, input_num_frames_, + interleaved, data_->channels()[i]); + } + } + + capture_mixer_.Mix(buffer_num_channels_, {&channels()[0][0], num_frames()}, + {&channels()[1][0], num_frames()}); + set_num_channels(buffer_num_channels_); + return; + } + if (num_channels_ == 1) { if (input_num_channels_ == 1) { if (resampling_required) { diff --git a/modules/audio_processing/audio_buffer.h b/modules/audio_processing/audio_buffer.h index 60aea87bfba..c53df4a2d9b 100644 --- a/modules/audio_processing/audio_buffer.h +++ b/modules/audio_processing/audio_buffer.h @@ -21,6 +21,7 @@ #include "api/audio/audio_view.h" #include "common_audio/channel_buffer.h" #include "common_audio/include/audio_util.h" +#include "modules/audio_processing/capture_mixer/capture_mixer.h" #include "rtc_base/gtest_prod_util.h" namespace webrtc { @@ -45,23 +46,22 @@ class AudioBuffer { size_t output_rate, size_t output_num_channels); - AudioBuffer(size_t input_rate, - size_t input_num_channels, - size_t buffer_rate, - size_t buffer_num_channels, - size_t output_rate); + AudioBuffer( + size_t input_rate, + size_t input_num_channels, + size_t buffer_rate, + size_t buffer_num_channels, + size_t output_rate, + AudioProcessing::Config::Pipeline::DownmixMethod downmix_method = + AudioProcessing::Config::Pipeline::DownmixMethod::kAverageChannels, + AudioProcessing::Config::Pipeline::DownmixMethod downmix_method_stereo = + AudioProcessing::Config::Pipeline::DownmixMethod::kAverageChannels); virtual ~AudioBuffer(); AudioBuffer(const AudioBuffer&) = delete; AudioBuffer& operator=(const AudioBuffer&) = delete; - // Specify that downmixing should be done by selecting a single channel. - void set_downmixing_to_specific_channel(size_t channel); - - // Specify that downmixing should be done by averaging all channels,. - void set_downmixing_by_averaging(); - // Set the number of channels in the buffer. The specified number of channels // cannot be larger than the specified buffer_num_channels. The number is also // reset at each call to CopyFrom or InterleaveFrom. @@ -163,10 +163,13 @@ class AudioBuffer { SetNumChannelsSetsChannelBuffersNumChannels); void RestoreNumChannels(); + const AudioProcessing::Config::Pipeline::DownmixMethod downmix_method_; + const AudioProcessing::Config::Pipeline::DownmixMethod downmix_method_stereo_; const size_t input_num_frames_; const size_t input_num_channels_; const size_t buffer_num_frames_; const size_t buffer_num_channels_; + const size_t buffer_internal_num_channels_; const size_t output_num_frames_; const size_t output_num_channels_; @@ -181,6 +184,7 @@ class AudioBuffer { std::vector> output_resamplers_; bool downmix_by_averaging_ = true; size_t channel_for_downmixing_ = 0; + CaptureMixer capture_mixer_; }; } // namespace webrtc diff --git a/modules/audio_processing/audio_buffer_unittest.cc b/modules/audio_processing/audio_buffer_unittest.cc index b2b928beb5b..69c499296fb 100644 --- a/modules/audio_processing/audio_buffer_unittest.cc +++ b/modules/audio_processing/audio_buffer_unittest.cc @@ -10,9 +10,15 @@ #include "modules/audio_processing/audio_buffer.h" +#include #include #include +#include +#include +#include +#include +#include "api/audio/audio_processing.h" #include "api/audio/audio_view.h" #include "rtc_base/checks.h" #include "test/gtest.h" @@ -22,37 +28,200 @@ namespace webrtc { namespace { -constexpr size_t kSampleRateHz = 48000u; -constexpr size_t kStereo = 2u; -constexpr size_t kMono = 1u; - void ExpectNumChannels(const AudioBuffer& ab, size_t num_channels) { EXPECT_EQ(ab.num_channels(), num_channels); } +void FillChannelWith100HzSine(int channel, float amplitude, AudioBuffer& ab) { + constexpr float kPi = std::numbers::pi; + float sample_rate_hz; + if (ab.num_frames() == 160) { + sample_rate_hz = 16000.0f; + } else if (ab.num_frames() == 320) { + sample_rate_hz = 32000.0f; + } else { + sample_rate_hz = 48000.0f; + } + + constexpr float kFrequencyHz = 100.0f; + for (size_t i = 0; i < ab.num_frames(); ++i) { + ab.channels()[channel][i] = + amplitude * std::sin(2 * kPi * kFrequencyHz / sample_rate_hz * i); + } +} + +void FillChannelWith100HzSine(int sample_rate_hz, + int channel, + float amplitude, + float* const* stacked_data) { + constexpr float kPi = std::numbers::pi; + int num_samples_per_channel; + if (sample_rate_hz == 16000) { + num_samples_per_channel = 160; + } else if (sample_rate_hz == 32000) { + num_samples_per_channel = 320; + } else { + num_samples_per_channel = 480; + } + + constexpr float kFrequencyHz = 100.0f; + for (int i = 0; i < num_samples_per_channel; ++i) { + stacked_data[channel][i] = + amplitude * std::sin(2 * kPi * kFrequencyHz / sample_rate_hz * i); + } +} + +void FillChannelWith100HzSine(int sample_rate_hz, + int num_channels, + int channel, + float amplitude, + int16_t* const interleaved_data) { + constexpr float kPi = std::numbers::pi; + int num_samples_per_channel; + if (sample_rate_hz == 16000) { + num_samples_per_channel = 160; + } else if (sample_rate_hz == 32000) { + num_samples_per_channel = 320; + } else { + num_samples_per_channel = 480; + } + + constexpr float kFrequencyHz = 100.0f; + for (int i = 0; i < num_samples_per_channel; ++i) { + interleaved_data[channel + i * num_channels] = static_cast( + amplitude * std::sin(2 * kPi * kFrequencyHz / sample_rate_hz * i)); + } +} + +enum class DownmixingSignalVariant { + kInactive, + kChannel0Inactive, + kVeryImbalanced, + kBalanced +}; + } // namespace +class AudioBufferMonoInputDownmixingTest + : public ::testing::Test, + public ::testing::WithParamInterface< + std::tuple> { + protected: + int Rate1() const { return std::get<0>(GetParam()); } + int Rate2() const { return std::get<1>(GetParam()); } + AudioProcessing::Config::Pipeline::DownmixMethod DownmixingMethod() const { + return std::get<2>(GetParam()); + } +}; + +INSTANTIATE_TEST_SUITE_P( + AudioBufferTests, + AudioBufferMonoInputDownmixingTest, + ::testing::Combine( + ::testing::Values(16000, 32000, 48000), + ::testing::Values(16000, 32000, 48000), + ::testing::Values( + AudioProcessing::Config::Pipeline::DownmixMethod::kAverageChannels, + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive))); + +class AudioBufferStereoInputDownmixingTest + : public ::testing::Test, + public ::testing::WithParamInterface< + std::tuple> { + protected: + int Rate1() const { return std::get<0>(GetParam()); } + int Rate2() const { return std::get<1>(GetParam()); } + size_t NumChannels() const { return std::get<2>(GetParam()); } + AudioProcessing::Config::Pipeline::DownmixMethod DownmixingMethod() const { + return std::get<3>(GetParam()); + } + bool OnlyInitialFrames() const { return std::get<4>(GetParam()); } + bool UseFloatInterface() const { return std::get<5>(GetParam()); } + DownmixingSignalVariant SignalVariant() const { + return std::get<6>(GetParam()); + } +}; + +INSTANTIATE_TEST_SUITE_P( + AudioBufferTests, + AudioBufferStereoInputDownmixingTest, + ::testing::Combine( + ::testing::Values(16000, 32000, 48000), + ::testing::Values(16000, 32000, 48000), + ::testing::Values(1, 2), + ::testing::Values( + AudioProcessing::Config::Pipeline::DownmixMethod::kAverageChannels, + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive), + ::testing::Values(false, true), + ::testing::Values(false, true), + ::testing::Values(DownmixingSignalVariant::kInactive, + DownmixingSignalVariant::kChannel0Inactive, + DownmixingSignalVariant::kVeryImbalanced, + DownmixingSignalVariant::kBalanced) + + )); + +class AudioBufferChannelCountAndTwoRatesTest + : public ::testing::Test, + public ::testing::WithParamInterface> { + protected: + int Rate1() const { return std::get<0>(GetParam()); } + int Rate2() const { return std::get<1>(GetParam()); } + int NumChannels() const { return std::get<2>(GetParam()); } +}; + +INSTANTIATE_TEST_SUITE_P( + AudioBufferTests, + AudioBufferChannelCountAndTwoRatesTest, + ::testing::Combine(::testing::Values(16000, 32000, 48000), + ::testing::Values(16000, 32000, 48000), + ::testing::Values(1, 2))); + +class AudioBufferChannelCountAndOneRateTest + : public ::testing::Test, + public ::testing::WithParamInterface> { + protected: + int Rate() const { return std::get<0>(GetParam()); } + int NumChannels() const { return std::get<1>(GetParam()); } +}; + +INSTANTIATE_TEST_SUITE_P( + AudioBufferTests, + AudioBufferChannelCountAndOneRateTest, + ::testing::Combine(::testing::Values(16000, 32000, 48000), + ::testing::Values(1, 2))); + TEST(AudioBufferTest, SetNumChannelsSetsChannelBuffersNumChannels) { - AudioBuffer ab(kSampleRateHz, kStereo, kSampleRateHz, kStereo, kSampleRateHz, - kStereo); - ExpectNumChannels(ab, kStereo); + constexpr size_t kSampleRateHz = 48000u; + AudioBuffer ab(kSampleRateHz, 2, kSampleRateHz, 2, kSampleRateHz, 2); + ExpectNumChannels(ab, 2); ab.set_num_channels(1); - ExpectNumChannels(ab, kMono); + ExpectNumChannels(ab, 1); ab.RestoreNumChannels(); - ExpectNumChannels(ab, kStereo); + ExpectNumChannels(ab, 2); } #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) TEST(AudioBufferDeathTest, SetNumChannelsDeathTest) { - AudioBuffer ab(kSampleRateHz, kMono, kSampleRateHz, kMono, kSampleRateHz, - kMono); - RTC_EXPECT_DEATH(ab.set_num_channels(kStereo), "num_channels"); + constexpr size_t kSampleRateHz = 48000u; + AudioBuffer ab(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, 1); + RTC_EXPECT_DEATH(ab.set_num_channels(2), "num_channels"); } #endif -TEST(AudioBufferTest, CopyWithoutResampling) { - AudioBuffer ab1(32000, 2, 32000, 2, 32000, 2); - AudioBuffer ab2(32000, 2, 32000, 2, 32000, 2); +TEST_P(AudioBufferChannelCountAndOneRateTest, CopyWithoutResampling) { + AudioBuffer ab1(Rate(), NumChannels(), Rate(), NumChannels(), Rate(), + NumChannels()); + AudioBuffer ab2(Rate(), NumChannels(), Rate(), NumChannels(), Rate(), + NumChannels()); // Fill first buffer. for (size_t ch = 0; ch < ab1.num_channels(); ++ch) { for (size_t i = 0; i < ab1.num_frames(); ++i) { @@ -69,16 +238,18 @@ TEST(AudioBufferTest, CopyWithoutResampling) { } } -TEST(AudioBufferTest, CopyWithResampling) { - AudioBuffer ab1(32000, 2, 32000, 2, 48000, 2); - AudioBuffer ab2(48000, 2, 48000, 2, 48000, 2); - float energy_ab1 = 0.f; - float energy_ab2 = 0.f; - const float pi = std::acos(-1.f); +TEST_P(AudioBufferChannelCountAndTwoRatesTest, CopyWithResampling) { + AudioBuffer ab1(Rate1(), NumChannels(), Rate1(), NumChannels(), Rate2(), + NumChannels()); + AudioBuffer ab2(Rate2(), NumChannels(), Rate2(), NumChannels(), Rate2(), + NumChannels()); + float energy_ab1 = 0.0f; + float energy_ab2 = 0.0f; // Put a sine and compute energy of first buffer. for (size_t ch = 0; ch < ab1.num_channels(); ++ch) { + FillChannelWith100HzSine(ch, 1.0f, ab1); + for (size_t i = 0; i < ab1.num_frames(); ++i) { - ab1.channels()[ch][i] = std::sin(2 * pi * 100.f / 32000.f * i); energy_ab1 += ab1.channels()[ch][i] * ab1.channels()[ch][i]; } } @@ -91,23 +262,21 @@ TEST(AudioBufferTest, CopyWithResampling) { } } // Verify that energies match. - EXPECT_NEAR(energy_ab1, energy_ab2 * 32000.f / 48000.f, .01f * energy_ab1); + EXPECT_NEAR(energy_ab1, energy_ab2 * Rate1() / Rate2(), .04f * energy_ab1); } -TEST(AudioBufferTest, DeinterleavedView) { - AudioBuffer ab(48000, 2, 48000, 2, 48000, 2); +TEST_P(AudioBufferChannelCountAndOneRateTest, DeinterleavedView) { + AudioBuffer ab(Rate(), NumChannels(), Rate(), NumChannels(), Rate(), + NumChannels()); // Fill the buffer with data. - const float pi = std::acos(-1.f); - float* const* channels = ab.channels(); for (size_t ch = 0; ch < ab.num_channels(); ++ch) { - for (size_t i = 0; i < ab.num_frames(); ++i) { - channels[ch][i] = std::sin(2 * pi * 100.f / 32000.f * i); - } + FillChannelWith100HzSine(ch, 1.0f, ab); } // Verify that the DeinterleavedView correctly maps to channels. DeinterleavedView view = ab.view(); ASSERT_EQ(view.num_channels(), ab.num_channels()); + float* const* channels = ab.channels(); for (size_t c = 0; c < view.num_channels(); ++c) { MonoView channel = view[c]; EXPECT_EQ(SamplesPerChannel(channel), ab.num_frames()); @@ -117,4 +286,221 @@ TEST(AudioBufferTest, DeinterleavedView) { } } +TEST_P(AudioBufferMonoInputDownmixingTest, MonoCaptureStacked) { + AudioProcessing::Config::Pipeline::DownmixMethod downmixing_method = + DownmixingMethod(); + int num_frames_per_channel = Rate1() / 100; + std::vector audio_data(num_frames_per_channel); + std::array audio = {audio_data.data()}; + AudioBuffer ab(Rate1(), 1, Rate2(), 1, Rate2(), downmixing_method); + float energy_input = 0.0f; + float energy_ab = 0.0f; + // Put a sine and compute energy of first buffer (compensating for the + // internal S16 format in AudioBuffer.. + FillChannelWith100HzSine(Rate1(), 0, 0.7f, audio.data()); + for (int i = 0; i < num_frames_per_channel; ++i) { + energy_input += audio[0][i] * 32768.0f * audio[0][i] * 32768.0f; + ; + } + + // Copy to audio buffer. + StreamConfig stream_config(Rate1(), 1); + ab.CopyFrom(audio.data(), stream_config); + + // Verify that the channel count is correct. + EXPECT_EQ(ab.num_channels(), 1u); + + // Compute energy of audio buffer. + for (size_t i = 0; i < ab.num_frames(); ++i) { + energy_ab += ab.channels()[0][i] * ab.channels()[0][i]; + } + // Verify that energies match. + EXPECT_NEAR(energy_input, energy_ab * Rate1() / Rate2(), .04f * energy_input); +} + +TEST_P(AudioBufferMonoInputDownmixingTest, MonoCaptureInterleaved) { + AudioProcessing::Config::Pipeline::DownmixMethod downmixing_method = + DownmixingMethod(); + int num_frames_per_channel = Rate1() / 100; + std::vector audio(num_frames_per_channel); + AudioBuffer ab(Rate1(), 1, Rate2(), 1, Rate2(), downmixing_method); + float energy_input = 0.0f; + float energy_ab = 0.0f; + // Put a sine and compute energy of first buffer (compensating for the + // internal S16 format in AudioBuffer). + FillChannelWith100HzSine(Rate1(), 1, 0, 0.7f, audio.data()); + for (int i = 0; i < num_frames_per_channel; ++i) { + energy_input += audio[i] * 32768.0f * audio[i] * 32768.0f; + } + + // Copy to audio buffer. + StreamConfig stream_config(Rate1(), 1); + ab.CopyFrom(audio.data(), stream_config); + + // Verify that the channel count is correct. + EXPECT_EQ(ab.num_channels(), 1u); + + // Compute energy of audio buffer. + for (size_t i = 0; i < ab.num_frames(); ++i) { + energy_ab += ab.channels()[0][i] * ab.channels()[0][i]; + } + // Verify that energies match. + EXPECT_NEAR(energy_input, energy_ab * Rate1() / Rate2(), .04f * energy_input); +} + +TEST_P(AudioBufferStereoInputDownmixingTest, StereoCapture) { + AudioProcessing::Config::Pipeline::DownmixMethod downmixing_method = + DownmixingMethod(); + int num_frames_per_channel = Rate1() / 100; + AudioBuffer ab(Rate1(), 2, Rate2(), NumChannels(), Rate2(), + downmixing_method); + float energy_ab_ch0 = 0.0f; + float energy_ab_ch1 = 0.0f; + float energy_input_ch0 = 0.0f; + float energy_input_ch1 = 0.0f; + float energy_input_average = 0.0f; + + const int num_frames_to_process = OnlyInitialFrames() ? 10 : 250; + + float amplitude0; + float amplitude1; + switch (SignalVariant()) { + case DownmixingSignalVariant::kInactive: + amplitude0 = 0.0001f; + amplitude1 = 0.0002f; + break; + case DownmixingSignalVariant::kChannel0Inactive: + amplitude0 = 0.0001f; + amplitude1 = 0.8f; + break; + case DownmixingSignalVariant::kVeryImbalanced: + amplitude0 = 0.01f; + amplitude1 = 0.8f; + break; + case DownmixingSignalVariant::kBalanced: + amplitude0 = 0.7f; + amplitude1 = 0.8f; + break; + } + + if (UseFloatInterface()) { + std::vector audio_data(2 * num_frames_per_channel); + std::array audio = {&audio_data[0], + &audio_data[num_frames_per_channel]}; + + // Put a sine and compute energy of first buffer (compensating for the + // internal S16 format in AudioBuffer). + FillChannelWith100HzSine(Rate1(), 0, amplitude0, audio.data()); + FillChannelWith100HzSine(Rate1(), 1, amplitude1, audio.data()); + for (int i = 0; i < num_frames_per_channel; ++i) { + energy_input_ch0 += (audio[0][i] * audio[0][i]) * 32768.0f * 32768.0f; + energy_input_ch1 += (audio[1][i] * audio[1][i]) * 32768.0f * 32768.0f; + float average = (audio[0][i] + audio[1][i]) * 32768.0f * 0.5f; + energy_input_average += average * average; + } + + // Copy to audio buffer. + StreamConfig stream_config(Rate1(), 2); + for (int k = 0; k < num_frames_to_process; ++k) { + ab.CopyFrom(audio.data(), stream_config); + + // Verify that the channel count is correct. + EXPECT_EQ(ab.num_channels(), NumChannels()); + } + } else { + std::vector audio(2 * num_frames_per_channel); + // Put a sine and compute energy of first buffer (compensating for the + // internal S16 format in AudioBuffer). + FillChannelWith100HzSine(Rate1(), 2, 0, amplitude0, audio.data()); + FillChannelWith100HzSine(Rate1(), 2, 1, amplitude1, audio.data()); + for (int i = 0; i < num_frames_per_channel; ++i) { + energy_input_ch0 += (audio[2 * i] * audio[2 * i]); + energy_input_ch1 += (audio[2 * i + 1] * audio[2 * i + 1]); + float average = (audio[2 * i] + audio[2 * i + 1]) * 0.5f; + energy_input_average += average * average; + } + + // Copy to audio buffer. + StreamConfig stream_config(Rate1(), 2); + for (int k = 0; k < num_frames_to_process; ++k) { + ab.CopyFrom(audio.data(), stream_config); + + // Verify that the channel count is correct. + EXPECT_EQ(ab.num_channels(), NumChannels()); + } + } + + // Compute energy of audio buffer. + for (size_t i = 0; i < ab.num_frames(); ++i) { + energy_ab_ch0 += ab.channels()[0][i] * ab.channels()[0][i]; + } + if (ab.num_channels() == 2) { + for (size_t i = 0; i < ab.num_frames(); ++i) { + energy_ab_ch1 += ab.channels()[1][i] * ab.channels()[1][i]; + } + } + + // Verify that energies match. + if (downmixing_method == + AudioProcessing::Config::Pipeline::DownmixMethod::kAverageChannels) { + if (NumChannels() == 1) { + EXPECT_NEAR(energy_input_average, energy_ab_ch0 * Rate1() / Rate2(), + .04f * energy_input_average); + } else { + EXPECT_NEAR(energy_input_ch0, energy_ab_ch0 * Rate1() / Rate2(), + .04f * energy_input_average); + EXPECT_NEAR(energy_input_ch1, energy_ab_ch1 * Rate1() / Rate2(), + .04f * energy_input_average); + } + return; + } + ASSERT_EQ(downmixing_method, + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive); + + if (OnlyInitialFrames()) { + EXPECT_NEAR(energy_input_average, energy_ab_ch0 * Rate1() / Rate2(), + .04f * energy_input_average); + if (NumChannels() == 2) { + EXPECT_NEAR(energy_input_average, energy_ab_ch1 * Rate1() / Rate2(), + .04f * energy_input_average); + } + return; + } + + switch (SignalVariant()) { + case DownmixingSignalVariant::kInactive: + EXPECT_NEAR(energy_input_average, energy_ab_ch0 * Rate1() / Rate2(), + .04f * energy_input_average); + if (NumChannels() == 2) { + EXPECT_NEAR(energy_input_average, energy_ab_ch1 * Rate1() / Rate2(), + .04f * energy_input_average); + } + break; + case DownmixingSignalVariant::kChannel0Inactive: + EXPECT_NEAR(energy_input_average, energy_ab_ch0 * Rate1() / Rate2(), + .04f * energy_input_average); + if (NumChannels() == 2) { + EXPECT_NEAR(energy_input_average, energy_ab_ch1 * Rate1() / Rate2(), + .04f * energy_input_average); + } + break; + case DownmixingSignalVariant::kVeryImbalanced: + EXPECT_NEAR(energy_input_ch1, energy_ab_ch0 * Rate1() / Rate2(), + .04f * energy_input_ch1); + if (NumChannels() == 2) { + EXPECT_NEAR(energy_input_ch1, energy_ab_ch1 * Rate1() / Rate2(), + .04f * energy_input_ch1); + } + break; + case DownmixingSignalVariant::kBalanced: + EXPECT_NEAR(energy_input_ch0, energy_ab_ch0 * Rate1() / Rate2(), + .04f * energy_input_ch0); + if (NumChannels() == 2) { + EXPECT_NEAR(energy_input_ch1, energy_ab_ch1 * Rate1() / Rate2(), + .04f * energy_input_ch0); + } + break; + } +} + } // namespace webrtc diff --git a/modules/audio_processing/audio_frame_view_unittest.cc b/modules/audio_processing/audio_frame_view_unittest.cc index e8aee63c26b..7952acea838 100644 --- a/modules/audio_processing/audio_frame_view_unittest.cc +++ b/modules/audio_processing/audio_frame_view_unittest.cc @@ -86,8 +86,10 @@ TEST(AudioFrameTest, FromDeinterleavedView) { AudioFrameView frame_view(view); EXPECT_EQ(static_cast(frame_view.num_channels()), view.num_channels()); - EXPECT_EQ(frame_view[0], view[0]); - EXPECT_EQ(frame_view[1], view[1]); + EXPECT_EQ(frame_view[0].size(), view[0].size()); + EXPECT_EQ(frame_view[0].data(), view[0].data()); + EXPECT_EQ(frame_view[1].size(), view[1].size()); + EXPECT_EQ(frame_view[1].data(), view[1].data()); } } // namespace webrtc diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index aac8c379805..265a230e31a 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -18,13 +18,13 @@ #include #include #include +#include #include #include #include #include "absl/base/nullability.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/audio/audio_processing_statistics.h" #include "api/audio/audio_view.h" @@ -45,7 +45,6 @@ #include "modules/audio_processing/agc2/input_volume_stats_reporter.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h" -#include "modules/audio_processing/echo_control_mobile_impl.h" #include "modules/audio_processing/gain_control_impl.h" #include "modules/audio_processing/gain_controller2.h" #include "modules/audio_processing/high_pass_filter.h" @@ -321,25 +320,10 @@ int HandleUnsupportedAudioFormats(const float* const* src, return error_code; } -using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod; - -void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) { - switch (method) { - case DownmixMethod::kAverageChannels: - buffer.set_downmixing_by_averaging(); - break; - case DownmixMethod::kUseFirstChannel: - buffer.set_downmixing_to_specific_channel(/*channel=*/0); - break; - } -} - bool NeedEchoController(const AudioProcessing::Config& config, bool has_echo_control_factory) { // For legacy reasons, having an echo control factory overrides the config. - return (config.echo_canceller.enabled && - !config.echo_canceller.mobile_mode) || - has_echo_control_factory; + return config.echo_canceller.enabled || has_echo_control_factory; } constexpr int kUnspecifiedDataDumpInputVolume = -100; @@ -360,7 +344,6 @@ AudioProcessingImpl::SubmoduleStates::SubmoduleStates( bool AudioProcessingImpl::SubmoduleStates::Update( bool high_pass_filter_enabled, - bool mobile_echo_controller_enabled, bool noise_suppressor_enabled, bool adaptive_gain_controller_enabled, bool gain_controller2_enabled, @@ -368,8 +351,6 @@ bool AudioProcessingImpl::SubmoduleStates::Update( bool echo_controller_enabled) { bool changed = false; changed |= (high_pass_filter_enabled != high_pass_filter_enabled_); - changed |= - (mobile_echo_controller_enabled != mobile_echo_controller_enabled_); changed |= (noise_suppressor_enabled != noise_suppressor_enabled_); changed |= (adaptive_gain_controller_enabled != adaptive_gain_controller_enabled_); @@ -378,7 +359,6 @@ bool AudioProcessingImpl::SubmoduleStates::Update( changed |= (echo_controller_enabled != echo_controller_enabled_); if (changed) { high_pass_filter_enabled_ = high_pass_filter_enabled; - mobile_echo_controller_enabled_ = mobile_echo_controller_enabled; noise_suppressor_enabled_ = noise_suppressor_enabled; adaptive_gain_controller_enabled_ = adaptive_gain_controller_enabled; gain_controller2_enabled_ = gain_controller2_enabled; @@ -584,16 +564,20 @@ void AudioProcessingImpl::InitializeLocked() { render_.render_converter.reset(nullptr); } + // Enforce adaptive downmixing when the echo canceller is active and + // multi-channel processing is used. + AudioProcessing::Config::Pipeline::DownmixMethod downmixing_method = + config_.pipeline.capture_downmix_method; + AudioProcessing::Config::Pipeline::DownmixMethod downmixing_method_stereo = + config_.pipeline.capture_downmix_method_stereo_aec; + capture_.capture_audio.reset(new AudioBuffer( formats_.api_format.input_stream().sample_rate_hz(), formats_.api_format.input_stream().num_channels(), capture_nonlocked_.capture_processing_format.sample_rate_hz(), formats_.api_format.output_stream().num_channels(), - formats_.api_format.output_stream().sample_rate_hz(), - formats_.api_format.output_stream().num_channels())); - SetDownmixMethod(*capture_.capture_audio, - config_.pipeline.capture_downmix_method); - + formats_.api_format.output_stream().sample_rate_hz(), downmixing_method, + downmixing_method_stereo)); if (capture_nonlocked_.capture_processing_format.sample_rate_hz() < formats_.api_format.output_stream().sample_rate_hz() && formats_.api_format.output_stream().sample_rate_hz() == 48000) { @@ -603,9 +587,7 @@ void AudioProcessingImpl::InitializeLocked() { formats_.api_format.output_stream().sample_rate_hz(), formats_.api_format.output_stream().num_channels(), formats_.api_format.output_stream().sample_rate_hz(), - formats_.api_format.output_stream().num_channels())); - SetDownmixMethod(*capture_.capture_fullband_audio, - config_.pipeline.capture_downmix_method); + downmixing_method, downmixing_method_stereo)); } else { capture_.capture_fullband_audio.reset(); } @@ -710,11 +692,12 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { config_.pipeline.maximum_internal_processing_rate != config.pipeline.maximum_internal_processing_rate || config_.pipeline.capture_downmix_method != - config.pipeline.capture_downmix_method; + config.pipeline.capture_downmix_method || + config_.pipeline.capture_downmix_method_stereo_aec != + config.pipeline.capture_downmix_method_stereo_aec; const bool aec_config_changed = - config_.echo_canceller.enabled != config.echo_canceller.enabled || - config_.echo_canceller.mobile_mode != config.echo_canceller.mobile_mode; + config_.echo_canceller.enabled != config.echo_canceller.enabled; const bool agc1_config_changed = config_.gain_controller1 != config.gain_controller1; @@ -1106,23 +1089,6 @@ void AudioProcessingImpl::HandleRenderRuntimeSettings() { void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) { RTC_DCHECK_GE(160, audio->num_frames_per_band()); - if (submodules_.echo_control_mobile) { - EchoControlMobileImpl::PackRenderAudioBuffer(audio, num_output_channels(), - num_reverse_channels(), - &aecm_render_queue_buffer_); - RTC_DCHECK(aecm_render_signal_queue_); - // Insert the samples into the queue. - if (!aecm_render_signal_queue_->Insert(&aecm_render_queue_buffer_)) { - // The data queue is full and needs to be emptied. - EmptyQueuedRenderAudio(); - - // Retry the insert (should always work). - bool result = - aecm_render_signal_queue_->Insert(&aecm_render_queue_buffer_); - RTC_DCHECK(result); - } - } - if (!submodules_.agc_manager && submodules_.gain_control) { GainControlImpl::PackRenderAudioBuffer(*audio, &agc_render_queue_buffer_); // Insert the samples into the queue. @@ -1211,14 +1177,6 @@ void AudioProcessingImpl::EmptyQueuedRenderAudio() { } void AudioProcessingImpl::EmptyQueuedRenderAudioLocked() { - if (submodules_.echo_control_mobile) { - RTC_DCHECK(aecm_render_signal_queue_); - while (aecm_render_signal_queue_->Remove(&aecm_capture_queue_buffer_)) { - submodules_.echo_control_mobile->ProcessRenderAudio( - aecm_capture_queue_buffer_); - } - } - if (submodules_.gain_control) { while (agc_render_signal_queue_->Remove(&agc_capture_queue_buffer_)) { submodules_.gain_control->ProcessRenderAudio(agc_capture_queue_buffer_); @@ -1274,12 +1232,6 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { HandleCaptureRuntimeSettings(); DenormalDisabler denormal_disabler; - // Ensure that not both the AEC and AECM are active at the same time. - // TODO(peah): Simplify once the public API Enable functions for these - // are moved to APM. - RTC_DCHECK_LE( - !!submodules_.echo_controller + !!submodules_.echo_control_mobile, 1); - data_dumper_->DumpRaw( "applied_input_volume", capture_.applied_input_volume.value_or(kUnspecifiedDataDumpInputVolume)); @@ -1306,7 +1258,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { *capture_buffer); } - capture_input_rms_.Analyze(ArrayView( + capture_input_rms_.Analyze(std::span( capture_buffer->channels_const()[0], capture_nonlocked_.capture_processing_format.num_frames())); const bool log_rms = ++capture_rms_interval_counter_ >= 1000; @@ -1393,25 +1345,11 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { } if ((!config_.noise_suppression.analyze_linear_aec_output_when_available || - !linear_aec_buffer || submodules_.echo_control_mobile) && + !linear_aec_buffer) && submodules_.noise_suppressor) { submodules_.noise_suppressor->Analyze(*capture_buffer); } - if (submodules_.echo_control_mobile) { - // Ensure that the stream delay was set before the call to the - // AECM ProcessCaptureAudio function. - if (!capture_.was_stream_delay_set) { - return AudioProcessing::kStreamParameterNotSetError; - } - - if (submodules_.noise_suppressor) { - submodules_.noise_suppressor->Process(capture_buffer); - } - - RETURN_ON_ERR(submodules_.echo_control_mobile->ProcessCaptureAudio( - capture_buffer, stream_delay_ms())); - } else { if (submodules_.echo_controller) { data_dumper_->DumpRaw("stream_delay", stream_delay_ms()); @@ -1431,13 +1369,12 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { if (submodules_.noise_suppressor) { submodules_.noise_suppressor->Process(capture_buffer); } - } if (submodules_.agc_manager) { submodules_.agc_manager->Process(*capture_buffer); std::optional new_digital_gain = - submodules_.agc_manager->GetDigitalComressionGain(); + submodules_.agc_manager->GetDigitalCompressionGain(); if (new_digital_gain && submodules_.gain_control) { submodules_.gain_control->set_compression_gain_db(*new_digital_gain); } @@ -1468,7 +1405,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { } if (submodules_.echo_detector) { - submodules_.echo_detector->AnalyzeCaptureAudio(ArrayView( + submodules_.echo_detector->AnalyzeCaptureAudio(std::span( capture_buffer->channels()[0], capture_buffer->num_frames())); } @@ -1492,7 +1429,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { submodules_.capture_post_processor->Process(capture_buffer); } - capture_output_rms_.Analyze(ArrayView( + capture_output_rms_.Analyze(std::span( capture_buffer->channels_const()[0], capture_nonlocked_.capture_processing_format.num_frames())); if (log_rms) { @@ -1552,7 +1489,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { if (!capture_.capture_output_used_last_frame && capture_.capture_output_used) { for (size_t ch = 0; ch < capture_buffer->num_channels(); ++ch) { - ArrayView channel_view(capture_buffer->channels()[ch], + std::span channel_view(capture_buffer->channels()[ch], capture_buffer->num_frames()); std::fill(channel_view.begin(), channel_view.end(), 0.f); } @@ -1715,7 +1652,7 @@ int AudioProcessingImpl::set_stream_delay_ms(int delay) { } bool AudioProcessingImpl::GetLinearAecOutput( - ArrayView> linear_output) const { + std::span> linear_output) const { MutexLock lock(&mutex_capture_); AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get(); @@ -1726,8 +1663,8 @@ bool AudioProcessingImpl::GetLinearAecOutput( for (size_t ch = 0; ch < linear_aec_buffer->num_channels(); ++ch) { RTC_DCHECK_EQ(linear_output[ch].size(), linear_aec_buffer->num_frames()); - ArrayView channel_view = - ArrayView(linear_aec_buffer->channels_const()[ch], + std::span channel_view = + std::span(linear_aec_buffer->channels_const()[ch], linear_aec_buffer->num_frames()); FloatS16ToFloat(channel_view.data(), channel_view.size(), linear_output[ch].data()); @@ -1881,9 +1818,8 @@ AudioProcessing::Config AudioProcessingImpl::GetConfig() const { bool AudioProcessingImpl::UpdateActiveSubmoduleStates() { return submodule_states_.Update( - config_.high_pass_filter.enabled, !!submodules_.echo_control_mobile, - !!submodules_.noise_suppressor, !!submodules_.gain_control, - !!submodules_.gain_controller2, + config_.high_pass_filter.enabled, !!submodules_.noise_suppressor, + !!submodules_.gain_control, !!submodules_.gain_controller2, config_.pre_amplifier.enabled || config_.capture_level_adjustment.enabled, NeedEchoController(config_, !!echo_control_factory_)); } @@ -1891,8 +1827,7 @@ bool AudioProcessingImpl::UpdateActiveSubmoduleStates() { void AudioProcessingImpl::InitializeHighPassFilter(bool forced_reset) { bool high_pass_filter_needed_by_aec = config_.echo_canceller.enabled && - config_.echo_canceller.enforce_high_pass_filtering && - !config_.echo_canceller.mobile_mode; + config_.echo_canceller.enforce_high_pass_filtering; if (submodule_states_.HighPassFilteringRequired() || high_pass_filter_needed_by_aec) { bool use_full_band = config_.high_pass_filter.apply_in_full_band && @@ -1918,8 +1853,6 @@ void AudioProcessingImpl::InitializeEchoController() { submodules_.echo_controller.reset(); capture_.linear_aec_output.reset(); submodules_.post_filter.reset(); - submodules_.echo_control_mobile.reset(); - aecm_render_signal_queue_.reset(); bool use_echo_controller = NeedEchoController(config_, !!echo_control_factory_); @@ -1968,33 +1901,6 @@ void AudioProcessingImpl::InitializeEchoController() { return; } - - if (!(config_.echo_canceller.enabled && config_.echo_canceller.mobile_mode)) { - return; - } - - // Create and activate AECM. - size_t max_element_size = - std::max(static_cast(1), - kMaxAllowedValuesOfSamplesPerBand * - EchoControlMobileImpl::NumCancellersRequired( - num_output_channels(), num_reverse_channels())); - - std::vector template_queue_element(max_element_size); - - aecm_render_signal_queue_.reset( - new SwapQueue, RenderQueueItemVerifier>( - kMaxNumFramesToBuffer, template_queue_element, - RenderQueueItemVerifier(max_element_size))); - - aecm_render_queue_buffer_.resize(max_element_size); - aecm_capture_queue_buffer_.resize(max_element_size); - - submodules_.echo_control_mobile.reset(new EchoControlMobileImpl()); - - submodules_.echo_control_mobile->Initialize(proc_split_sample_rate_hz(), - num_reverse_channels(), - num_output_channels()); } void AudioProcessingImpl::InitializeGainController1() { @@ -2198,15 +2104,6 @@ void AudioProcessingImpl::WriteAecDumpConfigMessage(bool forced) { apm_config.aec_extended_filter_enabled = false; apm_config.aec_suppression_level = 0; - apm_config.aecm_enabled = !!submodules_.echo_control_mobile; - apm_config.aecm_comfort_noise_enabled = - submodules_.echo_control_mobile && - submodules_.echo_control_mobile->is_comfort_noise_enabled(); - apm_config.aecm_routing_mode = - submodules_.echo_control_mobile - ? static_cast(submodules_.echo_control_mobile->routing_mode()) - : 0; - apm_config.agc_enabled = !!submodules_.gain_control; apm_config.agc_mode = submodules_.gain_control @@ -2227,6 +2124,8 @@ void AudioProcessingImpl::WriteAecDumpConfigMessage(bool forced) { apm_config.pre_amplifier_fixed_gain_factor = config_.pre_amplifier.fixed_gain_factor; + apm_config.api_config_string = config_.ToString(); + if (!forced && apm_config == apm_config_for_aec_dump_) { return; } diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h index 68df7fc65cc..a39dc133251 100644 --- a/modules/audio_processing/audio_processing_impl.h +++ b/modules/audio_processing/audio_processing_impl.h @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include "absl/base/nullability.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/audio/audio_processing_statistics.h" #include "api/audio/echo_canceller3_config.h" @@ -35,7 +35,6 @@ #include "modules/audio_processing/agc2/input_volume_stats_reporter.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/capture_levels_adjuster/capture_levels_adjuster.h" -#include "modules/audio_processing/echo_control_mobile_impl.h" #include "modules/audio_processing/gain_control_impl.h" #include "modules/audio_processing/gain_controller2.h" #include "modules/audio_processing/high_pass_filter.h" @@ -105,7 +104,7 @@ class AudioProcessingImpl : public AudioProcessing { const StreamConfig& output_config, float* const* dest) override; bool GetLinearAecOutput( - ArrayView> linear_output) const override; + std::span> linear_output) const override; void set_output_will_be_muted(bool muted) override; bool get_output_will_be_muted() override; void HandleCaptureOutputUsedSetting(bool capture_output_used) @@ -206,7 +205,6 @@ class AudioProcessingImpl : public AudioProcessing { bool capture_analyzer_enabled); // Updates the submodule state and returns true if it has changed. bool Update(bool high_pass_filter_enabled, - bool mobile_echo_controller_enabled, bool noise_suppressor_enabled, bool adaptive_gain_controller_enabled, bool gain_controller2_enabled, @@ -398,7 +396,6 @@ class AudioProcessingImpl : public AudioProcessing { std::unique_ptr gain_controller2; std::unique_ptr high_pass_filter; std::unique_ptr echo_controller; - std::unique_ptr echo_control_mobile; std::unique_ptr noise_suppressor; std::unique_ptr post_filter; std::unique_ptr capture_levels_adjuster; @@ -509,10 +506,6 @@ class AudioProcessingImpl : public AudioProcessing { SwapQueue stats_message_queue_; } stats_reporter_; - std::vector aecm_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); - std::vector aecm_capture_queue_buffer_ - RTC_GUARDED_BY(mutex_capture_); - size_t agc_render_queue_element_max_size_ RTC_GUARDED_BY(mutex_render_) RTC_GUARDED_BY(mutex_capture_) = 0; std::vector agc_render_queue_buffer_ RTC_GUARDED_BY(mutex_render_); @@ -533,9 +526,6 @@ class AudioProcessingImpl : public AudioProcessing { RTC_GUARDED_BY(mutex_capture_); // Lock protection not needed. - std::unique_ptr< - SwapQueue, RenderQueueItemVerifier>> - aecm_render_signal_queue_; std::unique_ptr< SwapQueue, RenderQueueItemVerifier>> agc_render_signal_queue_; diff --git a/modules/audio_processing/audio_processing_impl_locking_unittest.cc b/modules/audio_processing/audio_processing_impl_locking_unittest.cc index f9ca3075fbc..3bd808d2104 100644 --- a/modules/audio_processing/audio_processing_impl_locking_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_locking_unittest.cc @@ -11,9 +11,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/audio/builtin_audio_processing_builder.h" #include "api/environment/environment_factory.h" @@ -59,7 +59,6 @@ enum class AecType { AecTurnedOff, BasicWebRtcAecSettingsWithExtentedFilter, BasicWebRtcAecSettingsWithDelayAgnosticAec, - BasicWebRtcAecSettingsWithAecMobile }; // Thread-safe random number generator wrapper. @@ -122,8 +121,7 @@ struct TestConfig { // Test case generator for the test configurations to use in the brief tests. static std::vector GenerateBriefTestConfigs() { std::vector test_configs; - AecType aec_types[] = {AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec, - AecType::BasicWebRtcAecSettingsWithAecMobile}; + AecType aec_types[] = {AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec}; for (auto aec_type : aec_types) { TestConfig test_config; test_config.aec_type = aec_type; @@ -188,10 +186,11 @@ struct TestConfig { auto add_aec_settings = [](const std::vector& in) { std::vector out; AecType aec_types[] = { - AecType::BasicWebRtcAecSettings, AecType::AecTurnedOff, + AecType::BasicWebRtcAecSettings, + AecType::AecTurnedOff, AecType::BasicWebRtcAecSettingsWithExtentedFilter, AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec, - AecType::BasicWebRtcAecSettingsWithAecMobile}; + }; for (auto test_config : in) { // Due to a VisualStudio 2015 compiler issue, the internal loop // variable here cannot override a previously defined name. @@ -227,11 +226,7 @@ struct TestConfig { std::vector out; for (auto test_config : in) { - auto available_rates = - (test_config.aec_type == - AecType::BasicWebRtcAecSettingsWithAecMobile - ? ArrayView(sample_rates, 2) - : ArrayView(sample_rates)); + auto available_rates = std::span(sample_rates); for (auto rate : available_rates) { test_config.initial_sample_rate_hz = rate; @@ -465,7 +460,7 @@ void PopulateAudioFrame(float** frame, void PopulateAudioFrame(float amplitude, size_t num_channels, size_t samples_per_channel, - ArrayView frame, + std::span frame, RandomGenerator* rand_gen) { ASSERT_GT(amplitude, 0); ASSERT_LE(amplitude, 32767); @@ -481,8 +476,6 @@ void PopulateAudioFrame(float amplitude, AudioProcessing::Config GetApmTestConfig(AecType aec_type) { AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = aec_type != AecType::AecTurnedOff; - apm_config.echo_canceller.mobile_mode = - aec_type == AecType::BasicWebRtcAecSettingsWithAecMobile; apm_config.gain_controller1.enabled = true; apm_config.gain_controller1.mode = AudioProcessing::Config::GainController1::kAdaptiveDigital; @@ -545,9 +538,6 @@ void StatsProcessor::Process() { AudioProcessing::Config apm_config = apm_->GetConfig(); if (test_config_->aec_type != AecType::AecTurnedOff) { EXPECT_TRUE(apm_config.echo_canceller.enabled); - EXPECT_EQ(apm_config.echo_canceller.mobile_mode, - (test_config_->aec_type == - AecType::BasicWebRtcAecSettingsWithAecMobile)); } else { EXPECT_FALSE(apm_config.echo_canceller.enabled); } @@ -837,10 +827,8 @@ void RenderProcessor::Process() { void RenderProcessor::PrepareFrame() { // Restrict to a common fixed sample rate if the integer interface is // used. - if ((test_config_->render_api_function == - RenderApiImpl::ProcessReverseStreamImplInteger) || - (test_config_->aec_type != - AecType::BasicWebRtcAecSettingsWithAecMobile)) { + if (test_config_->render_api_function == + RenderApiImpl::ProcessReverseStreamImplInteger) { frame_data_.input_sample_rate_hz = test_config_->initial_sample_rate_hz; frame_data_.output_sample_rate_hz = test_config_->initial_sample_rate_hz; } diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc index 27a41344d41..2bbf10b7175 100644 --- a/modules/audio_processing/audio_processing_impl_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_unittest.cc @@ -15,12 +15,12 @@ #include #include #include +#include #include #include #include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/audio/builtin_audio_processing_builder.h" #include "api/audio/echo_control.h" @@ -90,12 +90,12 @@ class TestEchoDetector : public EchoDetector { : analyze_render_audio_called_(false), last_render_audio_first_sample_(0.f) {} ~TestEchoDetector() override = default; - void AnalyzeRenderAudio(ArrayView render_audio) override { + void AnalyzeRenderAudio(std::span render_audio) override { last_render_audio_first_sample_ = render_audio[0]; analyze_render_audio_called_ = true; } void AnalyzeCaptureAudio( - ArrayView /* capture_audio */) override {} + std::span /* capture_audio */) override {} void Initialize(int /* capture_sample_rate_hz */, int /* num_capture_channels */, int /* render_sample_rate_hz */, @@ -125,7 +125,7 @@ class TestRenderPreProcessor : public CustomProcessing { void Initialize(int /* sample_rate_hz */, int /* num_channels */) override {} void Process(AudioBuffer* audio) override { for (size_t k = 0; k < audio->num_channels(); ++k) { - ArrayView channel_view(audio->channels()[k], audio->num_frames()); + std::span channel_view(audio->channels()[k], audio->num_frames()); std::transform(channel_view.begin(), channel_view.end(), channel_view.begin(), ProcessSample); } diff --git a/modules/audio_processing/audio_processing_performance_unittest.cc b/modules/audio_processing/audio_processing_performance_unittest.cc index e4b99c324dd..267d1a7a6fd 100644 --- a/modules/audio_processing/audio_processing_performance_unittest.cc +++ b/modules/audio_processing/audio_processing_performance_unittest.cc @@ -48,7 +48,6 @@ enum class ProcessorType { kRender, kCapture }; // Variant of APM processing settings to use in the test. enum class SettingsType { kDefaultApmDesktop, - kDefaultApmMobile, kAllSubmodulesTurnedOff, kDefaultApmDesktopWithoutDelayAgnostic, kDefaultApmDesktopWithoutExtendedFilter @@ -99,26 +98,12 @@ struct SimulationConfig { } } #endif - - const SettingsType mobile_settings[] = {SettingsType::kDefaultApmMobile}; - - const int mobile_sample_rates[] = {8000, 16000}; - - for (auto sample_rate : mobile_sample_rates) { - for (auto settings : mobile_settings) { - simulation_configs.push_back(SimulationConfig(sample_rate, settings)); - } - } - return simulation_configs; } std::string SettingsDescription() const { std::string description; switch (simulation_settings) { - case SettingsType::kDefaultApmMobile: - description = "DefaultApmMobile"; - break; case SettingsType::kDefaultApmDesktop: description = "DefaultApmDesktop"; break; @@ -420,7 +405,6 @@ class CallSimulator : public ::testing::TestWithParam { auto set_default_desktop_apm_runtime_settings = [](AudioProcessing* apm) { AudioProcessing::Config apm_config = apm->GetConfig(); apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.mobile_mode = false; apm_config.noise_suppression.enabled = true; apm_config.gain_controller1.enabled = true; apm_config.gain_controller1.mode = @@ -428,17 +412,6 @@ class CallSimulator : public ::testing::TestWithParam { apm->ApplyConfig(apm_config); }; - // Lambda function for setting the default APM runtime settings for mobile. - auto set_default_mobile_apm_runtime_settings = [](AudioProcessing* apm) { - AudioProcessing::Config apm_config = apm->GetConfig(); - apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.mobile_mode = true; - apm_config.noise_suppression.enabled = true; - apm_config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveDigital; - apm->ApplyConfig(apm_config); - }; - // Lambda function for turning off all of the APM runtime settings // submodules. auto turn_off_default_apm_runtime_settings = [](AudioProcessing* apm) { @@ -451,12 +424,6 @@ class CallSimulator : public ::testing::TestWithParam { int num_capture_channels = 1; switch (simulation_config_.simulation_settings) { - case SettingsType::kDefaultApmMobile: { - apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); - ASSERT_TRUE(!!apm_); - set_default_mobile_apm_runtime_settings(apm_.get()); - break; - } case SettingsType::kDefaultApmDesktop: { apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); ASSERT_TRUE(!!apm_); @@ -558,6 +525,8 @@ const float CallSimulator::kRenderInputFloatLevel = 0.5f; const float CallSimulator::kCaptureInputFloatLevel = 0.03125f; } // anonymous namespace +#ifndef WEBRTC_ANDROID + TEST_P(CallSimulator, ApiCallDurationTest) { // Run test and verify that it did not time out. EXPECT_TRUE(Run()); @@ -567,5 +536,6 @@ INSTANTIATE_TEST_SUITE_P( AudioProcessingPerformanceTest, CallSimulator, ::testing::ValuesIn(SimulationConfig::GenerateSimulationConfigs())); +#endif } // namespace webrtc diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc index 496d61b54a8..e77357dfa7e 100644 --- a/modules/audio_processing/audio_processing_unittest.cc +++ b/modules/audio_processing/audio_processing_unittest.cc @@ -17,13 +17,11 @@ #include #include #include -#include -#include #include #include #include -#include #include +#include #include #include #include @@ -31,7 +29,6 @@ #include "absl/flags/flag.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_processing_statistics.h" #include "api/audio/audio_view.h" #include "api/audio/builtin_audio_processing_builder.h" @@ -44,8 +41,6 @@ #include "api/scoped_refptr.h" #include "common_audio/channel_buffer.h" #include "common_audio/include/audio_util.h" -#include "common_audio/resampler/include/push_resampler.h" -#include "common_audio/resampler/push_sinc_resampler.h" #include "modules/audio_processing/aec_dump/aec_dump_factory.h" #include "modules/audio_processing/include/mock_audio_processing.h" #include "modules/audio_processing/test/protobuf_utils.h" @@ -132,19 +127,10 @@ void VerifyChannelsAreEqual(const int16_t* stereo, size_t samples_per_channel) { void EnableAllAPComponents(AudioProcessing* ap) { AudioProcessing::Config apm_config = ap->GetConfig(); apm_config.echo_canceller.enabled = true; -#if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) - apm_config.echo_canceller.mobile_mode = true; - - apm_config.gain_controller1.enabled = true; - apm_config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveDigital; -#elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) - apm_config.echo_canceller.mobile_mode = false; apm_config.gain_controller1.enabled = true; apm_config.gain_controller1.mode = AudioProcessing::Config::GainController1::kAdaptiveAnalog; -#endif apm_config.noise_suppression.enabled = true; @@ -238,19 +224,6 @@ void ClearTempFiles() { remove(kv.second.c_str()); } -// Only remove "out" files. Keep "ref" files. -void ClearTempOutFiles() { - for (auto it = temp_filenames.begin(); it != temp_filenames.end();) { - const std::string& filename = it->first; - if (filename.substr(0, 3).compare("out") == 0) { - remove(it->second.c_str()); - temp_filenames.erase(it++); - } else { - it++; - } - } -} - void OpenFileAndReadMessage(absl::string_view filename, MessageLite* msg) { FILE* file = fopen(std::string(filename).c_str(), "rb"); ASSERT_TRUE(file != nullptr); @@ -292,14 +265,10 @@ bool ReadChunk(FILE* file, // Returns the reference file name that matches the current CPU // architecture/optimizations. std::string GetReferenceFilename() { -#if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) - return test::ResourcePath("audio_processing/output_data_fixed", "pb"); -#elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) if (cpu_info::Supports(cpu_info::ISA::kAVX2)) { return test::ResourcePath("audio_processing/output_data_float_avx2", "pb"); } return test::ResourcePath("audio_processing/output_data_float", "pb"); -#endif } // Flag that can temporarily be enabled for local debugging to inspect @@ -347,8 +316,8 @@ void ExpectEventFieldsEq(const audioproc::Event& actual, // and contain the same data. If they differ and `kDumpWhenExpectMessageEqFails` // is true, checks the equality of a subset of `audioproc::Event` (nested) // fields. -bool ExpectMessageEq(ArrayView actual, - ArrayView expected) { +bool ExpectMessageEq(std::span actual, + std::span expected) { EXPECT_EQ(actual.size(), expected.size()); if (actual.size() != expected.size()) { return false; @@ -719,7 +688,6 @@ void ApmTest::StreamParametersTest(Format format) { // Other stream parameters set correctly. apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.mobile_mode = false; apm_->ApplyConfig(apm_config); EXPECT_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(100)); EXPECT_EQ(apm_->kStreamParameterNotSetError, ProcessStreamChooser(format)); @@ -871,7 +839,7 @@ TEST_F(ApmTest, PreAmplifier) { tmp_frame.CopyFrom(frame_); auto compute_power = [](const Int16FrameData& frame) { - ArrayView data = frame.view().data(); + std::span data = frame.view().data(); return std::accumulate(data.begin(), data.end(), 0.0f, [](float a, float b) { return a + b * b; }) / data.size() / 32768 / 32768; @@ -974,7 +942,7 @@ TEST_F(ApmTest, CaptureLevelAdjustment) { tmp_frame.CopyFrom(frame_); auto compute_power = [](const Int16FrameData& frame) { - ArrayView data = frame.view().data(); + std::span data = frame.view().data(); return std::accumulate(data.begin(), data.end(), 0.0f, [](float a, float b) { return a + b * b; }) / data.size() / 32768 / 32768; @@ -1409,7 +1377,6 @@ TEST_F(ApmTest, SplittingFilter) { // Check the test is valid. We should have distortion from the filter // when AEC is enabled (which won't affect the audio). apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.mobile_mode = false; apm_->ApplyConfig(apm_config); frame_.SetProperties(/* samples_per_channel=*/320, /* num_channels=*/2); frame_.FillData(1000); @@ -1703,7 +1670,6 @@ TEST_F(ApmTest, Process) { } } } -#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) // To test the extended filter mode. audioproc::Test* test = ref_data.add_test(); test->set_num_reverse_channels(2); @@ -1711,7 +1677,6 @@ TEST_F(ApmTest, Process) { test->set_num_output_channels(2); test->set_sample_rate(AudioProcessing::kSampleRate32kHz); test->set_use_aec_extended_filter(true); -#endif } for (int i = 0; i < ref_data.test_size(); i++) { @@ -1741,9 +1706,7 @@ TEST_F(ApmTest, Process) { int analog_level = 127; int analog_level_average = 0; int max_output_average = 0; -#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) int stats_index = 0; -#endif while (ReadFrame(far_file_, &revframe_) && ReadFrame(near_file_, &frame_)) { EXPECT_EQ( @@ -1782,7 +1745,6 @@ TEST_F(ApmTest, Process) { frame_.set_num_channels(static_cast(test->num_input_channels())); frame_count++; -#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) const int kStatsAggregationFrameNum = 100; // 1 second. if (frame_count % kStatsAggregationFrameNum == 0) { // Get echo and delay metrics. @@ -1820,7 +1782,6 @@ TEST_F(ApmTest, Process) { residual_echo_likelihood_recent_max); } } -#endif // defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE). } max_output_average /= frame_count; analog_level_average /= frame_count; @@ -1857,60 +1818,8 @@ TEST_F(ApmTest, Process) { } } -// Compares the reference and test arrays over a region around the expected -// delay. Finds the highest SNR in that region and adds the variance and squared -// error results to the supplied accumulators. -void UpdateBestSNR(const float* ref, - const float* test, - size_t length, - int expected_delay, - double* variance_acc, - double* sq_error_acc) { - RTC_CHECK_LT(expected_delay, length) - << "delay greater than signal length, cannot compute SNR"; - double best_snr = std::numeric_limits::min(); - double best_variance = 0; - double best_sq_error = 0; - // Search over a region of nine samples around the expected delay. - for (int delay = std::max(expected_delay - 4, 0); delay <= expected_delay + 4; - ++delay) { - double sq_error = 0; - double variance = 0; - for (size_t i = 0; i < length - delay; ++i) { - double error = test[i + delay] - ref[i]; - sq_error += error * error; - variance += ref[i] * ref[i]; - } - - if (sq_error == 0) { - *variance_acc += variance; - return; - } - double snr = variance / sq_error; - if (snr > best_snr) { - best_snr = snr; - best_variance = variance; - best_sq_error = sq_error; - } - } - - *variance_acc += best_variance; - *sq_error_acc += best_sq_error; -} - -// Used to test a multitude of sample rate and channel combinations. It works -// by first producing a set of reference files (in SetUpTestCase) that are -// assumed to be correct, as the used parameters are verified by other tests -// in this collection. Primarily the reference files are all produced at -// "native" rates which do not involve any resampling. - -// Each test pass produces an output file with a particular format. The output -// is matched against the reference file closest to its internal processing -// format. If necessary the output is resampled back to its process format. -// Due to the resampling distortion, we don't expect identical results, but -// enforce SNR thresholds which vary depending on the format. 0 is a special -// case SNR which corresponds to inf, or zero error. -typedef std::tuple AudioProcessingTestData; +// Used to test a multitude of sample rate and channel combinations. +typedef std::tuple AudioProcessingTestData; class AudioProcessingTest : public ::testing::TestWithParam { public: @@ -1918,37 +1827,11 @@ class AudioProcessingTest : input_rate_(std::get<0>(GetParam())), output_rate_(std::get<1>(GetParam())), reverse_input_rate_(std::get<2>(GetParam())), - reverse_output_rate_(std::get<3>(GetParam())), - expected_snr_(std::get<4>(GetParam())), - expected_reverse_snr_(std::get<5>(GetParam())) {} + reverse_output_rate_(std::get<3>(GetParam())) {} ~AudioProcessingTest() override {} - static void SetUpTestSuite() { - // Create all needed output reference files. - const size_t kNumChannels[] = {1, 2}; - for (int sample_rate_hz : kProcessSampleRates) { - for (int num_channels : kNumChannels) { - for (int num_reverse_channels : kNumChannels) { - // The reference files always have matching input and output channels. - ProcessFormat(sample_rate_hz, sample_rate_hz, sample_rate_hz, - sample_rate_hz, num_channels, num_channels, - num_reverse_channels, num_reverse_channels, "ref"); - } - } - } - } - - void TearDown() override { - // Remove "out" files after each test. - ClearTempOutFiles(); - } - - static void TearDownTestSuite() { ClearTempFiles(); } - - // Runs a process pass on files with the given parameters and dumps the output - // to a file specified with `output_file_prefix`. Both forward and reverse - // output streams are dumped. + // Runs a process pass on files with the given parameters. static void ProcessFormat(int input_rate, int output_rate, int reverse_input_rate, @@ -1956,8 +1839,7 @@ class AudioProcessingTest size_t num_input_channels, size_t num_output_channels, size_t num_reverse_input_channels, - size_t num_reverse_output_channels, - absl::string_view output_file_prefix) { + size_t num_reverse_output_channels) { AudioProcessing::Config apm_config; apm_config.gain_controller1.analog_gain_controller.enabled = false; scoped_refptr ap = BuiltinAudioProcessingBuilder() @@ -1976,24 +1858,8 @@ class AudioProcessingTest FILE* far_file = fopen(ResourceFilePath("far", reverse_input_rate).c_str(), "rb"); FILE* near_file = fopen(ResourceFilePath("near", input_rate).c_str(), "rb"); - FILE* out_file = fopen( - OutputFilePath( - output_file_prefix, input_rate, output_rate, reverse_input_rate, - reverse_output_rate, num_input_channels, num_output_channels, - num_reverse_input_channels, num_reverse_output_channels, kForward) - .c_str(), - "wb"); - FILE* rev_out_file = fopen( - OutputFilePath( - output_file_prefix, input_rate, output_rate, reverse_input_rate, - reverse_output_rate, num_input_channels, num_output_channels, - num_reverse_input_channels, num_reverse_output_channels, kReverse) - .c_str(), - "wb"); ASSERT_TRUE(far_file != nullptr); ASSERT_TRUE(near_file != nullptr); - ASSERT_TRUE(out_file != nullptr); - ASSERT_TRUE(rev_out_file != nullptr); ChannelBuffer fwd_cb(AudioProcessing::GetFrameSize(input_rate), num_input_channels); @@ -2026,39 +1892,9 @@ class AudioProcessingTest EXPECT_NOERR(ap->ProcessStream( fwd_cb.channels(), StreamConfig(input_rate, num_input_channels), StreamConfig(output_rate, num_output_channels), out_cb.channels())); - - // Dump forward output to file. - RTC_DCHECK_EQ(out_cb.num_bands(), 1u); // Assumes full frequency band. - DeinterleavedView deinterleaved_src( - out_cb.channels(), out_cb.num_frames(), out_cb.num_channels()); - InterleavedView interleaved_dst( - float_data.get(), out_cb.num_frames(), out_cb.num_channels()); - Interleave(deinterleaved_src, interleaved_dst); - size_t out_length = out_cb.num_channels() * out_cb.num_frames(); - - ASSERT_EQ(out_length, fwrite(float_data.get(), sizeof(float_data[0]), - out_length, out_file)); - - // Dump reverse output to file. - RTC_DCHECK_EQ(rev_out_cb.num_bands(), 1u); - deinterleaved_src = DeinterleavedView( - rev_out_cb.channels(), rev_out_cb.num_frames(), - rev_out_cb.num_channels()); - interleaved_dst = InterleavedView( - float_data.get(), rev_out_cb.num_frames(), rev_out_cb.num_channels()); - Interleave(deinterleaved_src, interleaved_dst); - size_t rev_out_length = - rev_out_cb.num_channels() * rev_out_cb.num_frames(); - - ASSERT_EQ(rev_out_length, fwrite(float_data.get(), sizeof(float_data[0]), - rev_out_length, rev_out_file)); - - analog_level = ap->recommended_stream_analog_level(); } fclose(far_file); fclose(near_file); - fclose(out_file); - fclose(rev_out_file); } protected: @@ -2066,8 +1902,6 @@ class AudioProcessingTest int output_rate_; int reverse_input_rate_; int reverse_output_rate_; - double expected_snr_; - double expected_reverse_snr_; }; TEST_P(AudioProcessingTest, Formats) { @@ -2108,184 +1942,66 @@ TEST_P(AudioProcessingTest, Formats) { cf) { ProcessFormat(input_rate_, output_rate_, reverse_input_rate_, reverse_output_rate_, num_input, num_output, - num_reverse_input, num_reverse_output, "out"); - - // Verify output for both directions. - std::vector stream_directions; - stream_directions.push_back(kForward); - stream_directions.push_back(kReverse); - for (StreamDirection file_direction : stream_directions) { - const int in_rate = file_direction ? reverse_input_rate_ : input_rate_; - const int out_rate = file_direction ? reverse_output_rate_ : output_rate_; - const int out_num = file_direction ? num_reverse_output : num_output; - const double expected_snr = - file_direction ? expected_reverse_snr_ : expected_snr_; - - const int min_ref_rate = std::min(in_rate, out_rate); - int ref_rate; - if (min_ref_rate > 32000) { - ref_rate = 48000; - } else if (min_ref_rate > 16000) { - ref_rate = 32000; - } else { - ref_rate = 16000; - } - - FILE* out_file = fopen( - OutputFilePath("out", input_rate_, output_rate_, reverse_input_rate_, - reverse_output_rate_, num_input, num_output, - num_reverse_input, num_reverse_output, file_direction) - .c_str(), - "rb"); - // The reference files always have matching input and output channels. - FILE* ref_file = - fopen(OutputFilePath("ref", ref_rate, ref_rate, ref_rate, ref_rate, - num_output, num_output, num_reverse_output, - num_reverse_output, file_direction) - .c_str(), - "rb"); - ASSERT_TRUE(out_file != nullptr); - ASSERT_TRUE(ref_file != nullptr); - - const size_t ref_samples_per_channel = - AudioProcessing::GetFrameSize(ref_rate); - const size_t ref_length = ref_samples_per_channel * out_num; - const size_t out_samples_per_channel = - AudioProcessing::GetFrameSize(out_rate); - const size_t out_length = out_samples_per_channel * out_num; - // Data from the reference file. - std::unique_ptr ref_data(new float[ref_length]); - // Data from the output file. - std::unique_ptr out_data(new float[out_length]); - // Data from the resampled output, in case the reference and output rates - // don't match. - std::unique_ptr cmp_data(new float[ref_length]); - - PushResampler resampler(out_samples_per_channel, - ref_samples_per_channel, out_num); - - // Compute the resampling delay of the output relative to the reference, - // to find the region over which we should search for the best SNR. - float expected_delay_sec = 0; - if (in_rate != ref_rate) { - // Input resampling delay. - expected_delay_sec += - PushSincResampler::AlgorithmicDelaySeconds(in_rate); - } - if (out_rate != ref_rate) { - // Output resampling delay. - expected_delay_sec += - PushSincResampler::AlgorithmicDelaySeconds(ref_rate); - // Delay of converting the output back to its processing rate for - // testing. - expected_delay_sec += - PushSincResampler::AlgorithmicDelaySeconds(out_rate); - } - // The delay is multiplied by the number of channels because - // UpdateBestSNR() computes the SNR over interleaved data without taking - // channels into account. - int expected_delay = - std::floor(expected_delay_sec * ref_rate + 0.5f) * out_num; - - double variance = 0; - double sq_error = 0; - while (fread(out_data.get(), sizeof(out_data[0]), out_length, out_file) && - fread(ref_data.get(), sizeof(ref_data[0]), ref_length, ref_file)) { - float* out_ptr = out_data.get(); - if (out_rate != ref_rate) { - // Resample the output back to its internal processing rate if - // necessary. - InterleavedView src(out_ptr, out_samples_per_channel, - out_num); - InterleavedView dst(cmp_data.get(), ref_samples_per_channel, - out_num); - resampler.Resample(src, dst); - out_ptr = cmp_data.get(); - } - - // Update the `sq_error` and `variance` accumulators with the highest - // SNR of reference vs output. - UpdateBestSNR(ref_data.get(), out_ptr, ref_length, expected_delay, - &variance, &sq_error); - } - - std::cout << "(" << input_rate_ << ", " << output_rate_ << ", " - << reverse_input_rate_ << ", " << reverse_output_rate_ << ", " - << num_input << ", " << num_output << ", " << num_reverse_input - << ", " << num_reverse_output << ", " << file_direction - << "): "; - if (sq_error > 0) { - double snr = 10 * log10(variance / sq_error); - EXPECT_GE(snr, expected_snr); - EXPECT_NE(0, expected_snr); - std::cout << "SNR=" << snr << " dB" << std::endl; - } else { - std::cout << "SNR=inf dB" << std::endl; - } - - fclose(out_file); - fclose(ref_file); - } + num_reverse_input, num_reverse_output); } } -#if defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) INSTANTIATE_TEST_SUITE_P( CommonFormats, AudioProcessingTest, // Internal processing rates and the particularly common sample rate 44100 // Hz are tested in a grid of combinations (capture in, render in, out). - ::testing::Values(std::make_tuple(48000, 48000, 48000, 48000, 0, 0), - std::make_tuple(48000, 48000, 32000, 48000, 40, 30), - std::make_tuple(48000, 48000, 16000, 48000, 40, 20), - std::make_tuple(48000, 44100, 48000, 44100, 20, 20), - std::make_tuple(48000, 44100, 32000, 44100, 20, 15), - std::make_tuple(48000, 44100, 16000, 44100, 20, 15), - std::make_tuple(48000, 32000, 48000, 32000, 30, 35), - std::make_tuple(48000, 32000, 32000, 32000, 30, 0), - std::make_tuple(48000, 32000, 16000, 32000, 30, 20), - std::make_tuple(48000, 16000, 48000, 16000, 25, 20), - std::make_tuple(48000, 16000, 32000, 16000, 25, 20), - std::make_tuple(48000, 16000, 16000, 16000, 25, 0), - - std::make_tuple(44100, 48000, 48000, 48000, 30, 0), - std::make_tuple(44100, 48000, 32000, 48000, 30, 30), - std::make_tuple(44100, 48000, 16000, 48000, 30, 20), - std::make_tuple(44100, 44100, 48000, 44100, 20, 20), - std::make_tuple(44100, 44100, 32000, 44100, 20, 15), - std::make_tuple(44100, 44100, 16000, 44100, 20, 15), - std::make_tuple(44100, 32000, 48000, 32000, 30, 35), - std::make_tuple(44100, 32000, 32000, 32000, 30, 0), - std::make_tuple(44100, 32000, 16000, 32000, 30, 20), - std::make_tuple(44100, 16000, 48000, 16000, 25, 20), - std::make_tuple(44100, 16000, 32000, 16000, 25, 20), - std::make_tuple(44100, 16000, 16000, 16000, 25, 0), - - std::make_tuple(32000, 48000, 48000, 48000, 5, 0), - std::make_tuple(32000, 48000, 32000, 48000, 5, 30), - std::make_tuple(32000, 48000, 16000, 48000, 5, 20), - std::make_tuple(32000, 44100, 48000, 44100, 19, 20), - std::make_tuple(32000, 44100, 32000, 44100, 19, 15), - std::make_tuple(32000, 44100, 16000, 44100, 19, 15), - std::make_tuple(32000, 32000, 48000, 32000, 40, 35), - std::make_tuple(32000, 32000, 32000, 32000, 0, 0), - std::make_tuple(32000, 32000, 16000, 32000, 39, 20), - std::make_tuple(32000, 16000, 48000, 16000, 25, 20), - std::make_tuple(32000, 16000, 32000, 16000, 25, 20), - std::make_tuple(32000, 16000, 16000, 16000, 25, 0), - - std::make_tuple(16000, 48000, 48000, 48000, 1.7, 0), - std::make_tuple(16000, 48000, 32000, 48000, 1.5, 30), - std::make_tuple(16000, 48000, 16000, 48000, 1.5, 20), - std::make_tuple(16000, 44100, 48000, 44100, 15, 20), - std::make_tuple(16000, 44100, 32000, 44100, 15, 15), - std::make_tuple(16000, 44100, 16000, 44100, 15, 15), - std::make_tuple(16000, 32000, 48000, 32000, 25, 35), - std::make_tuple(16000, 32000, 32000, 32000, 25, 0), - std::make_tuple(16000, 32000, 16000, 32000, 25, 20), - std::make_tuple(16000, 16000, 48000, 16000, 39, 20), - std::make_tuple(16000, 16000, 32000, 16000, 39, 20), - std::make_tuple(16000, 16000, 16000, 16000, 0, 0), + ::testing::Values(std::make_tuple(48000, 48000, 48000, 48000), + std::make_tuple(48000, 48000, 32000, 48000), + std::make_tuple(48000, 48000, 16000, 48000), + std::make_tuple(48000, 44100, 48000, 44100), + std::make_tuple(48000, 44100, 32000, 44100), + std::make_tuple(48000, 44100, 16000, 44100), + std::make_tuple(48000, 32000, 48000, 32000), + std::make_tuple(48000, 32000, 32000, 32000), + std::make_tuple(48000, 32000, 16000, 32000), + std::make_tuple(48000, 16000, 48000, 16000), + std::make_tuple(48000, 16000, 32000, 16000), + std::make_tuple(48000, 16000, 16000, 16000), + + std::make_tuple(44100, 48000, 48000, 48000), + std::make_tuple(44100, 48000, 32000, 48000), + std::make_tuple(44100, 48000, 16000, 48000), + std::make_tuple(44100, 44100, 48000, 44100), + std::make_tuple(44100, 44100, 32000, 44100), + std::make_tuple(44100, 44100, 16000, 44100), + std::make_tuple(44100, 32000, 48000, 32000), + std::make_tuple(44100, 32000, 32000, 32000), + std::make_tuple(44100, 32000, 16000, 32000), + std::make_tuple(44100, 16000, 48000, 16000), + std::make_tuple(44100, 16000, 32000, 16000), + std::make_tuple(44100, 16000, 16000, 16000), + + std::make_tuple(32000, 48000, 48000, 48000), + std::make_tuple(32000, 48000, 32000, 48000), + std::make_tuple(32000, 48000, 16000, 48000), + std::make_tuple(32000, 44100, 48000, 44100), + std::make_tuple(32000, 44100, 32000, 44100), + std::make_tuple(32000, 44100, 16000, 44100), + std::make_tuple(32000, 32000, 48000, 32000), + std::make_tuple(32000, 32000, 32000, 32000), + std::make_tuple(32000, 32000, 16000, 32000), + std::make_tuple(32000, 16000, 48000, 16000), + std::make_tuple(32000, 16000, 32000, 16000), + std::make_tuple(32000, 16000, 16000, 16000), + + std::make_tuple(16000, 48000, 48000, 48000), + std::make_tuple(16000, 48000, 32000, 48000), + std::make_tuple(16000, 48000, 16000, 48000), + std::make_tuple(16000, 44100, 48000, 44100), + std::make_tuple(16000, 44100, 32000, 44100), + std::make_tuple(16000, 44100, 16000, 44100), + std::make_tuple(16000, 32000, 48000, 32000), + std::make_tuple(16000, 32000, 32000, 32000), + std::make_tuple(16000, 32000, 16000, 32000), + std::make_tuple(16000, 16000, 48000, 16000), + std::make_tuple(16000, 16000, 32000, 16000), + std::make_tuple(16000, 16000, 16000, 16000), // Other sample rates are not tested exhaustively, to keep // the test runtime manageable. @@ -2295,74 +2011,11 @@ INSTANTIATE_TEST_SUITE_P( // - WebRTC.AudioOutputSampleRate // ApmConfiguration.HandlingOfRateCombinations covers // remaining sample rates. - std::make_tuple(192000, 192000, 48000, 192000, 20, 40), - std::make_tuple(176400, 176400, 48000, 176400, 20, 35), - std::make_tuple(96000, 96000, 48000, 96000, 20, 40), - std::make_tuple(88200, 88200, 48000, 88200, 20, 20), - std::make_tuple(44100, 44100, 48000, 44100, 20, 20))); - -#elif defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) -INSTANTIATE_TEST_SUITE_P( - CommonFormats, - AudioProcessingTest, - ::testing::Values(std::make_tuple(48000, 48000, 48000, 48000, 19, 0), - std::make_tuple(48000, 48000, 32000, 48000, 19, 30), - std::make_tuple(48000, 48000, 16000, 48000, 19, 20), - std::make_tuple(48000, 44100, 48000, 44100, 15, 20), - std::make_tuple(48000, 44100, 32000, 44100, 15, 15), - std::make_tuple(48000, 44100, 16000, 44100, 15, 15), - std::make_tuple(48000, 32000, 48000, 32000, 19, 35), - std::make_tuple(48000, 32000, 32000, 32000, 19, 0), - std::make_tuple(48000, 32000, 16000, 32000, 19, 20), - std::make_tuple(48000, 16000, 48000, 16000, 20, 20), - std::make_tuple(48000, 16000, 32000, 16000, 20, 20), - std::make_tuple(48000, 16000, 16000, 16000, 20, 0), - - std::make_tuple(44100, 48000, 48000, 48000, 15, 0), - std::make_tuple(44100, 48000, 32000, 48000, 15, 30), - std::make_tuple(44100, 48000, 16000, 48000, 15, 20), - std::make_tuple(44100, 44100, 48000, 44100, 15, 20), - std::make_tuple(44100, 44100, 32000, 44100, 15, 15), - std::make_tuple(44100, 44100, 16000, 44100, 15, 15), - std::make_tuple(44100, 32000, 48000, 32000, 18, 35), - std::make_tuple(44100, 32000, 32000, 32000, 18, 0), - std::make_tuple(44100, 32000, 16000, 32000, 18, 20), - std::make_tuple(44100, 16000, 48000, 16000, 19, 20), - std::make_tuple(44100, 16000, 32000, 16000, 19, 20), - std::make_tuple(44100, 16000, 16000, 16000, 19, 0), - - std::make_tuple(32000, 48000, 48000, 48000, 8, 0), - std::make_tuple(32000, 48000, 32000, 48000, 8, 30), - std::make_tuple(32000, 48000, 16000, 48000, 8, 20), - std::make_tuple(32000, 44100, 48000, 44100, 20, 20), - std::make_tuple(32000, 44100, 32000, 44100, 20, 15), - std::make_tuple(32000, 44100, 16000, 44100, 20, 15), - std::make_tuple(32000, 32000, 48000, 32000, 27, 35), - std::make_tuple(32000, 32000, 32000, 32000, 0, 0), - std::make_tuple(32000, 32000, 16000, 32000, 30, 20), - std::make_tuple(32000, 16000, 48000, 16000, 20, 20), - std::make_tuple(32000, 16000, 32000, 16000, 20, 20), - std::make_tuple(32000, 16000, 16000, 16000, 20, 0), - - std::make_tuple(16000, 48000, 48000, 48000, 3, 0), - std::make_tuple(16000, 48000, 32000, 48000, 3, 30), - std::make_tuple(16000, 48000, 16000, 48000, 3, 20), - std::make_tuple(16000, 44100, 48000, 44100, 15, 20), - std::make_tuple(16000, 44100, 32000, 44100, 15, 15), - std::make_tuple(16000, 44100, 16000, 44100, 15, 15), - std::make_tuple(16000, 32000, 48000, 32000, 23, 35), - std::make_tuple(16000, 32000, 32000, 32000, 23, 0), - std::make_tuple(16000, 32000, 16000, 32000, 25, 20), - std::make_tuple(16000, 16000, 48000, 16000, 24, 20), - std::make_tuple(16000, 16000, 32000, 16000, 24, 20), - std::make_tuple(16000, 16000, 16000, 16000, 0, 0), - - std::make_tuple(192000, 192000, 48000, 192000, 20, 40), - std::make_tuple(176400, 176400, 48000, 176400, 20, 35), - std::make_tuple(96000, 96000, 48000, 96000, 20, 40), - std::make_tuple(88200, 88200, 48000, 88200, 20, 20), - std::make_tuple(44100, 44100, 48000, 44100, 20, 20))); -#endif + std::make_tuple(192000, 192000, 48000, 192000), + std::make_tuple(176400, 176400, 48000, 176400), + std::make_tuple(96000, 96000, 48000, 96000), + std::make_tuple(88200, 88200, 48000, 88200), + std::make_tuple(44100, 44100, 48000, 44100))); // Produces a scoped trace debug output. std::string ProduceDebugText(int render_input_sample_rate_hz, @@ -2398,9 +2051,10 @@ std::string ProduceDebugText(int render_input_sample_rate_hz, // Validates that running the audio processing module using various combinations // of sample rates and number of channels works as intended. -void RunApmRateAndChannelTest(ArrayView sample_rates_hz, - ArrayView render_channel_counts, - ArrayView capture_channel_counts) { +void RunApmRateAndChannelTest(std::span sample_rates_hz, + std::span render_channel_counts, + std::span capture_channel_counts, + std::span mono_capture_output) { AudioProcessing::Config apm_config; apm_config.pipeline.multi_channel_render = true; apm_config.pipeline.multi_channel_capture = true; @@ -2428,59 +2082,64 @@ void RunApmRateAndChannelTest(ArrayView sample_rates_hz, for (auto capture_output_sample_rate_hz : sample_rates_hz) { for (size_t render_input_num_channels : render_channel_counts) { for (size_t capture_input_num_channels : capture_channel_counts) { - size_t render_output_num_channels = render_input_num_channels; - size_t capture_output_num_channels = capture_input_num_channels; - auto populate_audio_frame = [](int sample_rate_hz, - size_t num_channels, - StreamConfig* cfg, - std::vector* channels_data, - std::vector* frame_data) { - cfg->set_sample_rate_hz(sample_rate_hz); - cfg->set_num_channels(num_channels); - - size_t max_frame_size = - AudioProcessing::GetFrameSize(sample_rate_hz); - channels_data->resize(num_channels * max_frame_size); - std::fill(channels_data->begin(), channels_data->end(), 0.5f); - frame_data->resize(num_channels); - for (size_t channel = 0; channel < num_channels; ++channel) { - (*frame_data)[channel] = - &(*channels_data)[channel * max_frame_size]; + for (bool use_mono_capture_output : mono_capture_output) { + size_t render_output_num_channels = render_input_num_channels; + size_t capture_output_num_channels = + use_mono_capture_output ? 1 : capture_input_num_channels; + auto populate_audio_frame = + [](int sample_rate_hz, size_t num_channels, + StreamConfig* cfg, std::vector* channels_data, + std::vector* frame_data) { + cfg->set_sample_rate_hz(sample_rate_hz); + cfg->set_num_channels(num_channels); + + size_t max_frame_size = + AudioProcessing::GetFrameSize(sample_rate_hz); + channels_data->resize(num_channels * max_frame_size); + std::fill(channels_data->begin(), channels_data->end(), + 0.5f); + frame_data->resize(num_channels); + for (size_t channel = 0; channel < num_channels; + ++channel) { + (*frame_data)[channel] = + &(*channels_data)[channel * max_frame_size]; + } + }; + + populate_audio_frame( + render_input_sample_rate_hz, render_input_num_channels, + &render_input_stream_config, &render_input_frame_channels, + &render_input_frame); + populate_audio_frame( + render_output_sample_rate_hz, render_output_num_channels, + &render_output_stream_config, &render_output_frame_channels, + &render_output_frame); + populate_audio_frame( + capture_input_sample_rate_hz, capture_input_num_channels, + &capture_input_stream_config, &capture_input_frame_channels, + &capture_input_frame); + populate_audio_frame( + capture_output_sample_rate_hz, capture_output_num_channels, + &capture_output_stream_config, + &capture_output_frame_channels, &capture_output_frame); + + for (size_t frame = 0; frame < 2; ++frame) { + SCOPED_TRACE(ProduceDebugText( + render_input_sample_rate_hz, render_output_sample_rate_hz, + capture_input_sample_rate_hz, + capture_output_sample_rate_hz, render_input_num_channels, + render_output_num_channels, render_input_num_channels, + capture_output_num_channels)); + + int result = apm->ProcessReverseStream( + &render_input_frame[0], render_input_stream_config, + render_output_stream_config, &render_output_frame[0]); + EXPECT_EQ(result, AudioProcessing::kNoError); + result = apm->ProcessStream( + &capture_input_frame[0], capture_input_stream_config, + capture_output_stream_config, &capture_output_frame[0]); + EXPECT_EQ(result, AudioProcessing::kNoError); } - }; - - populate_audio_frame( - render_input_sample_rate_hz, render_input_num_channels, - &render_input_stream_config, &render_input_frame_channels, - &render_input_frame); - populate_audio_frame( - render_output_sample_rate_hz, render_output_num_channels, - &render_output_stream_config, &render_output_frame_channels, - &render_output_frame); - populate_audio_frame( - capture_input_sample_rate_hz, capture_input_num_channels, - &capture_input_stream_config, &capture_input_frame_channels, - &capture_input_frame); - populate_audio_frame( - capture_output_sample_rate_hz, capture_output_num_channels, - &capture_output_stream_config, &capture_output_frame_channels, - &capture_output_frame); - - for (size_t frame = 0; frame < 2; ++frame) { - SCOPED_TRACE(ProduceDebugText( - render_input_sample_rate_hz, render_output_sample_rate_hz, - capture_input_sample_rate_hz, capture_output_sample_rate_hz, - render_input_num_channels, render_output_num_channels, - render_input_num_channels, capture_output_num_channels)); - - int result = apm->ProcessReverseStream( - &render_input_frame[0], render_input_stream_config, - render_output_stream_config, &render_output_frame[0]); - EXPECT_EQ(result, AudioProcessing::kNoError); - result = apm->ProcessStream( - &capture_input_frame[0], capture_input_stream_config, - capture_output_stream_config, &capture_output_frame[0]); - EXPECT_EQ(result, AudioProcessing::kNoError); } } } @@ -2667,11 +2326,11 @@ TEST(ApmConfiguration, EchoDetectorInjection) { // The echo detector is included in processing when enabled. EXPECT_CALL(*mock_echo_detector, AnalyzeRenderAudio(_)) - .WillOnce([](ArrayView render_audio) { + .WillOnce([](std::span render_audio) { EXPECT_EQ(render_audio.size(), 160u); }); EXPECT_CALL(*mock_echo_detector, AnalyzeCaptureAudio(_)) - .WillOnce([](ArrayView capture_audio) { + .WillOnce([](std::span capture_audio) { EXPECT_EQ(capture_audio.size(), 160u); }); EXPECT_CALL(*mock_echo_detector, GetMetrics()).Times(1); @@ -2697,12 +2356,12 @@ TEST(ApmConfiguration, EchoDetectorInjection) { /*render_sample_rate_hz=*/48000, _)) .Times(1); EXPECT_CALL(*mock_echo_detector, AnalyzeRenderAudio(_)) - .WillOnce([](ArrayView render_audio) { + .WillOnce([](std::span render_audio) { EXPECT_EQ(render_audio.size(), 480u); }); EXPECT_CALL(*mock_echo_detector, AnalyzeCaptureAudio(_)) .Times(2) - .WillRepeatedly([](ArrayView capture_audio) { + .WillRepeatedly([](std::span capture_audio) { EXPECT_EQ(capture_audio.size(), 480u); }); EXPECT_CALL(*mock_echo_detector, GetMetrics()).Times(2); @@ -2718,7 +2377,7 @@ TEST(ApmConfiguration, EchoDetectorInjection) { StreamConfig(48000, 1), frame.data.data()); } -scoped_refptr CreateApm(bool mobile_aec) { +scoped_refptr CreateApm() { // Enable residual echo detection, for stats. scoped_refptr apm = BuiltinAudioProcessingBuilder() @@ -2741,7 +2400,6 @@ scoped_refptr CreateApm(bool mobile_aec) { apm_config.gain_controller1.enabled = false; apm_config.gain_controller2.enabled = false; apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.mobile_mode = mobile_aec; apm_config.noise_suppression.enabled = false; apm->ApplyConfig(apm_config); return apm; @@ -2755,7 +2413,7 @@ scoped_refptr CreateApm(bool mobile_aec) { TEST(MAYBE_ApmStatistics, AECEnabledTest) { // Set up APM with AEC3 and process some audio. - scoped_refptr apm = CreateApm(false); + scoped_refptr apm = CreateApm(); ASSERT_TRUE(apm); AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = true; @@ -2805,54 +2463,6 @@ TEST(MAYBE_ApmStatistics, AECEnabledTest) { EXPECT_NE(*stats.echo_return_loss_enhancement, -100.0); } -TEST(MAYBE_ApmStatistics, AECMEnabledTest) { - // Set up APM with AECM and process some audio. - scoped_refptr apm = CreateApm(true); - ASSERT_TRUE(apm); - - // Set up an audioframe. - Int16FrameData frame; - frame.SetProperties(AudioProcessing::GetFrameSize( - AudioProcessing::NativeRate::kSampleRate32kHz), - /* num_channels=*/1); - - // Fill the audio frame with a sawtooth pattern. - int16_t* ptr = frame.data.data(); - for (size_t i = 0; i < Int16FrameData::kMaxDataSizeSamples; i++) { - ptr[i] = 10000 * ((i % 3) - 1); - } - - // Do some processing. - for (int i = 0; i < 200; i++) { - EXPECT_EQ(apm->ProcessReverseStream( - frame.data.data(), - StreamConfig(frame.sample_rate_hz, frame.num_channels()), - StreamConfig(frame.sample_rate_hz, frame.num_channels()), - frame.data.data()), - 0); - EXPECT_EQ(apm->set_stream_delay_ms(0), 0); - EXPECT_EQ(apm->ProcessStream( - frame.data.data(), - StreamConfig(frame.sample_rate_hz, frame.num_channels()), - StreamConfig(frame.sample_rate_hz, frame.num_channels()), - frame.data.data()), - 0); - } - - // Test statistics interface. - AudioProcessingStats stats = apm->GetStatistics(); - // We expect only the residual echo detector statistics to be set and have a - // sensible value. - ASSERT_TRUE(stats.residual_echo_likelihood.has_value()); - EXPECT_GE(*stats.residual_echo_likelihood, 0.0); - EXPECT_LE(*stats.residual_echo_likelihood, 1.0); - ASSERT_TRUE(stats.residual_echo_likelihood_recent_max.has_value()); - EXPECT_GE(*stats.residual_echo_likelihood_recent_max, 0.0); - EXPECT_LE(*stats.residual_echo_likelihood_recent_max, 1.0); - EXPECT_FALSE(stats.echo_return_loss.has_value()); - EXPECT_FALSE(stats.echo_return_loss_enhancement.has_value()); -} - TEST(ApmStatistics, DoNotReportVoiceDetectedStat) { ProcessingConfig processing_config = { {{32000, 1}, {32000, 1}, {32000, 1}, {32000, 1}}}; @@ -2931,16 +2541,18 @@ TEST(ApmConfiguration, HandlingOfRateAndChannelCombinations) { std::array sample_rates_hz = {16000, 32000, 48000}; std::array render_channel_counts = {1, 7}; std::array capture_channel_counts = {1, 7}; + std::array mono_capture_output = {true, false}; RunApmRateAndChannelTest(sample_rates_hz, render_channel_counts, - capture_channel_counts); + capture_channel_counts, mono_capture_output); } TEST(ApmConfiguration, HandlingOfChannelCombinations) { std::array sample_rates_hz = {48000}; std::array render_channel_counts = {1, 2, 3, 4, 5, 6, 7, 8}; std::array capture_channel_counts = {1, 2, 3, 4, 5, 6, 7, 8}; + std::array mono_capture_output = {true, false}; RunApmRateAndChannelTest(sample_rates_hz, render_channel_counts, - capture_channel_counts); + capture_channel_counts, mono_capture_output); } TEST(ApmConfiguration, HandlingOfRateCombinations) { @@ -2953,8 +2565,9 @@ TEST(ApmConfiguration, HandlingOfRateCombinations) { 44100, 48000, 88200, 96000}; std::array render_channel_counts = {2}; std::array capture_channel_counts = {2}; + std::array mono_capture_output = {true, false}; RunApmRateAndChannelTest(sample_rates_hz, render_channel_counts, - capture_channel_counts); + capture_channel_counts, mono_capture_output); } TEST(ApmConfiguration, SelfAssignment) { diff --git a/modules/audio_processing/capture_levels_adjuster/BUILD.gn b/modules/audio_processing/capture_levels_adjuster/BUILD.gn index 20b2e395a84..5bd81487d41 100644 --- a/modules/audio_processing/capture_levels_adjuster/BUILD.gn +++ b/modules/audio_processing/capture_levels_adjuster/BUILD.gn @@ -22,7 +22,6 @@ rtc_library("capture_levels_adjuster") { deps = [ "..:audio_buffer", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:safe_minmax", ] @@ -39,8 +38,6 @@ rtc_library("capture_levels_adjuster_unittests") { ":capture_levels_adjuster", "..:audio_buffer", "..:audioproc_test_utils", - "../../../rtc_base:gunit_helpers", - "../../../rtc_base:stringutils", "../../../test:test_support", ] } diff --git a/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc index a2430ab4f18..b32f8d2c004 100644 --- a/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc +++ b/modules/audio_processing/capture_levels_adjuster/audio_samples_scaler.cc @@ -11,8 +11,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/audio_buffer.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_minmax.h" @@ -40,7 +40,7 @@ void AudioSamplesScaler::Process(AudioBuffer& audio_buffer) { if (previous_gain_ == target_gain_) { // Apply a non-changing gain. for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) { - ArrayView channel_view(audio_buffer.channels()[channel], + std::span channel_view(audio_buffer.channels()[channel], samples_per_channel_); for (float& sample : channel_view) { sample *= gain; @@ -55,7 +55,7 @@ void AudioSamplesScaler::Process(AudioBuffer& audio_buffer) { for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) { gain = previous_gain_; - ArrayView channel_view(audio_buffer.channels()[channel], + std::span channel_view(audio_buffer.channels()[channel], samples_per_channel_); for (float& sample : channel_view) { gain = std::min(gain + increment, target_gain_); @@ -67,7 +67,7 @@ void AudioSamplesScaler::Process(AudioBuffer& audio_buffer) { for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) { gain = previous_gain_; - ArrayView channel_view(audio_buffer.channels()[channel], + std::span channel_view(audio_buffer.channels()[channel], samples_per_channel_); for (float& sample : channel_view) { gain = std::max(gain + increment, target_gain_); @@ -80,7 +80,7 @@ void AudioSamplesScaler::Process(AudioBuffer& audio_buffer) { // Saturate the samples to be in the S16 range. for (size_t channel = 0; channel < audio_buffer.num_channels(); ++channel) { - ArrayView channel_view(audio_buffer.channels()[channel], + std::span channel_view(audio_buffer.channels()[channel], samples_per_channel_); for (float& sample : channel_view) { constexpr float kMinFloatS16Value = -32768.f; diff --git a/modules/audio_processing/capture_mixer/BUILD.gn b/modules/audio_processing/capture_mixer/BUILD.gn index 981bb654012..8d7b0eb444a 100644 --- a/modules/audio_processing/capture_mixer/BUILD.gn +++ b/modules/audio_processing/capture_mixer/BUILD.gn @@ -8,7 +8,7 @@ import("../../../webrtc.gni") -rtc_library("stereo_remixer") { +rtc_library("capture_mixer") { visibility = [ "*" ] sources = [ @@ -30,10 +30,7 @@ rtc_library("stereo_remixer") { defines = [] - deps = [ - "../../../api:array_view", - "../../../rtc_base:checks", - ] + deps = [ "../../../rtc_base:checks" ] } rtc_library("capture_mixer_unittests") { @@ -49,11 +46,7 @@ rtc_library("capture_mixer_unittests") { "saturation_estimator_unittest.cc", ] deps = [ - ":stereo_remixer", - "..:audioproc_test_utils", - "../../../api:array_view", - "../../../rtc_base:gunit_helpers", - "../../../rtc_base:stringutils", + ":capture_mixer", "../../../test:test_support", ] } diff --git a/modules/audio_processing/capture_mixer/audio_content_analyzer.cc b/modules/audio_processing/capture_mixer/audio_content_analyzer.cc index 4f079427ec7..db47fb12a18 100644 --- a/modules/audio_processing/capture_mixer/audio_content_analyzer.cc +++ b/modules/audio_processing/capture_mixer/audio_content_analyzer.cc @@ -10,8 +10,7 @@ #include "modules/audio_processing/capture_mixer/audio_content_analyzer.h" #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -19,8 +18,8 @@ AudioContentAnalyzer::AudioContentAnalyzer(size_t num_samples_per_channel) : dc_levels_estimator_(num_samples_per_channel), saturation_estimator_(num_samples_per_channel) {} -bool AudioContentAnalyzer::Analyze(ArrayView channel0, - ArrayView channel1) { +bool AudioContentAnalyzer::Analyze(std::span channel0, + std::span channel1) { ++num_frames_analyzed_; // Exclude the first frame from the analysis to avoid reacting on any @@ -40,7 +39,7 @@ bool AudioContentAnalyzer::Analyze(ArrayView channel0, return false; } - ArrayView dc_levels = dc_levels_estimator_.GetLevels(); + std::span dc_levels = dc_levels_estimator_.GetLevels(); energy_estimator_.Update(channel0, channel1, dc_levels); saturation_estimator_.Update(channel0, channel1, dc_levels); diff --git a/modules/audio_processing/capture_mixer/audio_content_analyzer.h b/modules/audio_processing/capture_mixer/audio_content_analyzer.h index e4d8eb1fa79..436c15c76fa 100644 --- a/modules/audio_processing/capture_mixer/audio_content_analyzer.h +++ b/modules/audio_processing/capture_mixer/audio_content_analyzer.h @@ -12,7 +12,8 @@ #include -#include "api/array_view.h" +#include + #include "modules/audio_processing/capture_mixer/dc_levels_estimator.h" #include "modules/audio_processing/capture_mixer/energy_estimator.h" #include "modules/audio_processing/capture_mixer/saturation_estimator.h" @@ -33,17 +34,17 @@ class AudioContentAnalyzer { // Analyzes the provided audio samples for the two channels. // Updates the internal energy, and saturation estimators. // Returns true if the current frame is considered to contain activity. - bool Analyze(ArrayView channel0, - ArrayView channel1); + bool Analyze(std::span channel0, + std::span channel1); // Returns the current average energy estimates for the two channels. - ArrayView GetChannelEnergies() const { + std::span GetChannelEnergies() const { return energy_estimator_.GetChannelEnergies(); } // Returns the number of frames since the last activity was detected in each // of the channels. - ArrayView GetNumFramesSinceActivity() const { + std::span GetNumFramesSinceActivity() const { return saturation_estimator_.GetNumFramesSinceActivity(); } @@ -51,7 +52,7 @@ class AudioContentAnalyzer { // saturation factor is a value between 0 and 1, where 1 means that the signal // has recently been fully saturated and 0 means that no saturation has been // observed in the resent past. - ArrayView GetSaturationFactors() const { + std::span GetSaturationFactors() const { return saturation_estimator_.GetSaturationFactors(); } diff --git a/modules/audio_processing/capture_mixer/audio_content_analyzer_unittest.cc b/modules/audio_processing/capture_mixer/audio_content_analyzer_unittest.cc index 51abf94cfaa..ded4c0e83b1 100644 --- a/modules/audio_processing/capture_mixer/audio_content_analyzer_unittest.cc +++ b/modules/audio_processing/capture_mixer/audio_content_analyzer_unittest.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { diff --git a/modules/audio_processing/capture_mixer/capture_mixer.cc b/modules/audio_processing/capture_mixer/capture_mixer.cc index d1d011632f4..cbbfb928a61 100644 --- a/modules/audio_processing/capture_mixer/capture_mixer.cc +++ b/modules/audio_processing/capture_mixer/capture_mixer.cc @@ -10,8 +10,8 @@ #include "modules/audio_processing/capture_mixer/capture_mixer.h" #include +#include -#include "api/array_view.h" #include "modules/audio_processing/capture_mixer/channel_content_remixer.h" #include "rtc_base/checks.h" @@ -28,8 +28,8 @@ CaptureMixer::CaptureMixer(size_t num_samples_per_channel) remixing_logic_(num_samples_per_channel) {} void CaptureMixer::Mix(size_t num_output_channels, - ArrayView channel0, - ArrayView channel1) { + std::span channel0, + std::span channel1) { RTC_DCHECK_GE(num_output_channels, 1); RTC_DCHECK_LE(num_output_channels, 2); @@ -45,11 +45,11 @@ void CaptureMixer::Mix(size_t num_output_channels, return; } - ArrayView average_energies = + std::span average_energies = audio_content_analyzer_.GetChannelEnergies(); - ArrayView num_frames_since_activity = + std::span num_frames_since_activity = audio_content_analyzer_.GetNumFramesSinceActivity(); - ArrayView saturation_factors = + std::span saturation_factors = audio_content_analyzer_.GetSaturationFactors(); mixing_variant_ = remixing_logic_.SelectStereoChannelMixing( diff --git a/modules/audio_processing/capture_mixer/capture_mixer.h b/modules/audio_processing/capture_mixer/capture_mixer.h index 88c35f9ad37..2cc9605f367 100644 --- a/modules/audio_processing/capture_mixer/capture_mixer.h +++ b/modules/audio_processing/capture_mixer/capture_mixer.h @@ -12,7 +12,8 @@ #include -#include "api/array_view.h" +#include + #include "modules/audio_processing/capture_mixer/audio_content_analyzer.h" #include "modules/audio_processing/capture_mixer/channel_content_remixer.h" #include "modules/audio_processing/capture_mixer/remixing_logic.h" @@ -26,8 +27,8 @@ class CaptureMixer { CaptureMixer& operator=(const CaptureMixer&) = delete; void Mix(size_t num_output_channels, - ArrayView channel0, - ArrayView channel1); + std::span channel0, + std::span channel1); private: AudioContentAnalyzer audio_content_analyzer_; diff --git a/modules/audio_processing/capture_mixer/channel_content_remixer.cc b/modules/audio_processing/capture_mixer/channel_content_remixer.cc index 073abdb8f8f..8293f47909c 100644 --- a/modules/audio_processing/capture_mixer/channel_content_remixer.cc +++ b/modules/audio_processing/capture_mixer/channel_content_remixer.cc @@ -11,8 +11,8 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -28,8 +28,8 @@ ChannelContentRemixer::ChannelContentRemixer(size_t num_samples_per_channel, bool ChannelContentRemixer::Mix(size_t num_output_channels, StereoMixingVariant mixing_variant, - ArrayView channel0, - ArrayView channel1) { + std::span channel0, + std::span channel1) { RTC_DCHECK_EQ(channel0.size(), num_samples_per_channel_); RTC_DCHECK_EQ(channel1.size(), num_samples_per_channel_); @@ -304,14 +304,14 @@ bool ChannelContentRemixer::IsCrossfadeCompleted() { } void ChannelContentRemixer::CopyChannelContent( - ArrayView source, - ArrayView destination) const { + std::span source, + std::span destination) const { std::copy(source.begin(), source.end(), destination.begin()); } void ChannelContentRemixer::StoreChannelAverageIntoBothChannels( - ArrayView channel0, - ArrayView channel1) const { + std::span channel0, + std::span channel1) const { for (size_t k = 0; k < channel0.size(); ++k) { float average = (channel0[k] + channel1[k]) * 0.5f; channel0[k] = average; @@ -320,9 +320,9 @@ void ChannelContentRemixer::StoreChannelAverageIntoBothChannels( } void ChannelContentRemixer::CrossFadeFromSingleChannelToSingleChannel( - ArrayView crossfade_from, - ArrayView crossfade_to, - ArrayView destination, + std::span crossfade_from, + std::span crossfade_to, + std::span destination, size_t& crossfade_sample_counter) const { for (size_t k = 0; k < destination.size(); ++k, ++crossfade_sample_counter) { const float scaling = @@ -333,9 +333,9 @@ void ChannelContentRemixer::CrossFadeFromSingleChannelToSingleChannel( } void ChannelContentRemixer::CrossFadeFromSingleChannelContentToAverage( - ArrayView crossfade_from, - ArrayView channel0, - ArrayView channel1, + std::span crossfade_from, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const { for (size_t k = 0; k < channel0.size(); ++k, ++crossfade_sample_counter) { const float scaling = @@ -350,9 +350,9 @@ void ChannelContentRemixer::CrossFadeFromSingleChannelContentToAverage( } void ChannelContentRemixer::CrossFadeFromAverageToSingleChannelContent( - ArrayView crossfade_to, - ArrayView channel0, - ArrayView channel1, + std::span crossfade_to, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const { for (size_t k = 0; k < channel0.size(); ++k, ++crossfade_sample_counter) { const float scaling = @@ -366,8 +366,8 @@ void ChannelContentRemixer::CrossFadeFromAverageToSingleChannelContent( } void ChannelContentRemixer::CrossFadeFromAverageToBothChannels( - ArrayView channel0, - ArrayView channel1, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const { for (size_t k = 0; k < channel0.size(); ++k, ++crossfade_sample_counter) { const float scaling = @@ -381,8 +381,8 @@ void ChannelContentRemixer::CrossFadeFromAverageToBothChannels( } void ChannelContentRemixer::CrossFadeFromBothChannelsToAverage( - ArrayView channel0, - ArrayView channel1, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const { for (size_t k = 0; k < channel0.size(); ++k, ++crossfade_sample_counter) { const float scaling = @@ -395,9 +395,9 @@ void ChannelContentRemixer::CrossFadeFromBothChannelsToAverage( } void ChannelContentRemixer::CrossFadeFromAverageInToChannel0( - ArrayView crossfade_to, - ArrayView channel0, - ArrayView channel1, + std::span crossfade_to, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const { for (size_t k = 0; k < channel0.size(); ++k, ++crossfade_sample_counter) { const float scaling = @@ -409,9 +409,9 @@ void ChannelContentRemixer::CrossFadeFromAverageInToChannel0( } void ChannelContentRemixer::CrossFadeChannel0ToAverage( - ArrayView crossfade_from, - ArrayView channel0, - ArrayView channel1, + std::span crossfade_from, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const { for (size_t k = 0; k < channel0.size(); ++k, ++crossfade_sample_counter) { const float scaling = @@ -423,8 +423,8 @@ void ChannelContentRemixer::CrossFadeChannel0ToAverage( } void ChannelContentRemixer::StoreChannelAverageIntoChannel0( - ArrayView channel0, - ArrayView channel1) const { + std::span channel0, + std::span channel1) const { for (size_t k = 0; k < channel0.size(); ++k) { float average = (channel0[k] + channel1[k]) * 0.5f; channel0[k] = average; diff --git a/modules/audio_processing/capture_mixer/channel_content_remixer.h b/modules/audio_processing/capture_mixer/channel_content_remixer.h index 39d52bb7ed7..3226dbe1563 100644 --- a/modules/audio_processing/capture_mixer/channel_content_remixer.h +++ b/modules/audio_processing/capture_mixer/channel_content_remixer.h @@ -11,8 +11,7 @@ #define MODULES_AUDIO_PROCESSING_CAPTURE_MIXER_CHANNEL_CONTENT_REMIXER_H_ #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -50,8 +49,8 @@ class ChannelContentRemixer { // crossfades are completed. bool Mix(size_t num_output_channels, StereoMixingVariant mixing_variant, - ArrayView channel0, - ArrayView channel1); + std::span channel0, + std::span channel1); private: const size_t num_samples_per_channel_; @@ -67,63 +66,63 @@ class ChannelContentRemixer { bool IsCrossfadeCompleted(); // Copies content from source to destination. - void CopyChannelContent(ArrayView source, - ArrayView destination) const; + void CopyChannelContent(std::span source, + std::span destination) const; // Calculates the average of channel0 and channel1 and writes it to both. - void StoreChannelAverageIntoBothChannels(ArrayView channel0, - ArrayView channel1) const; + void StoreChannelAverageIntoBothChannels(std::span channel0, + std::span channel1) const; // Performs a linear cross-fade from `crossfade_from` to `crossfade_to` into // `destination`. void CrossFadeFromSingleChannelToSingleChannel( - ArrayView crossfade_from, - ArrayView crossfade_to, - ArrayView destination, + std::span crossfade_from, + std::span crossfade_to, + std::span destination, size_t& crossfade_sample_counter) const; // Cross-fades from a single channel to the average of both channels. void CrossFadeFromSingleChannelContentToAverage( - ArrayView crossfade_from, - ArrayView channel0, - ArrayView channel1, + std::span crossfade_from, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const; // Cross-fades from the average of both channels to a single channel. void CrossFadeFromAverageToSingleChannelContent( - ArrayView crossfade_to, - ArrayView channel0, - ArrayView channel1, + std::span crossfade_to, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const; // Cross-fades from the average of both channels to using both channels // independently. void CrossFadeFromAverageToBothChannels( - ArrayView channel0, - ArrayView channel1, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const; // Cross-fades from using both channels independently to their average. void CrossFadeFromBothChannelsToAverage( - ArrayView channel0, - ArrayView channel1, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const; // specific helper for Mix when num_output_channels == 1. - void CrossFadeFromAverageInToChannel0(ArrayView crossfade_to, - ArrayView channel0, - ArrayView channel1, + void CrossFadeFromAverageInToChannel0(std::span crossfade_to, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const; // specific helper for Mix when num_output_channels == 1. - void CrossFadeChannel0ToAverage(ArrayView crossfade_from, - ArrayView channel0, - ArrayView channel1, + void CrossFadeChannel0ToAverage(std::span crossfade_from, + std::span channel0, + std::span channel1, size_t& crossfade_sample_counter) const; // specific helper for Mix when num_output_channels == 1. - void StoreChannelAverageIntoChannel0(ArrayView channel0, - ArrayView channel1) const; + void StoreChannelAverageIntoChannel0(std::span channel0, + std::span channel1) const; }; } // namespace webrtc diff --git a/modules/audio_processing/capture_mixer/channel_content_remixer_unittest.cc b/modules/audio_processing/capture_mixer/channel_content_remixer_unittest.cc index 48ca99c698d..bfc2ef73808 100644 --- a/modules/audio_processing/capture_mixer/channel_content_remixer_unittest.cc +++ b/modules/audio_processing/capture_mixer/channel_content_remixer_unittest.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -33,7 +33,7 @@ void PopulateChannels(std::vector& channel0, void VerifyCrossFade(float value_begin, float value_end, - ArrayView channel_data) { + std::span channel_data) { const float one_by_num_samples_per_channel = 1.0f / channel_data.size(); for (size_t k = 0; k < channel_data.size(); ++k) { const float expected_value = @@ -44,7 +44,7 @@ void VerifyCrossFade(float value_begin, } void VerifyConstantValue(float expected_value, - ArrayView channel_data) { + std::span channel_data) { for (size_t k = 0; k < channel_data.size(); ++k) { EXPECT_NEAR(channel_data[k], expected_value, 1e-3); } diff --git a/modules/audio_processing/capture_mixer/dc_levels_estimator.cc b/modules/audio_processing/capture_mixer/dc_levels_estimator.cc index 5f8d1fdd45b..1651d373793 100644 --- a/modules/audio_processing/capture_mixer/dc_levels_estimator.cc +++ b/modules/audio_processing/capture_mixer/dc_levels_estimator.cc @@ -11,15 +11,14 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { namespace { void UpdateDcEstimate(float one_by_num_samples_per_channel, - ArrayView audio, + std::span audio, float& dc_estimate) { constexpr float kForgettingFactor = 0.05f; float mean = std::accumulate(audio.begin(), audio.end(), 0.0f) * @@ -34,8 +33,8 @@ DcLevelsEstimator::DcLevelsEstimator(size_t num_samples_per_channel) dc_levels_.fill(0.0f); } -void DcLevelsEstimator::Update(ArrayView channel0, - ArrayView channel1) { +void DcLevelsEstimator::Update(std::span channel0, + std::span channel1) { UpdateDcEstimate(one_by_num_samples_per_channel_, channel0, dc_levels_[0]); UpdateDcEstimate(one_by_num_samples_per_channel_, channel1, dc_levels_[1]); } diff --git a/modules/audio_processing/capture_mixer/dc_levels_estimator.h b/modules/audio_processing/capture_mixer/dc_levels_estimator.h index 8ab00ad62de..b7181feef55 100644 --- a/modules/audio_processing/capture_mixer/dc_levels_estimator.h +++ b/modules/audio_processing/capture_mixer/dc_levels_estimator.h @@ -13,8 +13,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -30,10 +29,10 @@ class DcLevelsEstimator { // Updates the DC level estimates. // `channel0` and `channel1` contain the samples of the two channels. - void Update(ArrayView channel0, ArrayView channel1); + void Update(std::span channel0, std::span channel1); // Returns the current DC level estimates for the two channels. - ArrayView GetLevels() const { return dc_levels_; } + std::span GetLevels() const { return dc_levels_; } private: const float one_by_num_samples_per_channel_; diff --git a/modules/audio_processing/capture_mixer/dc_levels_estimator_unittest.cc b/modules/audio_processing/capture_mixer/dc_levels_estimator_unittest.cc index e6676f4199b..ad5d8e4385b 100644 --- a/modules/audio_processing/capture_mixer/dc_levels_estimator_unittest.cc +++ b/modules/audio_processing/capture_mixer/dc_levels_estimator_unittest.cc @@ -13,10 +13,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -25,8 +25,8 @@ namespace { void PopulateStereoChannelsWithSinusoid(int sample_rate_hz, float dc_level, int& generated_sample_counter, - ArrayView channel0, - ArrayView channel1) { + std::span channel0, + std::span channel1) { constexpr float kPi = std::numbers::pi; constexpr float kApmlitudeScaling = 1000.0f; constexpr float kBaseSinusoidFrequencyHz = 100.0f; @@ -78,7 +78,7 @@ TEST_P(DcLevelsEstimatorParametrizedTest, VerifyEstimates) { estimator.Update(channel0, channel1); } - ArrayView levels = estimator.GetLevels(); + std::span levels = estimator.GetLevels(); for (const float level : levels) { EXPECT_NEAR(level, true_dc_level, diff --git a/modules/audio_processing/capture_mixer/energy_estimator.cc b/modules/audio_processing/capture_mixer/energy_estimator.cc index 8a43798d60d..e471aa61297 100644 --- a/modules/audio_processing/capture_mixer/energy_estimator.cc +++ b/modules/audio_processing/capture_mixer/energy_estimator.cc @@ -9,13 +9,13 @@ */ #include "modules/audio_processing/capture_mixer/energy_estimator.h" -#include "api/array_view.h" +#include namespace webrtc { namespace { -void UpdateChannelEnergyEstimate(ArrayView audio, +void UpdateChannelEnergyEstimate(std::span audio, float dc_level, float& channel_energy_estimate) { float energy = 0.0f; @@ -35,9 +35,9 @@ AverageEnergyEstimator::AverageEnergyEstimator() { average_energy_in_channels_.fill(0.0f); } -void AverageEnergyEstimator::Update(ArrayView channel0, - ArrayView channel1, - ArrayView dc_levels) { +void AverageEnergyEstimator::Update(std::span channel0, + std::span channel1, + std::span dc_levels) { UpdateChannelEnergyEstimate(channel0, dc_levels[0], average_energy_in_channels_[0]); UpdateChannelEnergyEstimate(channel1, dc_levels[1], diff --git a/modules/audio_processing/capture_mixer/energy_estimator.h b/modules/audio_processing/capture_mixer/energy_estimator.h index 3b3fa8f34d5..cc32cb147a2 100644 --- a/modules/audio_processing/capture_mixer/energy_estimator.h +++ b/modules/audio_processing/capture_mixer/energy_estimator.h @@ -13,8 +13,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -31,12 +30,12 @@ class AverageEnergyEstimator { // `channel0` and `channel1` contain the samples of the two channels. // `dc_levels` contains the estimated DC offsets for the two channels, which // are subtracted from the samples before energy calculation. - void Update(ArrayView channel0, - ArrayView channel1, - ArrayView dc_levels); + void Update(std::span channel0, + std::span channel1, + std::span dc_levels); // Returns the current average energy estimates for the two channels. - ArrayView GetChannelEnergies() const { + std::span GetChannelEnergies() const { return average_energy_in_channels_; } diff --git a/modules/audio_processing/capture_mixer/energy_estimator_unittest.cc b/modules/audio_processing/capture_mixer/energy_estimator_unittest.cc index 715f700a5b4..31b0c3de602 100644 --- a/modules/audio_processing/capture_mixer/energy_estimator_unittest.cc +++ b/modules/audio_processing/capture_mixer/energy_estimator_unittest.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -63,7 +63,7 @@ TEST_P(AverageEnergyEstimatorParametrizedTest, VerifyEstimates) { estimator.Update(channel0, channel1, dc_levels); } - ArrayView energies = estimator.GetChannelEnergies(); + std::span energies = estimator.GetChannelEnergies(); constexpr float kToleranceError = 0.0001f; const float expected_energy_channel_0 = diff --git a/modules/audio_processing/capture_mixer/remixing_logic.cc b/modules/audio_processing/capture_mixer/remixing_logic.cc index d490fb5b83d..d96bd633758 100644 --- a/modules/audio_processing/capture_mixer/remixing_logic.cc +++ b/modules/audio_processing/capture_mixer/remixing_logic.cc @@ -11,8 +11,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/capture_mixer/channel_content_remixer.h" #include "rtc_base/checks.h" @@ -33,7 +33,7 @@ bool ChoiceOfChannelMatchesSingleChannelMixing(int channel, } bool EnoughContentForUpdatingMixing( - ArrayView num_frames_since_activity) { + std::span num_frames_since_activity) { const bool channel0_inactive = num_frames_since_activity[0] > kInactivityThresholdFrames; const bool channel1_inactive = @@ -44,8 +44,8 @@ bool EnoughContentForUpdatingMixing( bool SingleSilentChannelDetected( size_t num_samples_per_channel, - ArrayView average_energies, - ArrayView num_frames_since_activity) { + std::span average_energies, + std::span num_frames_since_activity) { RTC_DCHECK(EnoughContentForUpdatingMixing(num_frames_since_activity)); const bool channel0_inactive = @@ -72,7 +72,7 @@ bool SingleSilentChannelDetected( } std::optional IdentifyLargelyImbalancedChannel( - ArrayView average_energies) { + std::span average_energies) { constexpr float kEnergyRatioThreshold = 50.0f; const float& energy0 = average_energies[0]; const float& energy1 = average_energies[1]; @@ -87,8 +87,8 @@ std::optional IdentifyLargelyImbalancedChannel( } std::optional IdentifyModerateImbalancedAndSaturatedChannel( - ArrayView average_energies, - ArrayView saturation_factors) { + std::span average_energies, + std::span saturation_factors) { constexpr float kEnergyRatioModerateThreshold = 4.0f; constexpr float kSignificantSaturationThreshold = 0.8f; constexpr float kNoSaturationThreshold = 0.1f; @@ -127,9 +127,9 @@ RemixingLogic::RemixingLogic(size_t num_samples_per_channel, : settings_(settings), num_samples_per_channel_(num_samples_per_channel) {} StereoMixingVariant RemixingLogic::SelectStereoChannelMixing( - ArrayView average_energies, - ArrayView num_frames_since_activity, - ArrayView saturation_factors) { + std::span average_energies, + std::span num_frames_since_activity, + std::span saturation_factors) { // Only update the mixing when there is sufficient audio activity. if (!EnoughContentForUpdatingMixing(num_frames_since_activity)) { return mixing_; @@ -173,8 +173,8 @@ StereoMixingVariant RemixingLogic::SelectStereoChannelMixing( } bool RemixingLogic::HandleAnySilentChannels( - ArrayView average_energies, - ArrayView num_frames_since_activity) { + std::span average_energies, + std::span num_frames_since_activity) { RTC_DCHECK(mode_ != Mode::kSilentChannel || mixing_ == StereoMixingVariant::kUseAverage); @@ -209,8 +209,8 @@ bool RemixingLogic::HandleAnySilentChannels( } bool RemixingLogic::HandleAnyImbalancedAndSaturatedChannels( - ArrayView average_energies, - ArrayView saturation_factors) { + std::span average_energies, + std::span saturation_factors) { RTC_DCHECK(mode_ != Mode::kSaturatedChannel || (mixing_ == StereoMixingVariant::kUseChannel0 || mixing_ == StereoMixingVariant::kUseChannel1)); @@ -257,7 +257,7 @@ bool RemixingLogic::HandleAnyImbalancedAndSaturatedChannels( } bool RemixingLogic::HandleAnyLargelyImbalancedChannels( - ArrayView average_energies) { + std::span average_energies) { RTC_DCHECK(mode_ != Mode::kImbalancedChannels || (mixing_ == StereoMixingVariant::kUseChannel0 || mixing_ == StereoMixingVariant::kUseChannel1)); diff --git a/modules/audio_processing/capture_mixer/remixing_logic.h b/modules/audio_processing/capture_mixer/remixing_logic.h index 4104fc7464b..d5653c9a11f 100644 --- a/modules/audio_processing/capture_mixer/remixing_logic.h +++ b/modules/audio_processing/capture_mixer/remixing_logic.h @@ -12,7 +12,8 @@ #include -#include "api/array_view.h" +#include + #include "modules/audio_processing/capture_mixer/channel_content_remixer.h" namespace webrtc { @@ -48,29 +49,29 @@ class RemixingLogic { // active. `saturation_factors`: Saturation measure for each channel. Returns // the chosen StereoMixingVariant. StereoMixingVariant SelectStereoChannelMixing( - ArrayView average_energies, - ArrayView num_frames_since_activity, - ArrayView saturation_factors); + std::span average_energies, + std::span num_frames_since_activity, + std::span saturation_factors); private: // Checks if any channel is silent and updates the mode and mixing variant // accordingly. Returns true if a mode change occurred. bool HandleAnySilentChannels( - ArrayView average_energies, - ArrayView num_frames_since_activity); + std::span average_energies, + std::span num_frames_since_activity); // Checks for channels that are moderately imbalanced and have differing // saturation levels, updating mode and mixing variant to favor the less // saturated channel. Returns true if a mode change occurred. bool HandleAnyImbalancedAndSaturatedChannels( - ArrayView average_energies, - ArrayView saturation_factors); + std::span average_energies, + std::span saturation_factors); // Checks for channels with a large energy imbalance and updates mode and // mixing variant to favor the louder channel. Returns true if a mode change // occurred. bool HandleAnyLargelyImbalancedChannels( - ArrayView average_energies); + std::span average_energies); // Represents the current state of the remixing logic. enum class Mode { diff --git a/modules/audio_processing/capture_mixer/saturation_estimator.cc b/modules/audio_processing/capture_mixer/saturation_estimator.cc index 4c6df83b722..fffcd122d70 100644 --- a/modules/audio_processing/capture_mixer/saturation_estimator.cc +++ b/modules/audio_processing/capture_mixer/saturation_estimator.cc @@ -12,15 +12,14 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { namespace { void AnalyzeChannel(float one_by_num_samples_per_channel, - ArrayView audio, + std::span audio, float dc_level, int& num_frames_since_activity, float& saturation_factors) { @@ -59,9 +58,9 @@ SaturationEstimator::SaturationEstimator(size_t num_samples_per_channel) saturation_factors_.fill(0.0f); } -void SaturationEstimator::Update(ArrayView channel0, - ArrayView channel1, - ArrayView dc_levels) { +void SaturationEstimator::Update(std::span channel0, + std::span channel1, + std::span dc_levels) { AnalyzeChannel(one_by_num_samples_per_channel_, channel0, dc_levels[0], num_frames_since_activity_[0], saturation_factors_[0]); AnalyzeChannel(one_by_num_samples_per_channel_, channel1, dc_levels[1], diff --git a/modules/audio_processing/capture_mixer/saturation_estimator.h b/modules/audio_processing/capture_mixer/saturation_estimator.h index d1cd3847bcc..87d8cb98828 100644 --- a/modules/audio_processing/capture_mixer/saturation_estimator.h +++ b/modules/audio_processing/capture_mixer/saturation_estimator.h @@ -13,8 +13,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -32,13 +31,13 @@ class SaturationEstimator { // `channel0` and `channel1` contain the samples of the two channels. // `dc_levels` contains the estimated DC offsets for the two channels, which // are subtracted from the samples before saturation calculation. - void Update(ArrayView channel0, - ArrayView channel1, - ArrayView dc_levels); + void Update(std::span channel0, + std::span channel1, + std::span dc_levels); // Returns the number of frames since the last activity was detected in each // of the channels. - ArrayView GetNumFramesSinceActivity() const { + std::span GetNumFramesSinceActivity() const { return num_frames_since_activity_; } @@ -46,7 +45,7 @@ class SaturationEstimator { // saturation factor is a value between 0 and 1, where 1 means that the signal // has recently been fully saturated and 0 means that no saturation has been // observed in the resent past. - ArrayView GetSaturationFactors() const { + std::span GetSaturationFactors() const { return saturation_factors_; } diff --git a/modules/audio_processing/capture_mixer/saturation_estimator_unittest.cc b/modules/audio_processing/capture_mixer/saturation_estimator_unittest.cc index 24c5a386d9f..682486ff884 100644 --- a/modules/audio_processing/capture_mixer/saturation_estimator_unittest.cc +++ b/modules/audio_processing/capture_mixer/saturation_estimator_unittest.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -56,7 +56,7 @@ TEST_P(SaturationDetectorParametrizedTest, VerifyLowValueActivityDetection) { constexpr int kNumFramesToAnalyze = 10; for (int k = 0; k < kNumFramesToAnalyze; ++k) { estimator.Update(channel, channel, dc_levels); - ArrayView num_frames_since_activity = + std::span num_frames_since_activity = estimator.GetNumFramesSinceActivity(); EXPECT_EQ(num_frames_since_activity[0], k + 1); EXPECT_EQ(num_frames_since_activity[1], k + 1); @@ -81,7 +81,7 @@ TEST_P(SaturationDetectorParametrizedTest, constexpr int kNumFramesToAnalyze = 10; for (int k = 0; k < kNumFramesToAnalyze; ++k) { estimator.Update(channel, channel, dc_levels); - ArrayView num_frames_since_activity = + std::span num_frames_since_activity = estimator.GetNumFramesSinceActivity(); EXPECT_EQ(num_frames_since_activity[0], 0); EXPECT_EQ(num_frames_since_activity[1], 0); @@ -108,7 +108,7 @@ TEST_P(SaturationDetectorParametrizedTest, estimator.Update(channel, channel, dc_levels); } - ArrayView num_frames_since_activity = + std::span num_frames_since_activity = estimator.GetNumFramesSinceActivity(); EXPECT_EQ(num_frames_since_activity[0], 1); EXPECT_EQ(num_frames_since_activity[1], 1); @@ -153,7 +153,7 @@ TEST_P(SaturationDetectorParametrizedTest, constexpr int kNumFramesToAnalyze = 10; for (int k = 0; k < kNumFramesToAnalyze; ++k) { estimator.Update(channel, channel, dc_levels); - ArrayView saturation_factors = + std::span saturation_factors = estimator.GetSaturationFactors(); EXPECT_EQ(saturation_factors[0], 0.0f); EXPECT_EQ(saturation_factors[1], 0.0f); @@ -179,7 +179,7 @@ TEST_P(SaturationDetectorParametrizedTest, std::vector previous_factors(2, 0.0f); for (int k = 0; k < kNumFramesToAnalyze; ++k) { estimator.Update(channel, channel, dc_levels); - ArrayView saturation_factors = + std::span saturation_factors = estimator.GetSaturationFactors(); EXPECT_GT(saturation_factors[0], 0.0f); EXPECT_GT(saturation_factors[1], 0.0f); @@ -209,7 +209,7 @@ TEST_P(SaturationDetectorParametrizedTest, for (int k = 0; k < kNumFramesToAnalyze; ++k) { estimator.Update(channel, channel, dc_levels); } - ArrayView saturation_factors = + std::span saturation_factors = estimator.GetSaturationFactors(); EXPECT_GT(saturation_factors[0], 0.99f); EXPECT_GT(saturation_factors[1], 0.99f); @@ -233,7 +233,7 @@ TEST_P(SaturationDetectorParametrizedTest, VerifyDecayingSaturationFactor) { num_samples_per_channel, dc_level + sign * kSampleValueSaturation); estimator.Update(channel, channel, dc_levels); } - ArrayView saturation_factors = + std::span saturation_factors = estimator.GetSaturationFactors(); EXPECT_GT(saturation_factors[0], 0.0f); EXPECT_GT(saturation_factors[1], 0.0f); diff --git a/modules/audio_processing/debug.proto b/modules/audio_processing/debug.proto index cc5efbc73ca..e4f0fe42570 100644 --- a/modules/audio_processing/debug.proto +++ b/modules/audio_processing/debug.proto @@ -1,12 +1,14 @@ syntax = "proto2"; -option optimize_for = LITE_RUNTIME; + package webrtc.audioproc; +option optimize_for = LITE_RUNTIME; + // Contains the format of input/output/reverse audio. An Init message is added // when any of the fields are changed. message Init { optional int32 sample_rate = 1; - optional int32 device_sample_rate = 2 [deprecated=true]; + optional int32 device_sample_rate = 2 [deprecated = true]; optional int32 num_input_channels = 3; optional int32 num_output_channels = 4; optional int32 num_reverse_channels = 5; @@ -47,16 +49,14 @@ message Stream { // Contains the configurations of various APM component. A Config message is // added when any of the fields are changed. message Config { + reserved 6 to 8; // AECm + // Acoustic echo canceler. optional bool aec_enabled = 1; optional bool aec_delay_agnostic_enabled = 2; optional bool aec_drift_compensation_enabled = 3; optional bool aec_extended_filter_enabled = 4; optional int32 aec_suppression_level = 5; - // Mobile AEC. - optional bool aecm_enabled = 6; - optional bool aecm_comfort_noise_enabled = 7 [deprecated = true]; - optional int32 aecm_routing_mode = 8 [deprecated = true]; // Automatic gain controller. optional bool agc_enabled = 9; optional int32 agc_mode = 10; @@ -77,6 +77,11 @@ message Config { optional bool pre_amplifier_enabled = 19; optional float pre_amplifier_fixed_gain_factor = 20; + // General string version of webrtc::AudioProcessing::Config. Partially + // overlaps with other fields in this Config message, but is more likely to be + // up-to-date. + optional string api_config_string = 21; + // Next field number 21. } diff --git a/modules/audio_processing/echo_control_mobile_bit_exact_unittest.cc b/modules/audio_processing/echo_control_mobile_bit_exact_unittest.cc deleted file mode 100644 index 3b495a0cd60..00000000000 --- a/modules/audio_processing/echo_control_mobile_bit_exact_unittest.cc +++ /dev/null @@ -1,226 +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 -#include -#include - -#include "api/array_view.h" -#include "api/audio/audio_processing.h" -#include "modules/audio_coding/neteq/tools/input_audio_file.h" -#include "modules/audio_processing/audio_buffer.h" -#include "modules/audio_processing/echo_control_mobile_impl.h" -#include "modules/audio_processing/test/audio_buffer_tools.h" -#include "modules/audio_processing/test/bitexactness_tools.h" -#include "rtc_base/checks.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -// TODO(peah): Increase the number of frames to proces when the issue of -// non repeatable test results have been found. -constexpr int kNumFramesToProcess = 200; - -void SetupComponent(int sample_rate_hz, - EchoControlMobileImpl::RoutingMode routing_mode, - bool comfort_noise_enabled, - EchoControlMobileImpl* echo_control_mobile) { - echo_control_mobile->Initialize( - sample_rate_hz > 16000 ? 16000 : sample_rate_hz, 1, 1); - echo_control_mobile->set_routing_mode(routing_mode); - echo_control_mobile->enable_comfort_noise(comfort_noise_enabled); -} - -void ProcessOneFrame(int sample_rate_hz, - int stream_delay_ms, - AudioBuffer* render_audio_buffer, - AudioBuffer* capture_audio_buffer, - EchoControlMobileImpl* echo_control_mobile) { - if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { - render_audio_buffer->SplitIntoFrequencyBands(); - capture_audio_buffer->SplitIntoFrequencyBands(); - } - - std::vector render_audio; - EchoControlMobileImpl::PackRenderAudioBuffer( - render_audio_buffer, 1, render_audio_buffer->num_channels(), - &render_audio); - echo_control_mobile->ProcessRenderAudio(render_audio); - - echo_control_mobile->ProcessCaptureAudio(capture_audio_buffer, - stream_delay_ms); - - if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { - capture_audio_buffer->MergeFrequencyBands(); - } -} - -void RunBitexactnessTest(int sample_rate_hz, - size_t num_channels, - int stream_delay_ms, - EchoControlMobileImpl::RoutingMode routing_mode, - bool comfort_noise_enabled, - const ArrayView& output_reference) { - EchoControlMobileImpl echo_control_mobile; - SetupComponent(sample_rate_hz, routing_mode, comfort_noise_enabled, - &echo_control_mobile); - - const int samples_per_channel = CheckedDivExact(sample_rate_hz, 100); - const StreamConfig render_config(sample_rate_hz, num_channels); - AudioBuffer render_buffer( - render_config.sample_rate_hz(), render_config.num_channels(), - render_config.sample_rate_hz(), 1, render_config.sample_rate_hz(), 1); - test::InputAudioFile render_file( - test::GetApmRenderTestVectorFileName(sample_rate_hz)); - std::vector render_input(samples_per_channel * num_channels); - - const StreamConfig capture_config(sample_rate_hz, num_channels); - AudioBuffer capture_buffer( - capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), 1, capture_config.sample_rate_hz(), 1); - test::InputAudioFile capture_file( - test::GetApmCaptureTestVectorFileName(sample_rate_hz)); - std::vector capture_input(samples_per_channel * num_channels); - - for (int frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) { - ReadFloatSamplesFromStereoFile(samples_per_channel, num_channels, - &render_file, render_input); - ReadFloatSamplesFromStereoFile(samples_per_channel, num_channels, - &capture_file, capture_input); - - test::CopyVectorToAudioBuffer(render_config, render_input, &render_buffer); - test::CopyVectorToAudioBuffer(capture_config, capture_input, - &capture_buffer); - - ProcessOneFrame(sample_rate_hz, stream_delay_ms, &render_buffer, - &capture_buffer, &echo_control_mobile); - } - - // Extract and verify the test results. - std::vector capture_output; - test::ExtractVectorFromAudioBuffer(capture_config, &capture_buffer, - &capture_output); - - // Compare the output with the reference. Only the first values of the output - // from last frame processed are compared in order not having to specify all - // preceeding frames as testvectors. As the algorithm being tested has a - // memory, testing only the last frame implicitly also tests the preceeding - // frames. - const float kElementErrorBound = 1.0f / 32768.0f; - EXPECT_TRUE(test::VerifyDeinterleavedArray( - capture_config.num_frames(), capture_config.num_channels(), - output_reference, capture_output, kElementErrorBound)); -} - -} // namespace - -// TODO(peah): Renable once the integer overflow issue in aecm_core.c:932:69 -// has been solved. -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono8kHz_LoudSpeakerPhone_CngOn_StreamDelay0) { - const float kOutputReference[] = {0.005280f, 0.002380f, -0.000427f}; - - RunBitexactnessTest(8000, 1, 0, - EchoControlMobileImpl::RoutingMode::kLoudSpeakerphone, - true, kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono16kHz_LoudSpeakerPhone_CngOn_StreamDelay0) { - const float kOutputReference[] = {0.003601f, 0.002991f, 0.001923f}; - RunBitexactnessTest(16000, 1, 0, - EchoControlMobileImpl::RoutingMode::kLoudSpeakerphone, - true, kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono32kHz_LoudSpeakerPhone_CngOn_StreamDelay0) { - const float kOutputReference[] = {0.002258f, 0.002899f, 0.003906f}; - - RunBitexactnessTest(32000, 1, 0, - EchoControlMobileImpl::RoutingMode::kLoudSpeakerphone, - true, kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono48kHz_LoudSpeakerPhone_CngOn_StreamDelay0) { - const float kOutputReference[] = {-0.000046f, 0.000041f, 0.000249f}; - - RunBitexactnessTest(48000, 1, 0, - EchoControlMobileImpl::RoutingMode::kLoudSpeakerphone, - true, kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono16kHz_LoudSpeakerPhone_CngOff_StreamDelay0) { - const float kOutputReference[] = {0.000000f, 0.000000f, 0.000000f}; - - RunBitexactnessTest(16000, 1, 0, - EchoControlMobileImpl::RoutingMode::kLoudSpeakerphone, - false, kOutputReference); -} - -// TODO(peah): Renable once the integer overflow issue in aecm_core.c:932:69 -// has been solved. -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono16kHz_LoudSpeakerPhone_CngOn_StreamDelay5) { - const float kOutputReference[] = {0.003693f, 0.002930f, 0.001801f}; - - RunBitexactnessTest(16000, 1, 5, - EchoControlMobileImpl::RoutingMode::kLoudSpeakerphone, - true, kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - Mono16kHz_LoudSpeakerPhone_CngOn_StreamDelay10) { - const float kOutputReference[] = {-0.002380f, -0.002533f, -0.002563f}; - - RunBitexactnessTest(16000, 1, 10, - EchoControlMobileImpl::RoutingMode::kLoudSpeakerphone, - true, kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono16kHz_QuietEarpieceOrHeadset_CngOn_StreamDelay0) { - const float kOutputReference[] = {0.000397f, 0.000000f, -0.000305f}; - - RunBitexactnessTest( - 16000, 1, 0, EchoControlMobileImpl::RoutingMode::kQuietEarpieceOrHeadset, - true, kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono16kHz_Earpiece_CngOn_StreamDelay0) { - const float kOutputReference[] = {0.002167f, 0.001617f, 0.001038f}; - - RunBitexactnessTest(16000, 1, 0, - EchoControlMobileImpl::RoutingMode::kEarpiece, true, - kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono16kHz_LoudEarpiece_CngOn_StreamDelay0) { - const float kOutputReference[] = {0.003540f, 0.002899f, 0.001862f}; - - RunBitexactnessTest(16000, 1, 0, - EchoControlMobileImpl::RoutingMode::kLoudEarpiece, true, - kOutputReference); -} - -TEST(EchoControlMobileBitExactnessTest, - DISABLED_Mono16kHz_SpeakerPhone_CngOn_StreamDelay0) { - const float kOutputReference[] = {0.003632f, 0.003052f, 0.001984f}; - - RunBitexactnessTest(16000, 1, 0, - EchoControlMobileImpl::RoutingMode::kSpeakerphone, true, - kOutputReference); -} - -} // namespace webrtc diff --git a/modules/audio_processing/echo_control_mobile_impl.cc b/modules/audio_processing/echo_control_mobile_impl.cc deleted file mode 100644 index edfb57b7426..00000000000 --- a/modules/audio_processing/echo_control_mobile_impl.cc +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/audio_processing/echo_control_mobile_impl.h" - -#include -#include -#include -#include - -#include "api/array_view.h" -#include "api/audio/audio_processing.h" -#include "common_audio/include/audio_util.h" -#include "modules/audio_processing/aecm/echo_control_mobile.h" -#include "modules/audio_processing/audio_buffer.h" -#include "rtc_base/checks.h" - -namespace webrtc { - -namespace { -int16_t MapSetting(EchoControlMobileImpl::RoutingMode mode) { - switch (mode) { - case EchoControlMobileImpl::kQuietEarpieceOrHeadset: - return 0; - case EchoControlMobileImpl::kEarpiece: - return 1; - case EchoControlMobileImpl::kLoudEarpiece: - return 2; - case EchoControlMobileImpl::kSpeakerphone: - return 3; - case EchoControlMobileImpl::kLoudSpeakerphone: - return 4; - } - RTC_DCHECK_NOTREACHED(); - return -1; -} - -AudioProcessing::Error MapError(int err) { - switch (err) { - case AECM_UNSUPPORTED_FUNCTION_ERROR: - return AudioProcessing::kUnsupportedFunctionError; - case AECM_NULL_POINTER_ERROR: - return AudioProcessing::kNullPointerError; - case AECM_BAD_PARAMETER_ERROR: - return AudioProcessing::kBadParameterError; - case AECM_BAD_PARAMETER_WARNING: - return AudioProcessing::kBadStreamParameterWarning; - default: - // AECM_UNSPECIFIED_ERROR - // AECM_UNINITIALIZED_ERROR - return AudioProcessing::kUnspecifiedError; - } -} - -} // namespace - -struct EchoControlMobileImpl::StreamProperties { - StreamProperties() = delete; - StreamProperties(int sample_rate_hz, - size_t num_reverse_channels, - size_t num_output_channels) - : sample_rate_hz(sample_rate_hz), - num_reverse_channels(num_reverse_channels), - num_output_channels(num_output_channels) {} - - int sample_rate_hz; - size_t num_reverse_channels; - size_t num_output_channels; -}; - -class EchoControlMobileImpl::Canceller { - public: - Canceller() { - state_ = WebRtcAecm_Create(); - RTC_CHECK(state_); - } - - ~Canceller() { - RTC_DCHECK(state_); - WebRtcAecm_Free(state_); - } - - Canceller(const Canceller&) = delete; - Canceller& operator=(const Canceller&) = delete; - - void* state() { - RTC_DCHECK(state_); - return state_; - } - - void Initialize(int sample_rate_hz) { - RTC_DCHECK(state_); - int error = WebRtcAecm_Init(state_, sample_rate_hz); - RTC_DCHECK_EQ(AudioProcessing::kNoError, error); - } - - private: - void* state_; -}; - -EchoControlMobileImpl::EchoControlMobileImpl() - : routing_mode_(kSpeakerphone), comfort_noise_enabled_(false) {} - -EchoControlMobileImpl::~EchoControlMobileImpl() {} - -void EchoControlMobileImpl::ProcessRenderAudio( - ArrayView packed_render_audio) { - RTC_DCHECK(stream_properties_); - - size_t buffer_index = 0; - size_t num_frames_per_band = - packed_render_audio.size() / (stream_properties_->num_output_channels * - stream_properties_->num_reverse_channels); - - for (auto& canceller : cancellers_) { - WebRtcAecm_BufferFarend(canceller->state(), - &packed_render_audio[buffer_index], - num_frames_per_band); - - buffer_index += num_frames_per_band; - } -} - -void EchoControlMobileImpl::PackRenderAudioBuffer( - const AudioBuffer* audio, - size_t num_output_channels, - size_t num_channels, - std::vector* packed_buffer) { - RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, - audio->num_frames_per_band()); - RTC_DCHECK_EQ(num_channels, audio->num_channels()); - - // The ordering convention must be followed to pass to the correct AECM. - packed_buffer->clear(); - int render_channel = 0; - for (size_t i = 0; i < num_output_channels; i++) { - for (size_t j = 0; j < audio->num_channels(); j++) { - std::array data_to_buffer; - FloatS16ToS16(audio->split_bands_const(render_channel)[kBand0To8kHz], - audio->num_frames_per_band(), data_to_buffer.data()); - - // Buffer the samples in the render queue. - packed_buffer->insert( - packed_buffer->end(), data_to_buffer.data(), - data_to_buffer.data() + audio->num_frames_per_band()); - render_channel = (render_channel + 1) % audio->num_channels(); - } - } -} - -size_t EchoControlMobileImpl::NumCancellersRequired( - size_t num_output_channels, - size_t num_reverse_channels) { - return num_output_channels * num_reverse_channels; -} - -int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio, - int stream_delay_ms) { - RTC_DCHECK(stream_properties_); - RTC_DCHECK_GE(160, audio->num_frames_per_band()); - RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_output_channels); - RTC_DCHECK_GE(cancellers_.size(), stream_properties_->num_reverse_channels * - audio->num_channels()); - - int err = AudioProcessing::kNoError; - - // The ordering convention must be followed to pass to the correct AECM. - size_t handle_index = 0; - for (size_t capture = 0; capture < audio->num_channels(); ++capture) { - // TODO(ajm): improve how this works, possibly inside AECM. - // This is kind of hacked up. - RTC_DCHECK_LT(capture, low_pass_reference_.size()); - const int16_t* noisy = - reference_copied_ ? low_pass_reference_[capture].data() : nullptr; - - RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, - audio->num_frames_per_band()); - - std::array split_bands_data; - int16_t* split_bands = split_bands_data.data(); - const int16_t* clean = split_bands_data.data(); - if (audio->split_bands(capture)[kBand0To8kHz]) { - FloatS16ToS16(audio->split_bands(capture)[kBand0To8kHz], - audio->num_frames_per_band(), split_bands_data.data()); - } else { - clean = nullptr; - split_bands = nullptr; - } - - if (noisy == nullptr) { - noisy = clean; - clean = nullptr; - } - for (size_t render = 0; render < stream_properties_->num_reverse_channels; - ++render) { - err = WebRtcAecm_Process(cancellers_[handle_index]->state(), noisy, clean, - split_bands, audio->num_frames_per_band(), - stream_delay_ms); - - if (split_bands) { - S16ToFloatS16(split_bands, audio->num_frames_per_band(), - audio->split_bands(capture)[kBand0To8kHz]); - } - - if (err != AudioProcessing::kNoError) { - return MapError(err); - } - - ++handle_index; - } - for (size_t band = 1u; band < audio->num_bands(); ++band) { - memset(audio->split_bands_f(capture)[band], 0, - audio->num_frames_per_band() * - sizeof(audio->split_bands_f(capture)[band][0])); - } - } - return AudioProcessing::kNoError; -} - -int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { - if (MapSetting(mode) == -1) { - return AudioProcessing::kBadParameterError; - } - routing_mode_ = mode; - return Configure(); -} - -EchoControlMobileImpl::RoutingMode EchoControlMobileImpl::routing_mode() const { - return routing_mode_; -} - -int EchoControlMobileImpl::enable_comfort_noise(bool enable) { - comfort_noise_enabled_ = enable; - return Configure(); -} - -bool EchoControlMobileImpl::is_comfort_noise_enabled() const { - return comfort_noise_enabled_; -} - -void EchoControlMobileImpl::Initialize(int sample_rate_hz, - size_t num_reverse_channels, - size_t num_output_channels) { - low_pass_reference_.resize(num_output_channels); - for (auto& reference : low_pass_reference_) { - reference.fill(0); - } - - stream_properties_.reset(new StreamProperties( - sample_rate_hz, num_reverse_channels, num_output_channels)); - - // AECM only supports 16 kHz or lower sample rates. - RTC_DCHECK_LE(stream_properties_->sample_rate_hz, - AudioProcessing::kSampleRate16kHz); - - cancellers_.resize( - NumCancellersRequired(stream_properties_->num_output_channels, - stream_properties_->num_reverse_channels)); - - for (auto& canceller : cancellers_) { - if (!canceller) { - canceller.reset(new Canceller()); - } - canceller->Initialize(sample_rate_hz); - } - Configure(); -} - -int EchoControlMobileImpl::Configure() { - AecmConfig config; - config.cngMode = comfort_noise_enabled_; - config.echoMode = MapSetting(routing_mode_); - int error = AudioProcessing::kNoError; - for (auto& canceller : cancellers_) { - int handle_error = WebRtcAecm_set_config(canceller->state(), config); - if (handle_error != AudioProcessing::kNoError) { - error = handle_error; - } - } - return error; -} - -} // namespace webrtc diff --git a/modules/audio_processing/echo_control_mobile_impl.h b/modules/audio_processing/echo_control_mobile_impl.h deleted file mode 100644 index 696ac6ee2f9..00000000000 --- a/modules/audio_processing/echo_control_mobile_impl.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ -#define MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ - -#include -#include -#include -#include -#include - -#include "api/array_view.h" - -namespace webrtc { - -class AudioBuffer; - -// The acoustic echo control for mobile (AECM) component is a low complexity -// robust option intended for use on mobile devices. -class EchoControlMobileImpl { - public: - EchoControlMobileImpl(); - - ~EchoControlMobileImpl(); - - // Recommended settings for particular audio routes. In general, the louder - // the echo is expected to be, the higher this value should be set. The - // preferred setting may vary from device to device. - enum RoutingMode { - kQuietEarpieceOrHeadset, - kEarpiece, - kLoudEarpiece, - kSpeakerphone, - kLoudSpeakerphone - }; - - // Sets echo control appropriate for the audio routing `mode` on the device. - // It can and should be updated during a call if the audio routing changes. - int set_routing_mode(RoutingMode mode); - RoutingMode routing_mode() const; - - // Comfort noise replaces suppressed background noise to maintain a - // consistent signal level. - int enable_comfort_noise(bool enable); - bool is_comfort_noise_enabled() const; - - void ProcessRenderAudio(ArrayView packed_render_audio); - int ProcessCaptureAudio(AudioBuffer* audio, int stream_delay_ms); - - void Initialize(int sample_rate_hz, - size_t num_reverse_channels, - size_t num_output_channels); - - static void PackRenderAudioBuffer(const AudioBuffer* audio, - size_t num_output_channels, - size_t num_channels, - std::vector* packed_buffer); - - static size_t NumCancellersRequired(size_t num_output_channels, - size_t num_reverse_channels); - - private: - class Canceller; - struct StreamProperties; - - int Configure(); - - RoutingMode routing_mode_; - bool comfort_noise_enabled_; - - std::vector> cancellers_; - std::unique_ptr stream_properties_; - std::vector> low_pass_reference_; - bool reference_copied_ = false; -}; -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ diff --git a/modules/audio_processing/echo_control_mobile_unittest.cc b/modules/audio_processing/echo_control_mobile_unittest.cc deleted file mode 100644 index d0063be556a..00000000000 --- a/modules/audio_processing/echo_control_mobile_unittest.cc +++ /dev/null @@ -1,42 +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 - -#include "api/audio/audio_processing.h" -#include "modules/audio_processing/echo_control_mobile_impl.h" -#include "test/gtest.h" - -namespace webrtc { -TEST(EchoControlMobileTest, InterfaceConfiguration) { - EchoControlMobileImpl aecm; - aecm.Initialize(AudioProcessing::kSampleRate16kHz, 2, 2); - - // Toggle routing modes - std::array routing_modes = { - EchoControlMobileImpl::kQuietEarpieceOrHeadset, - EchoControlMobileImpl::kEarpiece, - EchoControlMobileImpl::kLoudEarpiece, - EchoControlMobileImpl::kSpeakerphone, - EchoControlMobileImpl::kLoudSpeakerphone, - }; - for (auto mode : routing_modes) { - EXPECT_EQ(0, aecm.set_routing_mode(mode)); - EXPECT_EQ(mode, aecm.routing_mode()); - } - - // Turn comfort noise off/on - EXPECT_EQ(0, aecm.enable_comfort_noise(false)); - EXPECT_FALSE(aecm.is_comfort_noise_enabled()); - EXPECT_EQ(0, aecm.enable_comfort_noise(true)); - EXPECT_TRUE(aecm.is_comfort_noise_enabled()); -} - -} // namespace webrtc diff --git a/modules/audio_processing/gain_control_impl.cc b/modules/audio_processing/gain_control_impl.cc index 9b29e7ba6b5..7e40e9fb256 100644 --- a/modules/audio_processing/gain_control_impl.cc +++ b/modules/audio_processing/gain_control_impl.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "common_audio/include/audio_util.h" #include "modules/audio_processing/agc/gain_control.h" @@ -112,7 +112,7 @@ GainControlImpl::GainControlImpl() GainControlImpl::~GainControlImpl() = default; void GainControlImpl::ProcessRenderAudio( - ArrayView packed_render_audio) { + std::span packed_render_audio) { for (size_t ch = 0; ch < mono_agcs_.size(); ++ch) { WebRtcAgc_AddFarend(mono_agcs_[ch]->state, packed_render_audio.data(), packed_render_audio.size()); @@ -125,7 +125,7 @@ void GainControlImpl::PackRenderAudioBuffer( RTC_DCHECK_GE(AudioBuffer::kMaxSplitFrameLength, audio.num_frames_per_band()); std::array mixed_16_kHz_render_data; - ArrayView mixed_16_kHz_render(mixed_16_kHz_render_data.data(), + std::span mixed_16_kHz_render(mixed_16_kHz_render_data.data(), audio.num_frames_per_band()); if (audio.num_channels() == 1) { FloatS16ToS16(audio.split_bands_const(0)[kBand0To8kHz], diff --git a/modules/audio_processing/gain_control_impl.h b/modules/audio_processing/gain_control_impl.h index 8a3e94d1e55..cd8c912cfd9 100644 --- a/modules/audio_processing/gain_control_impl.h +++ b/modules/audio_processing/gain_control_impl.h @@ -16,9 +16,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/agc/gain_control.h" namespace webrtc { @@ -34,7 +34,7 @@ class GainControlImpl : public GainControl { ~GainControlImpl() override; - void ProcessRenderAudio(ArrayView packed_render_audio); + void ProcessRenderAudio(std::span packed_render_audio); int AnalyzeCaptureAudio(const AudioBuffer& audio); int ProcessCaptureAudio(AudioBuffer* audio, bool stream_has_echo); diff --git a/modules/audio_processing/gain_control_unittest.cc b/modules/audio_processing/gain_control_unittest.cc index 41123b98e09..1ecb623b89d 100644 --- a/modules/audio_processing/gain_control_unittest.cc +++ b/modules/audio_processing/gain_control_unittest.cc @@ -11,9 +11,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "modules/audio_coding/neteq/tools/input_audio_file.h" #include "modules/audio_processing/audio_buffer.h" @@ -77,7 +77,7 @@ void RunBitExactnessTest(int sample_rate_hz, int analog_level_min, int analog_level_max, int achieved_stream_analog_level_reference, - ArrayView output_reference) { + std::span output_reference) { GainControlImpl gain_controller; SetupComponent(sample_rate_hz, mode, target_level_dbfs, stream_analog_level, compression_gain_db, enable_limiter, analog_level_min, diff --git a/modules/audio_processing/high_pass_filter.cc b/modules/audio_processing/high_pass_filter.cc index 85b64608e87..7c64d613224 100644 --- a/modules/audio_processing/high_pass_filter.cc +++ b/modules/audio_processing/high_pass_filter.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/utility/cascaded_biquad_filter.h" #include "rtc_base/checks.h" @@ -54,23 +54,23 @@ constexpr std::array .a = {-1.9983570340145236f, 0.9984928491805198f}}, }}; -ArrayView ChooseCoefficients( +std::span ChooseCoefficients( int sample_rate_hz) { switch (sample_rate_hz) { case 16000: - return ArrayView( + return std::span( kHighPassFilterCoefficients16kHz); case 32000: - return ArrayView( + return std::span( kHighPassFilterCoefficients32kHz); case 48000: - return ArrayView( + return std::span( kHighPassFilterCoefficients48kHz); default: RTC_DCHECK_NOTREACHED(); } RTC_DCHECK_NOTREACHED(); - return ArrayView( + return std::span( kHighPassFilterCoefficients16kHz); } @@ -92,14 +92,14 @@ void HighPassFilter::Process(AudioBuffer* audio, bool use_split_band_data) { RTC_DCHECK_EQ(filters_.size(), audio->num_channels()); if (use_split_band_data) { for (size_t k = 0; k < audio->num_channels(); ++k) { - ArrayView channel_data = ArrayView( + std::span channel_data = std::span( audio->split_bands(k)[0], audio->num_frames_per_band()); filters_[k]->Process(channel_data); } } else { for (size_t k = 0; k < audio->num_channels(); ++k) { - ArrayView channel_data = - ArrayView(&audio->channels()[k][0], audio->num_frames()); + std::span channel_data = + std::span(&audio->channels()[k][0], audio->num_frames()); filters_[k]->Process(channel_data); } } diff --git a/modules/audio_processing/high_pass_filter_unittest.cc b/modules/audio_processing/high_pass_filter_unittest.cc index 93a56aef97f..76c405189ba 100644 --- a/modules/audio_processing/high_pass_filter_unittest.cc +++ b/modules/audio_processing/high_pass_filter_unittest.cc @@ -11,9 +11,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/test/audio_buffer_tools.h" @@ -124,7 +124,7 @@ void RunBitexactnessTest(int num_channels, // Method for forming a vector out of an array. // TODO(peah): Remove once braced initialization is allowed. -std::vector CreateVector(const ArrayView& array_view) { +std::vector CreateVector(const std::span& array_view) { std::vector v; for (auto value : array_view) { v.push_back(value); @@ -230,8 +230,8 @@ TEST(HighPassFilterAccuracyTest, MonoInitial) { for (bool use_audio_buffer_interface : {true, false}) { RunBitexactnessTest(1, use_audio_buffer_interface, - CreateVector(ArrayView(kReferenceInput)), - CreateVector(ArrayView(kReference))); + CreateVector(std::span(kReferenceInput)), + CreateVector(std::span(kReference))); } } @@ -324,8 +324,8 @@ TEST(HighPassFilterAccuracyTest, MonoConverged) { for (bool use_audio_buffer_interface : {true, false}) { RunBitexactnessTest(1, use_audio_buffer_interface, - CreateVector(ArrayView(kReferenceInput)), - CreateVector(ArrayView(kReference))); + CreateVector(std::span(kReferenceInput)), + CreateVector(std::span(kReference))); } } diff --git a/modules/audio_processing/include/aec_dump.cc b/modules/audio_processing/include/aec_dump.cc index 8f788cb8023..13f1e77a15a 100644 --- a/modules/audio_processing/include/aec_dump.cc +++ b/modules/audio_processing/include/aec_dump.cc @@ -24,9 +24,6 @@ bool InternalAPMConfig::operator==(const InternalAPMConfig& other) const { other.aec_drift_compensation_enabled && aec_extended_filter_enabled == other.aec_extended_filter_enabled && aec_suppression_level == other.aec_suppression_level && - aecm_enabled == other.aecm_enabled && - aecm_comfort_noise_enabled == other.aecm_comfort_noise_enabled && - aecm_routing_mode == other.aecm_routing_mode && agc_enabled == other.agc_enabled && agc_mode == other.agc_mode && agc_limiter_enabled == other.agc_limiter_enabled && hpf_enabled == other.hpf_enabled && ns_enabled == other.ns_enabled && @@ -36,6 +33,7 @@ bool InternalAPMConfig::operator==(const InternalAPMConfig& other) const { pre_amplifier_enabled == other.pre_amplifier_enabled && pre_amplifier_fixed_gain_factor == other.pre_amplifier_fixed_gain_factor && - experiments_description == other.experiments_description; + experiments_description == other.experiments_description && + api_config_string == other.api_config_string; } } // namespace webrtc diff --git a/modules/audio_processing/include/aec_dump.h b/modules/audio_processing/include/aec_dump.h index 4e273d4cde2..72b503663ca 100644 --- a/modules/audio_processing/include/aec_dump.h +++ b/modules/audio_processing/include/aec_dump.h @@ -40,9 +40,6 @@ struct InternalAPMConfig { bool aec_drift_compensation_enabled = false; bool aec_extended_filter_enabled = false; int aec_suppression_level = 0; - bool aecm_enabled = false; - bool aecm_comfort_noise_enabled = false; - int aecm_routing_mode = 0; bool agc_enabled = false; int agc_mode = 0; bool agc_limiter_enabled = false; @@ -54,6 +51,7 @@ struct InternalAPMConfig { bool pre_amplifier_enabled = false; float pre_amplifier_fixed_gain_factor = 1.f; std::string experiments_description = ""; + std::string api_config_string = ""; }; // An interface for recording configuration and input/output streams diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h index 0f736d47ce1..4e34bc18939 100644 --- a/modules/audio_processing/include/mock_audio_processing.h +++ b/modules/audio_processing/include/mock_audio_processing.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include "absl/base/nullability.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "api/audio/audio_processing_statistics.h" #include "api/audio/echo_control.h" @@ -91,11 +91,11 @@ class MockEchoDetector : public EchoDetector { (override)); MOCK_METHOD(void, AnalyzeRenderAudio, - (webrtc::ArrayView render_audio), + (std::span render_audio), (override)); MOCK_METHOD(void, AnalyzeCaptureAudio, - (webrtc::ArrayView capture_audio), + (std::span capture_audio), (override)); MOCK_METHOD(Metrics, GetMetrics, (), (const, override)); }; @@ -156,7 +156,7 @@ class MockAudioProcessing : public AudioProcessing { (override)); MOCK_METHOD(bool, GetLinearAecOutput, - ((webrtc::ArrayView> linear_output)), + ((std::span> linear_output)), (const, override)); MOCK_METHOD(int, set_stream_delay_ms, (int delay), (override)); MOCK_METHOD(int, stream_delay_ms, (), (const, override)); diff --git a/modules/audio_processing/logging/apm_data_dumper.h b/modules/audio_processing/logging/apm_data_dumper.h index 24e5ad913a7..5b3afd133a6 100644 --- a/modules/audio_processing/logging/apm_data_dumper.h +++ b/modules/audio_processing/logging/apm_data_dumper.h @@ -19,8 +19,9 @@ #include #endif +#include + #include "absl/strings/string_view.h" -#include "api/array_view.h" #if WEBRTC_APM_DEBUG_DUMP == 1 #include "common_audio/wav_file.h" #include "rtc_base/checks.h" @@ -123,25 +124,20 @@ class ApmDataDumper { [[maybe_unused]] const double* v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) - return; - - if (recording_activated_) { - FILE* file = GetRawFile(name); - fwrite(v, sizeof(v[0]), v_length, file); - } + DumpRaw(name, std::span(v, v_length), dump_set); #endif } void DumpRaw([[maybe_unused]] absl::string_view name, - [[maybe_unused]] ArrayView v, + [[maybe_unused]] std::span v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) return; if (recording_activated_) { - DumpRaw(name, v.size(), v.data()); + FILE* file = GetRawFile(name); + fwrite(v.data(), sizeof(v[0]), v.size(), file); } #endif } @@ -165,25 +161,20 @@ class ApmDataDumper { [[maybe_unused]] const float* v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) - return; - - if (recording_activated_) { - FILE* file = GetRawFile(name); - fwrite(v, sizeof(v[0]), v_length, file); - } + DumpRaw(name, std::span(v, v_length), dump_set); #endif } void DumpRaw([[maybe_unused]] absl::string_view name, - [[maybe_unused]] ArrayView v, + [[maybe_unused]] std::span v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) return; if (recording_activated_) { - DumpRaw(name, v.size(), v.data()); + FILE* file = GetRawFile(name); + fwrite(v.data(), sizeof(v[0]), v.size(), file); } #endif } @@ -206,28 +197,23 @@ class ApmDataDumper { [[maybe_unused]] const bool* v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) - return; - - if (recording_activated_) { - FILE* file = GetRawFile(name); - for (size_t k = 0; k < v_length; ++k) { - int16_t value = static_cast(v[k]); - fwrite(&value, sizeof(value), 1, file); - } - } + DumpRaw(name, std::span(v, v_length), dump_set); #endif } void DumpRaw([[maybe_unused]] absl::string_view name, - [[maybe_unused]] ArrayView v, + [[maybe_unused]] std::span v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) return; if (recording_activated_) { - DumpRaw(name, v.size(), v.data()); + FILE* file = GetRawFile(name); + for (bool val : v) { + int16_t value = static_cast(val); + fwrite(&value, sizeof(value), 1, file); + } } #endif } @@ -251,25 +237,20 @@ class ApmDataDumper { [[maybe_unused]] const int16_t* v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) - return; - - if (recording_activated_) { - FILE* file = GetRawFile(name); - fwrite(v, sizeof(v[0]), v_length, file); - } + DumpRaw(name, std::span(v, v_length), dump_set); #endif } void DumpRaw([[maybe_unused]] absl::string_view name, - [[maybe_unused]] ArrayView v, + [[maybe_unused]] std::span v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) return; if (recording_activated_) { - DumpRaw(name, v.size(), v.data()); + FILE* file = GetRawFile(name); + fwrite(v.data(), sizeof(v[0]), v.size(), file); } #endif } @@ -292,13 +273,21 @@ class ApmDataDumper { [[maybe_unused]] size_t v_length, [[maybe_unused]] const int32_t* v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { +#if WEBRTC_APM_DEBUG_DUMP == 1 + DumpRaw(name, std::span(v, v_length), dump_set); +#endif + } + + void DumpRaw([[maybe_unused]] absl::string_view name, + [[maybe_unused]] std::span v, + [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) return; if (recording_activated_) { FILE* file = GetRawFile(name); - fwrite(v, sizeof(v[0]), v_length, file); + fwrite(v.data(), sizeof(v[0]), v.size(), file); } #endif } @@ -322,40 +311,24 @@ class ApmDataDumper { [[maybe_unused]] const size_t* v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) - return; - - if (recording_activated_) { - FILE* file = GetRawFile(name); - fwrite(v, sizeof(v[0]), v_length, file); - } + DumpRaw(name, std::span(v, v_length), dump_set); #endif } void DumpRaw([[maybe_unused]] absl::string_view name, - [[maybe_unused]] ArrayView v, + [[maybe_unused]] std::span v, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) return; if (recording_activated_) { - DumpRaw(name, v.size(), v.data()); + FILE* file = GetRawFile(name); + fwrite(v.data(), sizeof(v[0]), v.size(), file); } #endif } - void DumpRaw(absl::string_view name, - ArrayView v, - int dump_set = kDefaultDumpSet) { -#if WEBRTC_APM_DEBUG_DUMP == 1 - if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) - return; - - DumpRaw(name, v.size(), v.data()); -#endif - } - void DumpWav([[maybe_unused]] absl::string_view name, [[maybe_unused]] size_t v_length, [[maybe_unused]] const float* v, @@ -363,19 +336,13 @@ class ApmDataDumper { [[maybe_unused]] int num_channels, [[maybe_unused]] int dump_set = kDefaultDumpSet) { #if WEBRTC_APM_DEBUG_DUMP == 1 - if (dump_set_to_use_ && *dump_set_to_use_ != dump_set) - return; - - if (recording_activated_) { - WavWriter* file = GetWavFile(name, sample_rate_hz, num_channels, - WavFile::SampleFormat::kFloat); - file->WriteSamples(v, v_length); - } + DumpWav(name, std::span(v, v_length), sample_rate_hz, + num_channels, dump_set); #endif } void DumpWav([[maybe_unused]] absl::string_view name, - [[maybe_unused]] ArrayView v, + [[maybe_unused]] std::span v, [[maybe_unused]] int sample_rate_hz, [[maybe_unused]] int num_channels, [[maybe_unused]] int dump_set = kDefaultDumpSet) { @@ -384,7 +351,9 @@ class ApmDataDumper { return; if (recording_activated_) { - DumpWav(name, v.size(), v.data(), sample_rate_hz, num_channels); + WavWriter* file = GetWavFile(name, sample_rate_hz, num_channels, + WavFile::SampleFormat::kFloat); + file->WriteSamples(v.data(), v.size()); } #endif } diff --git a/modules/audio_processing/ns/BUILD.gn b/modules/audio_processing/ns/BUILD.gn index e90ef0e06a5..6324ed2bb78 100644 --- a/modules/audio_processing/ns/BUILD.gn +++ b/modules/audio_processing/ns/BUILD.gn @@ -49,19 +49,9 @@ rtc_static_library("ns") { } deps = [ - "..:apm_logging", "..:audio_buffer", - "..:high_pass_filter", - "../../../api:array_view", - "../../../common_audio:common_audio_c", - "../../../common_audio/third_party/ooura:fft_size_128", "../../../common_audio/third_party/ooura:fft_size_256", "../../../rtc_base:checks", - "../../../rtc_base:safe_minmax", - "../../../rtc_base/system:arch", - "../../../system_wrappers", - "../../../system_wrappers:metrics", - "../utility:cascaded_biquad_filter", ] } @@ -74,18 +64,9 @@ if (rtc_include_tests) { deps = [ ":ns", - "..:apm_logging", "..:audio_buffer", - "..:audio_processing", - "..:high_pass_filter", - "../../../api:array_view", - "../../../rtc_base:checks", - "../../../rtc_base:safe_minmax", "../../../rtc_base:stringutils", - "../../../rtc_base/system:arch", - "../../../system_wrappers", "../../../test:test_support", - "../utility:cascaded_biquad_filter", ] defines = [] diff --git a/modules/audio_processing/ns/fast_math.cc b/modules/audio_processing/ns/fast_math.cc index 7d522522015..9f5ed6cfb15 100644 --- a/modules/audio_processing/ns/fast_math.cc +++ b/modules/audio_processing/ns/fast_math.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -60,7 +60,7 @@ float LogApproximation(float x) { return FastLog2f(x) * kLogOf2; } -void LogApproximation(ArrayView x, ArrayView y) { +void LogApproximation(std::span x, std::span y) { for (size_t k = 0; k < x.size(); ++k) { y[k] = LogApproximation(x[k]); } @@ -71,13 +71,13 @@ float ExpApproximation(float x) { return PowApproximation(10.f, x * kLog10Ofe); } -void ExpApproximation(ArrayView x, ArrayView y) { +void ExpApproximation(std::span x, std::span y) { for (size_t k = 0; k < x.size(); ++k) { y[k] = ExpApproximation(x[k]); } } -void ExpApproximationSignFlip(ArrayView x, ArrayView y) { +void ExpApproximationSignFlip(std::span x, std::span y) { for (size_t k = 0; k < x.size(); ++k) { y[k] = ExpApproximation(-x[k]); } diff --git a/modules/audio_processing/ns/fast_math.h b/modules/audio_processing/ns/fast_math.h index 598b31cfd78..1064780e7fb 100644 --- a/modules/audio_processing/ns/fast_math.h +++ b/modules/audio_processing/ns/fast_math.h @@ -11,7 +11,7 @@ #ifndef MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ #define MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ -#include "api/array_view.h" +#include namespace webrtc { @@ -20,7 +20,7 @@ float SqrtFastApproximation(float f); // Log base conversion log(x) = log2(x)/log2(e). float LogApproximation(float x); -void LogApproximation(ArrayView x, ArrayView y); +void LogApproximation(std::span x, std::span y); // 2^x approximation. float Pow2Approximation(float p); @@ -30,8 +30,8 @@ float PowApproximation(float x, float p); // e^x approximation. float ExpApproximation(float x); -void ExpApproximation(ArrayView x, ArrayView y); -void ExpApproximationSignFlip(ArrayView x, ArrayView y); +void ExpApproximation(std::span x, std::span y); +void ExpApproximationSignFlip(std::span x, std::span y); } // namespace webrtc #endif // MODULES_AUDIO_PROCESSING_NS_FAST_MATH_H_ diff --git a/modules/audio_processing/ns/histograms.h b/modules/audio_processing/ns/histograms.h index 3f4445486b3..3f659172456 100644 --- a/modules/audio_processing/ns/histograms.h +++ b/modules/audio_processing/ns/histograms.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_NS_HISTOGRAMS_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/signal_model.h" namespace webrtc { @@ -35,11 +35,11 @@ class Histograms { void Update(const SignalModel& features_); // Methods for accessing the histograms. - ArrayView get_lrt() const { return lrt_; } - ArrayView get_spectral_flatness() const { + std::span get_lrt() const { return lrt_; } + std::span get_spectral_flatness() const { return spectral_flatness_; } - ArrayView get_spectral_diff() const { + std::span get_spectral_diff() const { return spectral_diff_; } diff --git a/modules/audio_processing/ns/noise_estimator.cc b/modules/audio_processing/ns/noise_estimator.cc index 00b647ca354..4661b6d32a1 100644 --- a/modules/audio_processing/ns/noise_estimator.cc +++ b/modules/audio_processing/ns/noise_estimator.cc @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/fast_math.h" #include "modules/audio_processing/ns/ns_common.h" #include "modules/audio_processing/ns/suppression_params.h" @@ -70,7 +70,7 @@ void NoiseEstimator::PrepareAnalysis() { void NoiseEstimator::PreUpdate( int32_t num_analyzed_frames, - ArrayView signal_spectrum, + std::span signal_spectrum, float signal_spectral_sum) { quantile_noise_estimator_.Estimate(signal_spectrum, noise_spectrum_); @@ -159,8 +159,8 @@ void NoiseEstimator::PreUpdate( } void NoiseEstimator::PostUpdate( - ArrayView speech_probability, - ArrayView signal_spectrum) { + std::span speech_probability, + std::span signal_spectrum) { // Time-avg parameter for noise_spectrum update. constexpr float kNoiseUpdate = 0.9f; diff --git a/modules/audio_processing/ns/noise_estimator.h b/modules/audio_processing/ns/noise_estimator.h index 2ae89d5ed32..fd06b3b1380 100644 --- a/modules/audio_processing/ns/noise_estimator.h +++ b/modules/audio_processing/ns/noise_estimator.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/ns_common.h" #include "modules/audio_processing/ns/quantile_noise_estimator.h" #include "modules/audio_processing/ns/suppression_params.h" @@ -32,29 +32,29 @@ class NoiseEstimator { // Performs the first step of the estimator update. void PreUpdate(int32_t num_analyzed_frames, - ArrayView signal_spectrum, + std::span signal_spectrum, float signal_spectral_sum); // Performs the second step of the estimator update. - void PostUpdate(ArrayView speech_probability, - ArrayView signal_spectrum); + void PostUpdate(std::span speech_probability, + std::span signal_spectrum); // Returns the noise spectral estimate. - ArrayView get_noise_spectrum() const { + std::span get_noise_spectrum() const { return noise_spectrum_; } // Returns the noise from the previous frame. - ArrayView get_prev_noise_spectrum() const { + std::span get_prev_noise_spectrum() const { return prev_noise_spectrum_; } // Returns a noise spectral estimate based on white and pink noise parameters. - ArrayView get_parametric_noise_spectrum() + std::span get_parametric_noise_spectrum() const { return parametric_noise_spectrum_; } - ArrayView get_conservative_noise_spectrum() + std::span get_conservative_noise_spectrum() const { return conservative_noise_spectrum_; } diff --git a/modules/audio_processing/ns/noise_suppressor.cc b/modules/audio_processing/ns/noise_suppressor.cc index 47030c2acdf..5f535e51244 100644 --- a/modules/audio_processing/ns/noise_suppressor.cc +++ b/modules/audio_processing/ns/noise_suppressor.cc @@ -16,8 +16,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/ns/fast_math.h" #include "modules/audio_processing/ns/ns_common.h" @@ -76,7 +76,7 @@ constexpr std::array kBlocks160w256FirstHalf = { 0.99986614f}; // Applies the filterbank window to a buffer. -void ApplyFilterBankWindow(ArrayView x) { +void ApplyFilterBankWindow(std::span x) { for (size_t i = 0; i < 96; ++i) { x[i] = kBlocks160w256FirstHalf[i] * x[i]; } @@ -88,9 +88,9 @@ void ApplyFilterBankWindow(ArrayView x) { } // Extends a frame with previous data. -void FormExtendedFrame(ArrayView frame, - ArrayView old_data, - ArrayView extended_frame) { +void FormExtendedFrame(std::span frame, + std::span old_data, + std::span extended_frame) { std::copy(old_data.begin(), old_data.end(), extended_frame.begin()); std::copy(frame.begin(), frame.end(), extended_frame.begin() + old_data.size()); @@ -99,9 +99,9 @@ void FormExtendedFrame(ArrayView frame, } // Uses overlap-and-add to produce an output frame. -void OverlapAndAdd(ArrayView extended_frame, - ArrayView overlap_memory, - ArrayView output_frame) { +void OverlapAndAdd(std::span extended_frame, + std::span overlap_memory, + std::span output_frame) { for (size_t i = 0; i < kOverlapSize; ++i) { output_frame[i] = overlap_memory[i] + extended_frame[i]; } @@ -113,9 +113,9 @@ void OverlapAndAdd(ArrayView extended_frame, } // Produces a delayed frame. -void DelaySignal(ArrayView frame, - ArrayView delay_buffer, - ArrayView delayed_frame) { +void DelaySignal(std::span frame, + std::span delay_buffer, + std::span delayed_frame) { constexpr size_t kSamplesFromFrame = kNsFrameSize - (kFftSize - kNsFrameSize); std::copy(delay_buffer.begin(), delay_buffer.end(), delayed_frame.begin()); std::copy(frame.begin(), frame.begin() + kSamplesFromFrame, @@ -126,7 +126,7 @@ void DelaySignal(ArrayView frame, } // Computes the energy of an extended frame. -float ComputeEnergyOfExtendedFrame(ArrayView x) { +float ComputeEnergyOfExtendedFrame(std::span x) { float energy = 0.f; for (float x_k : x) { energy += x_k * x_k; @@ -137,8 +137,8 @@ float ComputeEnergyOfExtendedFrame(ArrayView x) { // Computes the energy of an extended frame based on its subcomponents. float ComputeEnergyOfExtendedFrame( - ArrayView frame, - ArrayView old_data) { + std::span frame, + std::span old_data) { float energy = 0.f; for (float v : old_data) { energy += v * v; @@ -152,9 +152,9 @@ float ComputeEnergyOfExtendedFrame( // Computes the magnitude spectrum based on an FFT output. void ComputeMagnitudeSpectrum( - ArrayView real, - ArrayView imag, - ArrayView signal_spectrum) { + std::span real, + std::span imag, + std::span signal_spectrum) { signal_spectrum[0] = fabsf(real[0]) + 1.f; signal_spectrum[kFftSizeBy2Plus1 - 1] = fabsf(real[kFftSizeBy2Plus1 - 1]) + 1.f; @@ -166,13 +166,13 @@ void ComputeMagnitudeSpectrum( } // Compute prior and post SNR. -void ComputeSnr(ArrayView filter, - ArrayView prev_signal_spectrum, - ArrayView signal_spectrum, - ArrayView prev_noise_spectrum, - ArrayView noise_spectrum, - ArrayView prior_snr, - ArrayView post_snr) { +void ComputeSnr(std::span filter, + std::span prev_signal_spectrum, + std::span signal_spectrum, + std::span prev_noise_spectrum, + std::span noise_spectrum, + std::span prior_snr, + std::span post_snr) { for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { // Previous post SNR. // Previous estimate: based on previous frame with gain filter. @@ -193,10 +193,10 @@ void ComputeSnr(ArrayView filter, // Computes the attenuating gain for the noise suppression of the upper bands. float ComputeUpperBandsGain( float minimum_attenuating_gain, - ArrayView filter, - ArrayView speech_probability, - ArrayView prev_analysis_signal_spectrum, - ArrayView signal_spectrum) { + std::span filter, + std::span speech_probability, + std::span prev_analysis_signal_spectrum, + std::span signal_spectrum) { // Average speech prob and filter gain for the end of the lowest band. constexpr int kNumAvgBins = 32; constexpr float kOneByNumAvgBins = 1.f / kNumAvgBins; @@ -277,13 +277,13 @@ NoiseSuppressor::NoiseSuppressor(const NsConfig& config, } void NoiseSuppressor::AggregateWienerFilters( - ArrayView filter) const { - ArrayView filter0 = + std::span filter) const { + std::span filter0 = channels_[0]->wiener_filter.get_filter(); std::copy(filter0.begin(), filter0.end(), filter.begin()); for (size_t ch = 1; ch < num_channels_; ++ch) { - ArrayView filter_ch = + std::span filter_ch = channels_[ch]->wiener_filter.get_filter(); for (size_t k = 0; k < kFftSizeBy2Plus1; ++k) { @@ -301,7 +301,7 @@ void NoiseSuppressor::Analyze(const AudioBuffer& audio) { // Check for zero frames. bool zero_frame = true; for (size_t ch = 0; ch < num_channels_; ++ch) { - ArrayView y_band0( + std::span y_band0( &audio.split_bands_const(ch)[0][0], kNsFrameSize); float energy = ComputeEnergyOfExtendedFrame( y_band0, channels_[ch]->analyze_analysis_memory); @@ -331,7 +331,7 @@ void NoiseSuppressor::Analyze(const AudioBuffer& audio) { // Analyze all channels. for (size_t ch = 0; ch < num_channels_; ++ch) { std::unique_ptr& ch_p = channels_[ch]; - ArrayView y_band0( + std::span y_band0( &audio.split_bands_const(ch)[0][0], kNsFrameSize); // Form an extended frame and apply analysis filter bank windowing. @@ -389,34 +389,34 @@ void NoiseSuppressor::Analyze(const AudioBuffer& audio) { void NoiseSuppressor::Process(AudioBuffer* audio) { // Select the space for storing data during the processing. std::array filter_bank_states_stack; - ArrayView filter_bank_states(filter_bank_states_stack.data(), + std::span filter_bank_states(filter_bank_states_stack.data(), num_channels_); std::array upper_band_gains_stack; - ArrayView upper_band_gains(upper_band_gains_stack.data(), + std::span upper_band_gains(upper_band_gains_stack.data(), num_channels_); std::array energies_before_filtering_stack; - ArrayView energies_before_filtering( + std::span energies_before_filtering( energies_before_filtering_stack.data(), num_channels_); std::array gain_adjustments_stack; - ArrayView gain_adjustments(gain_adjustments_stack.data(), + std::span gain_adjustments(gain_adjustments_stack.data(), num_channels_); if (NumChannelsOnHeap(num_channels_) > 0) { // If the stack-allocated space is too small, use the heap for storing the // data. - filter_bank_states = ArrayView( + filter_bank_states = std::span( filter_bank_states_heap_.data(), num_channels_); upper_band_gains = - ArrayView(upper_band_gains_heap_.data(), num_channels_); + std::span(upper_band_gains_heap_.data(), num_channels_); energies_before_filtering = - ArrayView(energies_before_filtering_heap_.data(), num_channels_); + std::span(energies_before_filtering_heap_.data(), num_channels_); gain_adjustments = - ArrayView(gain_adjustments_heap_.data(), num_channels_); + std::span(gain_adjustments_heap_.data(), num_channels_); } // Compute the suppression filters for all channels. for (size_t ch = 0; ch < num_channels_; ++ch) { // Form an extended frame and apply analysis filter bank windowing. - ArrayView y_band0(&audio->split_bands(ch)[0][0], + std::span y_band0(&audio->split_bands(ch)[0][0], kNsFrameSize); FormExtendedFrame(y_band0, channels_[ch]->process_analysis_memory, @@ -463,7 +463,7 @@ void NoiseSuppressor::Process(AudioBuffer* audio) { // Aggregate the Wiener filters for all channels. std::array filter_data; - ArrayView filter = filter_data; + std::span filter = filter_data; if (num_channels_ == 1) { filter = channels_[0]->wiener_filter.get_filter(); } else { @@ -515,7 +515,7 @@ void NoiseSuppressor::Process(AudioBuffer* audio) { // Use overlap-and-add to form the output frame of the lowest band. for (size_t ch = 0; ch < num_channels_; ++ch) { - ArrayView y_band0(&audio->split_bands(ch)[0][0], + std::span y_band0(&audio->split_bands(ch)[0][0], kNsFrameSize); OverlapAndAdd(filter_bank_states[ch].extended_frame, channels_[ch]->process_synthesis_memory, y_band0); @@ -533,7 +533,7 @@ void NoiseSuppressor::Process(AudioBuffer* audio) { for (size_t b = 1; b < num_bands_; ++b) { // Delay the upper bands to match the delay of the filterbank applied to // the lowest band. - ArrayView y_band(&audio->split_bands(ch)[b][0], + std::span y_band(&audio->split_bands(ch)[b][0], kNsFrameSize); std::array delayed_frame; DelaySignal(y_band, channels_[ch]->process_delay_memory[b - 1], @@ -550,7 +550,7 @@ void NoiseSuppressor::Process(AudioBuffer* audio) { // Limit the output the allowed range. for (size_t ch = 0; ch < num_channels_; ++ch) { for (size_t b = 0; b < num_bands_; ++b) { - ArrayView y_band(&audio->split_bands(ch)[b][0], + std::span y_band(&audio->split_bands(ch)[b][0], kNsFrameSize); for (size_t j = 0; j < kNsFrameSize; j++) { y_band[j] = std::min(std::max(y_band[j], -32768.f), 32767.f); diff --git a/modules/audio_processing/ns/noise_suppressor.h b/modules/audio_processing/ns/noise_suppressor.h index 59312ef0fb5..5e71decf592 100644 --- a/modules/audio_processing/ns/noise_suppressor.h +++ b/modules/audio_processing/ns/noise_suppressor.h @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/ns/noise_estimator.h" #include "modules/audio_processing/ns/ns_common.h" @@ -87,7 +87,7 @@ class NoiseSuppressor { std::vector> channels_; // Aggregates the Wiener filters into a single filter to use. - void AggregateWienerFilters(ArrayView filter) const; + void AggregateWienerFilters(std::span filter) const; }; } // namespace webrtc diff --git a/modules/audio_processing/ns/ns_fft.cc b/modules/audio_processing/ns/ns_fft.cc index 07e63739aa4..43112191db2 100644 --- a/modules/audio_processing/ns/ns_fft.cc +++ b/modules/audio_processing/ns/ns_fft.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "common_audio/third_party/ooura/fft_size_256/fft4g.h" #include "modules/audio_processing/ns/ns_common.h" @@ -29,9 +29,9 @@ NrFft::NrFft() : bit_reversal_state_(kFftSize / 2), tables_(kFftSize / 2) { tables_.data()); } -void NrFft::Fft(ArrayView time_data, - ArrayView real, - ArrayView imag) { +void NrFft::Fft(std::span time_data, + std::span real, + std::span imag) { WebRtc_rdft(kFftSize, 1, time_data.data(), bit_reversal_state_.data(), tables_.data()); @@ -47,9 +47,9 @@ void NrFft::Fft(ArrayView time_data, } } -void NrFft::Ifft(ArrayView real, - ArrayView imag, - ArrayView time_data) { +void NrFft::Ifft(std::span real, + std::span imag, + std::span time_data) { time_data[0] = real[0]; time_data[1] = real[kFftSizeBy2Plus1 - 1]; for (size_t i = 1; i < kFftSizeBy2Plus1 - 1; ++i) { diff --git a/modules/audio_processing/ns/ns_fft.h b/modules/audio_processing/ns/ns_fft.h index 78872512da0..358d23157bb 100644 --- a/modules/audio_processing/ns/ns_fft.h +++ b/modules/audio_processing/ns/ns_fft.h @@ -12,9 +12,9 @@ #define MODULES_AUDIO_PROCESSING_NS_NS_FFT_H_ #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/ns/ns_common.h" namespace webrtc { @@ -27,14 +27,14 @@ class NrFft { NrFft& operator=(const NrFft&) = delete; // Transforms the signal from time to frequency domain. - void Fft(ArrayView time_data, - ArrayView real, - ArrayView imag); + void Fft(std::span time_data, + std::span real, + std::span imag); // Transforms the signal from frequency to time domain. - void Ifft(ArrayView real, - ArrayView imag, - ArrayView time_data); + void Ifft(std::span real, + std::span imag, + std::span time_data); private: std::vector bit_reversal_state_; diff --git a/modules/audio_processing/ns/prior_signal_model_estimator.cc b/modules/audio_processing/ns/prior_signal_model_estimator.cc index f4017645540..dcfe297f2ed 100644 --- a/modules/audio_processing/ns/prior_signal_model_estimator.cc +++ b/modules/audio_processing/ns/prior_signal_model_estimator.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/histograms.h" #include "modules/audio_processing/ns/ns_common.h" #include "rtc_base/checks.h" @@ -25,7 +25,7 @@ namespace { // Identifies the first of the two largest peaks in the histogram. void FindFirstOfTwoLargestPeaks( float bin_size, - ArrayView spectral_flatness, + std::span spectral_flatness, float* peak_position, int* peak_weight) { RTC_DCHECK(peak_position); @@ -66,7 +66,7 @@ void FindFirstOfTwoLargestPeaks( } } -void UpdateLrt(ArrayView lrt_histogram, +void UpdateLrt(std::span lrt_histogram, float* prior_model_lrt, bool* low_lrt_fluctuations) { RTC_DCHECK(prior_model_lrt); diff --git a/modules/audio_processing/ns/quantile_noise_estimator.cc b/modules/audio_processing/ns/quantile_noise_estimator.cc index fec852b531f..d322f6294d8 100644 --- a/modules/audio_processing/ns/quantile_noise_estimator.cc +++ b/modules/audio_processing/ns/quantile_noise_estimator.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/fast_math.h" #include "modules/audio_processing/ns/ns_common.h" @@ -33,8 +33,8 @@ QuantileNoiseEstimator::QuantileNoiseEstimator() { } void QuantileNoiseEstimator::Estimate( - ArrayView signal_spectrum, - ArrayView noise_spectrum) { + std::span signal_spectrum, + std::span noise_spectrum) { std::array log_spectrum; LogApproximation(signal_spectrum, log_spectrum); @@ -82,7 +82,7 @@ void QuantileNoiseEstimator::Estimate( if (quantile_index_to_return >= 0) { ExpApproximation( - ArrayView(&log_quantile_[quantile_index_to_return], + std::span(&log_quantile_[quantile_index_to_return], kFftSizeBy2Plus1), quantile_); } diff --git a/modules/audio_processing/ns/quantile_noise_estimator.h b/modules/audio_processing/ns/quantile_noise_estimator.h index 79a1b3e1ccc..6eaf9d2e973 100644 --- a/modules/audio_processing/ns/quantile_noise_estimator.h +++ b/modules/audio_processing/ns/quantile_noise_estimator.h @@ -14,8 +14,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/ns_common.h" namespace webrtc { @@ -30,8 +30,8 @@ class QuantileNoiseEstimator { QuantileNoiseEstimator& operator=(const QuantileNoiseEstimator&) = delete; // Estimate noise. - void Estimate(ArrayView signal_spectrum, - ArrayView noise_spectrum); + void Estimate(std::span signal_spectrum, + std::span noise_spectrum); private: std::array density_; diff --git a/modules/audio_processing/ns/signal_model_estimator.cc b/modules/audio_processing/ns/signal_model_estimator.cc index 84f175f71f8..0c45bd2d389 100644 --- a/modules/audio_processing/ns/signal_model_estimator.cc +++ b/modules/audio_processing/ns/signal_model_estimator.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/fast_math.h" #include "modules/audio_processing/ns/ns_common.h" #include "rtc_base/checks.h" @@ -27,8 +27,8 @@ constexpr float kOneByFftSizeBy2Plus1 = 1.f / kFftSizeBy2Plus1; // Computes the difference measure between input spectrum and a template/learned // noise spectrum. float ComputeSpectralDiff( - ArrayView conservative_noise_spectrum, - ArrayView signal_spectrum, + std::span conservative_noise_spectrum, + std::span signal_spectrum, float signal_spectral_sum, float diff_normalization) { // spectral_diff = var(signal_spectrum) - cov(signal_spectrum, magnAvgPause)^2 @@ -67,7 +67,7 @@ float ComputeSpectralDiff( // Updates the spectral flatness based on the input spectrum. void UpdateSpectralFlatness( - ArrayView signal_spectrum, + std::span signal_spectrum, float signal_spectral_sum, float* spectral_flatness) { RTC_DCHECK(spectral_flatness); @@ -100,9 +100,9 @@ void UpdateSpectralFlatness( } // Updates the log LRT measures. -void UpdateSpectralLrt(ArrayView prior_snr, - ArrayView post_snr, - ArrayView avg_log_lrt, +void UpdateSpectralLrt(std::span prior_snr, + std::span post_snr, + std::span avg_log_lrt, float* lrt) { RTC_DCHECK(lrt); @@ -135,10 +135,10 @@ void SignalModelEstimator::AdjustNormalization(int32_t num_analyzed_frames, // Update the noise features. void SignalModelEstimator::Update( - ArrayView prior_snr, - ArrayView post_snr, - ArrayView conservative_noise_spectrum, - ArrayView signal_spectrum, + std::span prior_snr, + std::span post_snr, + std::span conservative_noise_spectrum, + std::span signal_spectrum, float signal_spectral_sum, float signal_energy) { // Compute spectral flatness on input spectrum. diff --git a/modules/audio_processing/ns/signal_model_estimator.h b/modules/audio_processing/ns/signal_model_estimator.h index 540e4786615..ef0e5e44019 100644 --- a/modules/audio_processing/ns/signal_model_estimator.h +++ b/modules/audio_processing/ns/signal_model_estimator.h @@ -12,8 +12,8 @@ #define MODULES_AUDIO_PROCESSING_NS_SIGNAL_MODEL_ESTIMATOR_H_ #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/histograms.h" #include "modules/audio_processing/ns/ns_common.h" #include "modules/audio_processing/ns/prior_signal_model.h" @@ -32,10 +32,10 @@ class SignalModelEstimator { void AdjustNormalization(int32_t num_analyzed_frames, float signal_energy); void Update( - ArrayView prior_snr, - ArrayView post_snr, - ArrayView conservative_noise_spectrum, - ArrayView signal_spectrum, + std::span prior_snr, + std::span post_snr, + std::span conservative_noise_spectrum, + std::span signal_spectrum, float signal_spectral_sum, float signal_energy); diff --git a/modules/audio_processing/ns/speech_probability_estimator.cc b/modules/audio_processing/ns/speech_probability_estimator.cc index 0f7369382fc..44b90fe047d 100644 --- a/modules/audio_processing/ns/speech_probability_estimator.cc +++ b/modules/audio_processing/ns/speech_probability_estimator.cc @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/fast_math.h" #include "modules/audio_processing/ns/ns_common.h" #include "modules/audio_processing/ns/prior_signal_model.h" @@ -30,10 +30,10 @@ SpeechProbabilityEstimator::SpeechProbabilityEstimator() { void SpeechProbabilityEstimator::Update( int32_t num_analyzed_frames, - ArrayView prior_snr, - ArrayView post_snr, - ArrayView conservative_noise_spectrum, - ArrayView signal_spectrum, + std::span prior_snr, + std::span post_snr, + std::span conservative_noise_spectrum, + std::span signal_spectrum, float signal_spectral_sum, float signal_energy) { // Update models. diff --git a/modules/audio_processing/ns/speech_probability_estimator.h b/modules/audio_processing/ns/speech_probability_estimator.h index 8651e8a883e..0516a6d461a 100644 --- a/modules/audio_processing/ns/speech_probability_estimator.h +++ b/modules/audio_processing/ns/speech_probability_estimator.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/ns_common.h" #include "modules/audio_processing/ns/signal_model_estimator.h" @@ -31,15 +31,15 @@ class SpeechProbabilityEstimator { // Compute speech probability. void Update( int32_t num_analyzed_frames, - ArrayView prior_snr, - ArrayView post_snr, - ArrayView conservative_noise_spectrum, - ArrayView signal_spectrum, + std::span prior_snr, + std::span post_snr, + std::span conservative_noise_spectrum, + std::span signal_spectrum, float signal_spectral_sum, float signal_energy); float get_prior_probability() const { return prior_speech_prob_; } - ArrayView get_probability() { return speech_probability_; } + std::span get_probability() { return speech_probability_; } private: SignalModelEstimator signal_model_estimator_; diff --git a/modules/audio_processing/ns/wiener_filter.cc b/modules/audio_processing/ns/wiener_filter.cc index ff8ec4d6382..521fd712976 100644 --- a/modules/audio_processing/ns/wiener_filter.cc +++ b/modules/audio_processing/ns/wiener_filter.cc @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/fast_math.h" #include "modules/audio_processing/ns/ns_common.h" #include "modules/audio_processing/ns/suppression_params.h" @@ -32,10 +32,10 @@ WienerFilter::WienerFilter(const SuppressionParams& suppression_params) void WienerFilter::Update( int32_t num_analyzed_frames, - ArrayView noise_spectrum, - ArrayView prev_noise_spectrum, - ArrayView parametric_noise_spectrum, - ArrayView signal_spectrum) { + std::span noise_spectrum, + std::span prev_noise_spectrum, + std::span parametric_noise_spectrum, + std::span signal_spectrum) { for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) { // Previous estimate based on previous frame with gain filter. float prev_tsa = spectrum_prev_process_[i] / diff --git a/modules/audio_processing/ns/wiener_filter.h b/modules/audio_processing/ns/wiener_filter.h index d34cd56e158..5f956b79dcf 100644 --- a/modules/audio_processing/ns/wiener_filter.h +++ b/modules/audio_processing/ns/wiener_filter.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/ns/ns_common.h" #include "modules/audio_processing/ns/suppression_params.h" @@ -30,10 +30,10 @@ class WienerFilter { // Updates the filter estimate. void Update( int32_t num_analyzed_frames, - ArrayView noise_spectrum, - ArrayView prev_noise_spectrum, - ArrayView parametric_noise_spectrum, - ArrayView signal_spectrum); + std::span noise_spectrum, + std::span prev_noise_spectrum, + std::span parametric_noise_spectrum, + std::span signal_spectrum); // Compute an overall gain scaling factor. float ComputeOverallScalingFactor(int32_t num_analyzed_frames, @@ -42,7 +42,7 @@ class WienerFilter { float energy_after_filtering) const; // Returns the filter. - ArrayView get_filter() const { + std::span get_filter() const { return filter_; } diff --git a/modules/audio_processing/post_filter.cc b/modules/audio_processing/post_filter.cc index ab089b10ce1..919d7cf5b6a 100644 --- a/modules/audio_processing/post_filter.cc +++ b/modules/audio_processing/post_filter.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/utility/cascaded_biquad_filter.h" #include "rtc_base/checks.h" @@ -52,7 +52,7 @@ std::unique_ptr PostFilter::CreateIfNeeded(int sample_rate_hz, } PostFilter::PostFilter( - ArrayView coefficients, + std::span coefficients, size_t num_channels) { RTC_DCHECK(!coefficients.empty()); @@ -65,8 +65,8 @@ PostFilter::PostFilter( void PostFilter::Process(AudioBuffer& audio) { RTC_DCHECK_EQ(filters_.size(), audio.num_channels()); for (size_t k = 0; k < audio.num_channels(); ++k) { - ArrayView channel_data = - ArrayView(&audio.channels()[k][0], audio.num_frames()); + std::span channel_data = + std::span(&audio.channels()[k][0], audio.num_frames()); filters_[k]->Process(channel_data); } } diff --git a/modules/audio_processing/post_filter.h b/modules/audio_processing/post_filter.h index cce42608a67..2817c6ece97 100644 --- a/modules/audio_processing/post_filter.h +++ b/modules/audio_processing/post_filter.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/utility/cascaded_biquad_filter.h" @@ -39,7 +39,7 @@ class PostFilter { private: PostFilter( - ArrayView coefficients, + std::span coefficients, size_t num_channels); std::vector> filters_; diff --git a/modules/audio_processing/post_filter_unittest.cc b/modules/audio_processing/post_filter_unittest.cc index cc62f1308d2..facb613af71 100644 --- a/modules/audio_processing/post_filter_unittest.cc +++ b/modules/audio_processing/post_filter_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/test/audio_buffer_tools.h" @@ -48,7 +48,7 @@ std::vector ProcessOneFrameAsAudioBuffer( return frame_output; } -float ComputePower(ArrayView audio) { +float ComputePower(std::span audio) { double energy = 0.0; std::for_each(audio.begin(), audio.end(), [&energy](float x) { energy += x * x; }); diff --git a/modules/audio_processing/residual_echo_detector.cc b/modules/audio_processing/residual_echo_detector.cc index 437d857c226..4db2a1e15a5 100644 --- a/modules/audio_processing/residual_echo_detector.cc +++ b/modules/audio_processing/residual_echo_detector.cc @@ -15,17 +15,19 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "system_wrappers/include/metrics.h" +namespace webrtc { + namespace { -float Power(webrtc::ArrayView input) { +float Power(std::span input) { if (input.empty()) { return 0.f; } @@ -42,8 +44,6 @@ constexpr size_t kAggregationBufferSize = 10 * 100; } // namespace -namespace webrtc { - std::atomic ResidualEchoDetector::instance_count_(0); ResidualEchoDetector::ResidualEchoDetector() @@ -58,7 +58,7 @@ ResidualEchoDetector::ResidualEchoDetector() ResidualEchoDetector::~ResidualEchoDetector() = default; void ResidualEchoDetector::AnalyzeRenderAudio( - ArrayView render_audio) { + std::span render_audio) { // Dump debug data assuming 48 kHz sample rate (if this assumption is not // valid the dumped audio will need to be converted offline accordingly). data_dumper_->DumpWav("ed_render", render_audio.size(), render_audio.data(), @@ -79,7 +79,7 @@ void ResidualEchoDetector::AnalyzeRenderAudio( } void ResidualEchoDetector::AnalyzeCaptureAudio( - ArrayView capture_audio) { + std::span capture_audio) { // Dump debug data assuming 48 kHz sample rate (if this assumption is not // valid the dumped audio will need to be converted offline accordingly). data_dumper_->DumpWav("ed_capture", capture_audio.size(), diff --git a/modules/audio_processing/residual_echo_detector.h b/modules/audio_processing/residual_echo_detector.h index 292b6ce6583..4c5823ebc94 100644 --- a/modules/audio_processing/residual_echo_detector.h +++ b/modules/audio_processing/residual_echo_detector.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "modules/audio_processing/echo_detector/circular_buffer.h" #include "modules/audio_processing/echo_detector/mean_variance_estimator.h" @@ -34,10 +34,10 @@ class ResidualEchoDetector : public EchoDetector { ~ResidualEchoDetector() override; // This function should be called while holding the render lock. - void AnalyzeRenderAudio(ArrayView render_audio) override; + void AnalyzeRenderAudio(std::span render_audio) override; // This function should be called while holding the capture lock. - void AnalyzeCaptureAudio(ArrayView capture_audio) override; + void AnalyzeCaptureAudio(std::span capture_audio) override; // This function should be called while holding the capture lock. void Initialize(int capture_sample_rate_hz, diff --git a/modules/audio_processing/rms_level.cc b/modules/audio_processing/rms_level.cc index 8ae21fcb29a..8cfab7f3f62 100644 --- a/modules/audio_processing/rms_level.cc +++ b/modules/audio_processing/rms_level.cc @@ -16,8 +16,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -61,7 +61,7 @@ void RmsLevel::Reset() { block_size_ = std::nullopt; } -void RmsLevel::Analyze(ArrayView data) { +void RmsLevel::Analyze(std::span data) { if (data.empty()) { return; } @@ -78,7 +78,7 @@ void RmsLevel::Analyze(ArrayView data) { max_sum_square_ = std::max(max_sum_square_, sum_square); } -void RmsLevel::Analyze(ArrayView data) { +void RmsLevel::Analyze(std::span data) { if (data.empty()) { return; } diff --git a/modules/audio_processing/rms_level.h b/modules/audio_processing/rms_level.h index 879fdff224e..0d804d0b368 100644 --- a/modules/audio_processing/rms_level.h +++ b/modules/audio_processing/rms_level.h @@ -15,8 +15,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -45,8 +44,8 @@ class RmsLevel { void Reset(); // Pass each chunk of audio to Analyze() to accumulate the level. - void Analyze(ArrayView data); - void Analyze(ArrayView data); + void Analyze(std::span data); + void Analyze(std::span data); // If all samples with the given `length` have a magnitude of zero, this is // a shortcut to avoid some computation. diff --git a/modules/audio_processing/rms_level_unittest.cc b/modules/audio_processing/rms_level_unittest.cc index 9c95652705d..0fc3c26276d 100644 --- a/modules/audio_processing/rms_level_unittest.cc +++ b/modules/audio_processing/rms_level_unittest.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" #include "test/gtest.h" @@ -26,20 +26,20 @@ namespace { constexpr int kSampleRateHz = 48000; constexpr size_t kBlockSizeSamples = kSampleRateHz / 100; -std::unique_ptr RunTest(ArrayView input) { +std::unique_ptr RunTest(std::span input) { std::unique_ptr level(new RmsLevel); for (size_t n = 0; n + kBlockSizeSamples <= input.size(); n += kBlockSizeSamples) { - level->Analyze(input.subview(n, kBlockSizeSamples)); + level->Analyze(input.subspan(n, kBlockSizeSamples)); } return level; } -std::unique_ptr RunTest(ArrayView input) { +std::unique_ptr RunTest(std::span input) { std::unique_ptr level(new RmsLevel); for (size_t n = 0; n + kBlockSizeSamples <= input.size(); n += kBlockSizeSamples) { - level->Analyze(input.subview(n, kBlockSizeSamples)); + level->Analyze(input.subspan(n, kBlockSizeSamples)); } return level; } diff --git a/modules/audio_processing/splitting_filter.cc b/modules/audio_processing/splitting_filter.cc index 6513bf80f17..76d77abec37 100644 --- a/modules/audio_processing/splitting_filter.cc +++ b/modules/audio_processing/splitting_filter.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "common_audio/channel_buffer.h" #include "common_audio/signal_processing/include/signal_processing_library.h" #include "modules/audio_processing/three_band_filter_bank.h" @@ -113,10 +113,10 @@ void SplittingFilter::ThreeBandsAnalysis(const ChannelBuffer* data, for (size_t i = 0; i < three_band_filter_banks_.size(); ++i) { three_band_filter_banks_[i].Analysis( - ArrayView( + std::span( data->channels_view()[i].data(), ThreeBandFilterBank::kFullBandSize), - ArrayView, ThreeBandFilterBank::kNumBands>( + std::span, ThreeBandFilterBank::kNumBands>( bands->bands_view(i).data(), ThreeBandFilterBank::kNumBands)); } } @@ -134,9 +134,9 @@ void SplittingFilter::ThreeBandsSynthesis(const ChannelBuffer* bands, for (size_t i = 0; i < data->num_channels(); ++i) { three_band_filter_banks_[i].Synthesis( - ArrayView, ThreeBandFilterBank::kNumBands>( + std::span, ThreeBandFilterBank::kNumBands>( bands->bands_view(i).data(), ThreeBandFilterBank::kNumBands), - ArrayView( + std::span( data->channels_view()[i].data(), ThreeBandFilterBank::kFullBandSize)); } diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc index 2ee92f68bde..f78ab46a507 100644 --- a/modules/audio_processing/test/aec_dump_based_simulator.cc +++ b/modules/audio_processing/test/aec_dump_based_simulator.cc @@ -19,7 +19,6 @@ #include #include #include -#include // no-presubmit-check TODO(webrtc:8982) #include #include @@ -29,13 +28,11 @@ #include "common_audio/channel_buffer.h" #include "common_audio/wav_file.h" #include "modules/audio_processing/debug.pb.h" -#include "modules/audio_processing/echo_control_mobile_impl.h" #include "modules/audio_processing/test/aec_dump_based_simulator.h" #include "modules/audio_processing/test/audio_processing_simulator.h" #include "modules/audio_processing/test/protobuf_utils.h" #include "modules/audio_processing/test/test_utils.h" #include "rtc_base/checks.h" -#include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { @@ -81,18 +78,6 @@ bool VerifyFloatBitExactness(const audioproc::Stream& msg, return true; } -// Selectively reads the next proto-buf message from dump-file or string input. -// Returns a bool indicating whether a new message was available. -bool ReadNextMessage(bool use_dump_file, - FILE* dump_input_file, - std::stringstream& input, - audioproc::Event& event_msg) { - if (use_dump_file) { - return ReadMessageFromFile(dump_input_file, &event_msg); - } - return ReadMessageFromString(&input, &event_msg); -} - } // namespace AecDumpBasedSimulator::AecDumpBasedSimulator( @@ -258,19 +243,12 @@ void AecDumpBasedSimulator::Process() { CheckedDivExact(sample_rate_hz, kChunksPerSecond), 1)); } - const bool use_dump_file = !settings_.aec_dump_input_string.has_value(); - std::stringstream input; - if (use_dump_file) { - dump_input_file_ = - OpenFile(settings_.aec_dump_input_filename->c_str(), "rb"); - } else { - input << settings_.aec_dump_input_string.value(); - } + dump_input_file_ = OpenFile(settings_.aec_dump_input_filename->c_str(), "rb"); audioproc::Event event_msg; int capture_frames_since_init = 0; int init_index = 0; - while (ReadNextMessage(use_dump_file, dump_input_file_, input, event_msg)) { + while (ReadMessageFromFile(dump_input_file_, &event_msg)) { SelectivelyToggleDataDumping(init_index, capture_frames_since_init); HandleEvent(event_msg, capture_frames_since_init, init_index); @@ -283,28 +261,19 @@ void AecDumpBasedSimulator::Process() { *settings_.init_to_process >= init_index); } - if (use_dump_file) { - fclose(dump_input_file_); - } + fclose(dump_input_file_); DetachAecDump(); } void AecDumpBasedSimulator::Analyze() { - const bool use_dump_file = !settings_.aec_dump_input_string.has_value(); - std::stringstream input; - if (use_dump_file) { - dump_input_file_ = - OpenFile(settings_.aec_dump_input_filename->c_str(), "rb"); - } else { - input << settings_.aec_dump_input_string.value(); - } + dump_input_file_ = OpenFile(settings_.aec_dump_input_filename->c_str(), "rb"); audioproc::Event event_msg; int num_capture_frames = 0; int num_render_frames = 0; int init_index = 0; - while (ReadNextMessage(use_dump_file, dump_input_file_, input, event_msg)) { + while (ReadMessageFromFile(dump_input_file_, &event_msg)) { if (event_msg.type() == audioproc::Event::INIT) { ++init_index; constexpr float kNumFramesPerSecond = 100.f; @@ -325,9 +294,7 @@ void AecDumpBasedSimulator::Analyze() { } } - if (use_dump_file) { - fclose(dump_input_file_); - } + fclose(dump_input_file_); } void AecDumpBasedSimulator::HandleEvent(const audioproc::Event& event_msg, @@ -384,29 +351,6 @@ void AecDumpBasedSimulator::HandleMessage(const audioproc::Config& msg) { } } - if (msg.has_aecm_enabled() || settings_.use_aecm) { - bool enable = - settings_.use_aecm ? *settings_.use_aecm : msg.aecm_enabled(); - apm_config.echo_canceller.enabled |= enable; - apm_config.echo_canceller.mobile_mode = enable; - if (settings_.use_verbose_logging) { - std::cout << " aecm_enabled: " << (enable ? "true" : "false") - << std::endl; - } - } - - if (msg.has_aecm_comfort_noise_enabled() && - msg.aecm_comfort_noise_enabled()) { - RTC_LOG(LS_ERROR) << "Ignoring deprecated setting: AECM comfort noise"; - } - - if (msg.has_aecm_routing_mode() && - static_cast( - msg.aecm_routing_mode()) != EchoControlMobileImpl::kSpeakerphone) { - RTC_LOG(LS_ERROR) << "Ignoring deprecated setting: AECM routing mode: " - << msg.aecm_routing_mode(); - } - if (msg.has_agc_enabled() || settings_.use_agc) { bool enable = settings_.use_agc ? *settings_.use_agc : msg.agc_enabled(); apm_config.gain_controller1.enabled = enable; diff --git a/modules/audio_processing/test/apmtest.m b/modules/audio_processing/test/apmtest.m index 1367295d5db..0bd739a8498 100644 --- a/modules/audio_processing/test/apmtest.m +++ b/modules/audio_processing/test/apmtest.m @@ -23,7 +23,6 @@ function apmtest(task, testname, filepath, casenumber, legacy) % 'apm' The standard APM test set (default). % 'apmm' The mobile APM test set. % 'aec' The AEC test set. -% 'aecm' The AECM test set. % 'agc' The AGC test set. % 'ns' The NS test set. % 'vad' The VAD test set. @@ -68,7 +67,7 @@ function apmtest(task, testname, filepath, casenumber, legacy) refpath = [filepath 'reference/']; if strcmp(testname, 'all') - tests = {'apm','apmm','aec','aecm','agc','ns','vad'}; + tests = {'apm','apmm','aec','agc','ns','vad'}; else tests = {testname}; end diff --git a/modules/audio_processing/test/audio_buffer_tools.cc b/modules/audio_processing/test/audio_buffer_tools.cc index 8c9f58ed830..1fa7d86bf06 100644 --- a/modules/audio_processing/test/audio_buffer_tools.cc +++ b/modules/audio_processing/test/audio_buffer_tools.cc @@ -11,9 +11,9 @@ #include "modules/audio_processing/test/audio_buffer_tools.h" #include +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "modules/audio_processing/audio_buffer.h" #include "rtc_base/checks.h" @@ -33,7 +33,7 @@ void SetupFrame(const StreamConfig& stream_config, } void CopyVectorToAudioBuffer(const StreamConfig& stream_config, - ArrayView source, + std::span source, AudioBuffer* destination) { std::vector input; std::vector input_samples; diff --git a/modules/audio_processing/test/audio_buffer_tools.h b/modules/audio_processing/test/audio_buffer_tools.h index a324bb23cad..8bdebe041e4 100644 --- a/modules/audio_processing/test/audio_buffer_tools.h +++ b/modules/audio_processing/test/audio_buffer_tools.h @@ -11,9 +11,9 @@ #ifndef MODULES_AUDIO_PROCESSING_TEST_AUDIO_BUFFER_TOOLS_H_ #define MODULES_AUDIO_PROCESSING_TEST_AUDIO_BUFFER_TOOLS_H_ +#include #include -#include "api/array_view.h" #include "api/audio/audio_processing.h" #include "modules/audio_processing/audio_buffer.h" @@ -22,7 +22,7 @@ namespace test { // Copies a vector into an audiobuffer. void CopyVectorToAudioBuffer(const StreamConfig& stream_config, - ArrayView source, + std::span source, AudioBuffer* destination); // Extracts a vector from an audiobuffer. diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc index 914edb775fd..aa1af1737cc 100644 --- a/modules/audio_processing/test/audio_processing_simulator.cc +++ b/modules/audio_processing/test/audio_processing_simulator.cc @@ -222,11 +222,7 @@ void AudioProcessingSimulator::ProcessStream(bool fixed_interface) { applied_input_volume_ = ap_->recommended_stream_analog_level(); } - if (buffer_memory_writer_) { - RTC_CHECK(!buffer_file_writer_); - buffer_memory_writer_->Write(*out_buf_); - } else if (buffer_file_writer_) { - RTC_CHECK(!buffer_memory_writer_); + if (buffer_file_writer_) { buffer_file_writer_->Write(*out_buf_); } @@ -373,9 +369,6 @@ void AudioProcessingSimulator::SetupOutput() { static_cast(out_config_.num_channels()), settings_.wav_output_format)); buffer_file_writer_.reset(new ChannelBufferWavWriter(std::move(out_file))); - } else if (settings_.aec_dump_input_string.has_value()) { - buffer_memory_writer_ = std::make_unique( - settings_.processed_capture_samples); } if (settings_.linear_aec_output_filename) { @@ -434,6 +427,16 @@ void AudioProcessingSimulator::ConfigureAudioProcessor() { *settings_.multi_channel_capture; } + if (settings_.use_adaptive_stereo_downmixing_for_aec) { + if (*settings_.use_adaptive_stereo_downmixing_for_aec) { + apm_config.pipeline.capture_downmix_method_stereo_aec = + AudioProcessing::Config::Pipeline::DownmixMethod::kAdaptive; + } else { + apm_config.pipeline.capture_downmix_method_stereo_aec = + AudioProcessing::Config::Pipeline::DownmixMethod::kAverageChannels; + } + } + if (settings_.use_agc2) { apm_config.gain_controller2.enabled = *settings_.use_agc2; if (settings_.agc2_fixed_gain_db) { @@ -486,10 +489,8 @@ void AudioProcessingSimulator::ConfigureAudioProcessor() { } const bool use_aec = settings_.use_aec && *settings_.use_aec; - const bool use_aecm = settings_.use_aecm && *settings_.use_aecm; - if (use_aec || use_aecm) { + if (use_aec) { apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.mobile_mode = use_aecm; } apm_config.echo_canceller.export_linear_aec_output = !!settings_.linear_aec_output_filename; diff --git a/modules/audio_processing/test/audio_processing_simulator.h b/modules/audio_processing/test/audio_processing_simulator.h index 6ab26a5074f..1bf5c1b49f9 100644 --- a/modules/audio_processing/test/audio_processing_simulator.h +++ b/modules/audio_processing/test/audio_processing_simulator.h @@ -21,7 +21,6 @@ #include #include "absl/base/nullability.h" -#include "absl/strings/string_view.h" #include "api/audio/audio_processing.h" #include "api/scoped_refptr.h" #include "common_audio/channel_buffer.h" @@ -99,7 +98,6 @@ struct SimulationSettings { std::optional artificial_nearend_filename; std::optional linear_aec_output_filename; std::optional use_aec; - std::optional use_aecm; std::optional use_ed; // Residual Echo Detector. std::optional ed_graph_output_filename; std::optional use_agc; @@ -153,13 +151,12 @@ struct SimulationSettings { std::optional call_order_input_filename; std::optional call_order_output_filename; std::optional aec_settings_filename; - std::optional aec_dump_input_string; - std::vector* processed_capture_samples = nullptr; bool analysis_only = false; std::optional dump_start_frame; std::optional dump_end_frame; std::optional init_to_process; std::optional neural_echo_residual_estimator_model; + std::optional use_adaptive_stereo_downmixing_for_aec; }; // State used by the audio processor, but not owned by it. @@ -244,7 +241,6 @@ class AudioProcessingSimulator { size_t num_reverse_process_stream_calls_ = 0; std::unique_ptr buffer_file_writer_; std::unique_ptr reverse_buffer_file_writer_; - std::unique_ptr buffer_memory_writer_; std::unique_ptr linear_aec_output_file_writer_; ApiCallStatistics api_call_statistics_; std::ofstream residual_echo_likelihood_graph_writer_; diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc index b44ed19932b..39ee1766e91 100644 --- a/modules/audio_processing/test/audioproc_float_impl.cc +++ b/modules/audio_processing/test/audioproc_float_impl.cc @@ -81,10 +81,6 @@ ABSL_FLAG(int, aec, kParameterNotSpecifiedValue, "Activate (1) or deactivate (0) the echo canceller"); -ABSL_FLAG(int, - aecm, - kParameterNotSpecifiedValue, - "Activate (1) or deactivate (0) the mobile echo controller"); ABSL_FLAG(int, ed, kParameterNotSpecifiedValue, @@ -136,6 +132,11 @@ ABSL_FLAG(bool, false, "Activate all of the default components (will be overridden by any " "other settings)"); +ABSL_FLAG(int, + use_adaptive_stereo_downmixing_for_aec, + kParameterNotSpecifiedValue, + "Activate (1) or deactivate (0) adaptive downmixing for stereo " + "microphones when aec is active"); ABSL_FLAG(int, analog_agc_use_digital_adaptive_controller, kParameterNotSpecifiedValue, @@ -399,7 +400,6 @@ SimulationSettings CreateSettings() { settings.use_agc2 = false; settings.use_pre_amplifier = false; settings.use_aec = true; - settings.use_aecm = false; settings.use_ed = false; } SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_input), @@ -425,7 +425,6 @@ SimulationSettings CreateSettings() { SetSettingIfSpecified(absl::GetFlag(FLAGS_reverse_output_sample_rate_hz), &settings.reverse_output_sample_rate_hz); SetSettingIfFlagSet(absl::GetFlag(FLAGS_aec), &settings.use_aec); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_aecm), &settings.use_aecm); SetSettingIfFlagSet(absl::GetFlag(FLAGS_ed), &settings.use_ed); SetSettingIfSpecified(absl::GetFlag(FLAGS_ed_graph), &settings.ed_graph_output_filename); @@ -501,6 +500,9 @@ SimulationSettings CreateSettings() { settings.report_performance = absl::GetFlag(FLAGS_performance_report); SetSettingIfSpecified(absl::GetFlag(FLAGS_performance_report_output_file), &settings.performance_report_output_filename); + SetSettingIfFlagSet( + absl::GetFlag(FLAGS_use_adaptive_stereo_downmixing_for_aec), + &settings.use_adaptive_stereo_downmixing_for_aec); settings.use_verbose_logging = absl::GetFlag(FLAGS_verbose); settings.use_quiet_output = absl::GetFlag(FLAGS_quiet); settings.report_bitexactness = absl::GetFlag(FLAGS_bitexactness_report); @@ -563,11 +565,6 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) { "Error: The aec dump file cannot be specified " "together with input wav files!\n"); - ReportConditionalErrorAndExit( - !!settings.aec_dump_input_string, - "Error: The aec dump input string cannot be specified " - "together with input wav files!\n"); - ReportConditionalErrorAndExit(!!settings.artificial_nearend_filename, "Error: The artificial nearend cannot be " "specified together with input wav files!\n"); @@ -583,13 +580,8 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) { "must be specified if the reverse output wav filename is specified!\n"); } else { ReportConditionalErrorAndExit( - !settings.aec_dump_input_filename && !settings.aec_dump_input_string, - "Error: Either the aec dump input file, the wav " - "input file or the aec dump input string must be specified!\n"); - ReportConditionalErrorAndExit( - settings.aec_dump_input_filename && settings.aec_dump_input_string, - "Error: The aec dump input file cannot be specified together with the " - "aec dump input string!\n"); + !settings.aec_dump_input_filename, + "Error: The aec dump input file must be specified!\n"); } ReportConditionalErrorAndExit(settings.use_aec && !(*settings.use_aec) && @@ -597,11 +589,6 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) { "Error: The linear AEC ouput filename cannot " "be specified without the AEC being active"); - ReportConditionalErrorAndExit( - settings.use_aec && *settings.use_aec && settings.use_aecm && - *settings.use_aecm, - "Error: The AEC and the AECM cannot be activated at the same time!\n"); - ReportConditionalErrorAndExit( settings.output_sample_rate_hz && *settings.output_sample_rate_hz <= 0, "Error: --output_sample_rate_hz must be positive!\n"); @@ -793,7 +780,6 @@ void SetDependencies(const SimulationSettings& settings, BuiltinAudioProcessingBuilder& builder, AudioProcessingBuilderState& builder_state) { EchoCanceller3Config aec3_config; - std::optional aec3_multichannel_config; if (settings.neural_echo_residual_estimator_model) { tflite::ops::builtin::BuiltinOpResolver op_resolver; builder_state.model = tflite::FlatBufferModel::BuildFromFile( @@ -802,9 +788,6 @@ void SetDependencies(const SimulationSettings& settings, std::unique_ptr estimator = CreateNeuralResidualEchoEstimator(builder_state.model.get(), &op_resolver); - aec3_config = estimator->GetConfiguration(/*multi_channel=*/false); - aec3_multichannel_config = - estimator->GetConfiguration(/*multi_channel=*/true); RTC_CHECK(estimator); builder.SetNeuralResidualEchoEstimator(std::move(estimator)); } @@ -826,7 +809,7 @@ void SetDependencies(const SimulationSettings& settings, } std::cout << Aec3ConfigToJsonString(aec3_config) << std::endl; } - builder.SetEchoCancellerConfig(aec3_config, aec3_multichannel_config); + builder.SetEchoCancellerConfig(aec3_config, std::nullopt); if (settings.use_ed && *settings.use_ed) { builder.SetEchoDetector(CreateEchoDetector()); @@ -860,7 +843,7 @@ int RunSimulation( RTC_CHECK(audio_processing); std::unique_ptr processor; - if (settings.aec_dump_input_filename || settings.aec_dump_input_string) { + if (settings.aec_dump_input_filename) { processor = std::make_unique( settings, std::move(audio_processing)); } else { diff --git a/modules/audio_processing/test/bitexactness_tools.cc b/modules/audio_processing/test/bitexactness_tools.cc index 64538b3feaf..01ba02cfd63 100644 --- a/modules/audio_processing/test/bitexactness_tools.cc +++ b/modules/audio_processing/test/bitexactness_tools.cc @@ -15,10 +15,10 @@ #include #include #include // no-presubmit-check TODO(webrtc:8982) +#include #include #include -#include "api/array_view.h" #include "modules/audio_coding/neteq/tools/input_audio_file.h" #include "rtc_base/checks.h" #include "test/gtest.h" @@ -62,7 +62,7 @@ std::string GetApmCaptureTestVectorFileName(int sample_rate_hz) { void ReadFloatSamplesFromStereoFile(size_t samples_per_channel, size_t num_channels, InputAudioFile* stereo_pcm_file, - ArrayView data) { + std::span data) { RTC_DCHECK_LE(num_channels, 2); RTC_DCHECK_EQ(data.size(), samples_per_channel * num_channels); std::vector read_samples(samples_per_channel * 2); @@ -80,8 +80,8 @@ void ReadFloatSamplesFromStereoFile(size_t samples_per_channel, ::testing::AssertionResult VerifyDeinterleavedArray( size_t samples_per_channel, size_t num_channels, - ArrayView reference, - ArrayView output, + std::span reference, + std::span output, float element_error_bound) { // Form vectors to compare the reference to. Only the first values of the // outputs are compared in order not having to specify all preceeding frames @@ -100,8 +100,8 @@ ::testing::AssertionResult VerifyDeinterleavedArray( return VerifyArray(reference, output_to_verify, element_error_bound); } -::testing::AssertionResult VerifyArray(ArrayView reference, - ArrayView output, +::testing::AssertionResult VerifyArray(std::span reference, + std::span output, float element_error_bound) { // The vectors are deemed to be bitexact only if // a) output have a size at least as long as the reference. @@ -127,7 +127,7 @@ ::testing::AssertionResult VerifyArray(ArrayView reference, // Lambda function that produces a formatted string with the data in the // vector. - auto print_vector_in_c_format = [](ArrayView v, + auto print_vector_in_c_format = [](std::span v, size_t num_values_to_print) { std::string s = "{ "; for (size_t k = 0; k < std::min(num_values_to_print, v.size()); ++k) { diff --git a/modules/audio_processing/test/bitexactness_tools.h b/modules/audio_processing/test/bitexactness_tools.h index f87658e3c65..6f8a1f47c54 100644 --- a/modules/audio_processing/test/bitexactness_tools.h +++ b/modules/audio_processing/test/bitexactness_tools.h @@ -13,9 +13,9 @@ #define MODULES_AUDIO_PROCESSING_TEST_BITEXACTNESS_TOOLS_H_ #include +#include #include -#include "api/array_view.h" #include "modules/audio_coding/neteq/tools/input_audio_file.h" #include "test/gtest.h" @@ -34,21 +34,21 @@ std::string GetApmCaptureTestVectorFileName(int sample_rate_hz); void ReadFloatSamplesFromStereoFile(size_t samples_per_channel, size_t num_channels, InputAudioFile* stereo_pcm_file, - ArrayView data); + std::span data); // Verifies a frame against a reference and returns the results as an // AssertionResult. ::testing::AssertionResult VerifyDeinterleavedArray( size_t samples_per_channel, size_t num_channels, - ArrayView reference, - ArrayView output, + std::span reference, + std::span output, float element_error_bound); // Verifies a vector against a reference and returns the results as an // AssertionResult. -::testing::AssertionResult VerifyArray(ArrayView reference, - ArrayView output, +::testing::AssertionResult VerifyArray(std::span reference, + std::span output, float element_error_bound); } // namespace test diff --git a/modules/audio_processing/test/conversational_speech/BUILD.gn b/modules/audio_processing/test/conversational_speech/BUILD.gn index 28f27daae42..19b8beebdae 100644 --- a/modules/audio_processing/test/conversational_speech/BUILD.gn +++ b/modules/audio_processing/test/conversational_speech/BUILD.gn @@ -21,7 +21,6 @@ if (!build_with_chromium) { ":lib", "../../../../rtc_base:checks", "../../../../test:fileutils", - "../../../../test:test_support", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] @@ -45,7 +44,6 @@ rtc_library("lib") { "wavreader_interface.h", ] deps = [ - "../../../../api:array_view", "../../../../common_audio", "../../../../rtc_base:checks", "../../../../rtc_base:logging", @@ -68,7 +66,6 @@ rtc_library("unittest") { ] deps = [ ":lib", - "../../../../api:array_view", "../../../../common_audio", "../../../../rtc_base:logging", "../../../../rtc_base:safe_conversions", diff --git a/modules/audio_processing/test/conversational_speech/OWNERS b/modules/audio_processing/test/conversational_speech/OWNERS index 07cff405e6c..18a48034b1f 100644 --- a/modules/audio_processing/test/conversational_speech/OWNERS +++ b/modules/audio_processing/test/conversational_speech/OWNERS @@ -1,3 +1,2 @@ -alessiob@webrtc.org henrik.lundin@webrtc.org peah@webrtc.org diff --git a/modules/audio_processing/test/conversational_speech/mock_wavreader.h b/modules/audio_processing/test/conversational_speech/mock_wavreader.h index 32acec60037..a1881586b3c 100644 --- a/modules/audio_processing/test/conversational_speech/mock_wavreader.h +++ b/modules/audio_processing/test/conversational_speech/mock_wavreader.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/audio_processing/test/conversational_speech/wavreader_interface.h" #include "test/gmock.h" @@ -27,11 +27,8 @@ class MockWavReader : public WavReaderInterface { MockWavReader(int sample_rate, size_t num_channels, size_t num_samples); ~MockWavReader() override; - MOCK_METHOD(size_t, ReadFloatSamples, (webrtc::ArrayView), (override)); - MOCK_METHOD(size_t, - ReadInt16Samples, - (webrtc::ArrayView), - (override)); + MOCK_METHOD(size_t, ReadFloatSamples, (std::span), (override)); + MOCK_METHOD(size_t, ReadInt16Samples, (std::span), (override)); MOCK_METHOD(int, SampleRate, (), (const, override)); MOCK_METHOD(size_t, NumChannels, (), (const, override)); diff --git a/modules/audio_processing/test/conversational_speech/multiend_call.cc b/modules/audio_processing/test/conversational_speech/multiend_call.cc index d0b5d7cf11c..bf3f19c9b65 100644 --- a/modules/audio_processing/test/conversational_speech/multiend_call.cc +++ b/modules/audio_processing/test/conversational_speech/multiend_call.cc @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/test/conversational_speech/timing.h" #include "modules/audio_processing/test/conversational_speech/wavreader_abstract_factory.h" #include "modules/audio_processing/test/conversational_speech/wavreader_interface.h" @@ -34,7 +34,7 @@ namespace test { namespace conversational_speech { MultiEndCall::MultiEndCall( - ArrayView timing, + std::span timing, absl::string_view audiotracks_path, std::unique_ptr wavreader_abstract_factory) : timing_(timing), diff --git a/modules/audio_processing/test/conversational_speech/multiend_call.h b/modules/audio_processing/test/conversational_speech/multiend_call.h index 4ea53c9ff5a..fbf926bdf09 100644 --- a/modules/audio_processing/test/conversational_speech/multiend_call.h +++ b/modules/audio_processing/test/conversational_speech/multiend_call.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/audio_processing/test/conversational_speech/timing.h" #include "modules/audio_processing/test/conversational_speech/wavreader_abstract_factory.h" #include "modules/audio_processing/test/conversational_speech/wavreader_interface.h" @@ -51,7 +51,7 @@ class MultiEndCall { }; MultiEndCall( - ArrayView timing, + std::span timing, absl::string_view audiotracks_path, std::unique_ptr wavreader_abstract_factory); ~MultiEndCall(); @@ -84,7 +84,7 @@ class MultiEndCall { // only up to 2 speakers. Rejects unordered turns and self cross-talk. bool CheckTiming(); - ArrayView timing_; + std::span timing_; std::string audiotracks_path_; std::unique_ptr wavreader_abstract_factory_; std::set speaker_names_; diff --git a/modules/audio_processing/test/conversational_speech/simulator.cc b/modules/audio_processing/test/conversational_speech/simulator.cc index f95b508cc32..2dcfbdbd2a3 100644 --- a/modules/audio_processing/test/conversational_speech/simulator.cc +++ b/modules/audio_processing/test/conversational_speech/simulator.cc @@ -16,13 +16,13 @@ #include #include #include +#include #include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "common_audio/include/audio_util.h" #include "common_audio/wav_file.h" #include "modules/audio_processing/test/conversational_speech/multiend_call.h" @@ -134,7 +134,7 @@ std::unique_ptr>> PreloadAudioTracks( // previously written samples in `wav_writer` is less than `interval_begin`, it // adds zeros as left padding. The padding corresponds to intervals during which // a speaker is not active. -void PadLeftWriteChunk(ArrayView source_samples, +void PadLeftWriteChunk(std::span source_samples, size_t interval_begin, WavWriter* wav_writer) { // Add left padding. @@ -163,9 +163,9 @@ void PadRightWrite(WavWriter* wav_writer, size_t pad_samples) { } } -void ScaleSignal(ArrayView source_samples, +void ScaleSignal(std::span source_samples, int gain, - ArrayView output_samples) { + std::span output_samples) { const float gain_linear = DbToRatio(gain); RTC_DCHECK_EQ(source_samples.size(), output_samples.size()); std::transform(source_samples.begin(), source_samples.end(), diff --git a/modules/audio_processing/test/conversational_speech/timing.cc b/modules/audio_processing/test/conversational_speech/timing.cc index 6f21113a090..bd1998ed405 100644 --- a/modules/audio_processing/test/conversational_speech/timing.cc +++ b/modules/audio_processing/test/conversational_speech/timing.cc @@ -12,11 +12,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/string_encode.h" #include "rtc_base/string_to_number.h" @@ -62,7 +62,7 @@ std::vector LoadTiming(absl::string_view timing_filepath) { } void SaveTiming(absl::string_view timing_filepath, - ArrayView timing) { + std::span timing) { std::ofstream outfile(std::string{timing_filepath}); RTC_CHECK(outfile.is_open()); for (const Turn& turn : timing) { diff --git a/modules/audio_processing/test/conversational_speech/timing.h b/modules/audio_processing/test/conversational_speech/timing.h index 56e19c6e8e5..edf8cd6bcec 100644 --- a/modules/audio_processing/test/conversational_speech/timing.h +++ b/modules/audio_processing/test/conversational_speech/timing.h @@ -11,11 +11,11 @@ #ifndef MODULES_AUDIO_PROCESSING_TEST_CONVERSATIONAL_SPEECH_TIMING_H_ #define MODULES_AUDIO_PROCESSING_TEST_CONVERSATIONAL_SPEECH_TIMING_H_ +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" namespace webrtc { namespace test { @@ -42,7 +42,7 @@ std::vector LoadTiming(absl::string_view timing_filepath); // Writes a list of turns into a file. void SaveTiming(absl::string_view timing_filepath, - ArrayView timing); + std::span timing); } // namespace conversational_speech } // namespace test diff --git a/modules/audio_processing/test/conversational_speech/wavreader_factory.cc b/modules/audio_processing/test/conversational_speech/wavreader_factory.cc index 0a24f4f2e09..63f4d68f3e2 100644 --- a/modules/audio_processing/test/conversational_speech/wavreader_factory.cc +++ b/modules/audio_processing/test/conversational_speech/wavreader_factory.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "common_audio/wav_file.h" #include "modules/audio_processing/test/conversational_speech/wavreader_interface.h" @@ -31,12 +31,12 @@ class WavReaderAdaptor final : public WavReaderInterface { : wav_reader_(filepath) {} ~WavReaderAdaptor() override = default; - size_t ReadFloatSamples(ArrayView samples) override { - return wav_reader_.ReadSamples(samples.size(), samples.begin()); + size_t ReadFloatSamples(std::span samples) override { + return wav_reader_.ReadSamples(samples.size(), samples.data()); } - size_t ReadInt16Samples(ArrayView samples) override { - return wav_reader_.ReadSamples(samples.size(), samples.begin()); + size_t ReadInt16Samples(std::span samples) override { + return wav_reader_.ReadSamples(samples.size(), samples.data()); } int SampleRate() const override { return wav_reader_.sample_rate(); } diff --git a/modules/audio_processing/test/conversational_speech/wavreader_interface.h b/modules/audio_processing/test/conversational_speech/wavreader_interface.h index c589c579d45..012c70497f5 100644 --- a/modules/audio_processing/test/conversational_speech/wavreader_interface.h +++ b/modules/audio_processing/test/conversational_speech/wavreader_interface.h @@ -13,8 +13,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { namespace test { @@ -25,8 +24,8 @@ class WavReaderInterface { virtual ~WavReaderInterface() = default; // Returns the number of samples read. - virtual size_t ReadFloatSamples(ArrayView samples) = 0; - virtual size_t ReadInt16Samples(ArrayView samples) = 0; + virtual size_t ReadFloatSamples(std::span samples) = 0; + virtual size_t ReadInt16Samples(std::span samples) = 0; // Getters. virtual int SampleRate() const = 0; diff --git a/modules/audio_processing/test/debug_dump_replayer.cc b/modules/audio_processing/test/debug_dump_replayer.cc index 1b2f2ae1f72..ca093679401 100644 --- a/modules/audio_processing/test/debug_dump_replayer.cc +++ b/modules/audio_processing/test/debug_dump_replayer.cc @@ -204,11 +204,9 @@ void DebugDumpReplayer::MaybeRecreateApm(const audioproc::Config& msg) { void DebugDumpReplayer::ConfigureApm(const audioproc::Config& msg) { AudioProcessing::Config apm_config; - // AEC2/AECM configs. + // AEC2 configs. RTC_CHECK(msg.has_aec_enabled()); - RTC_CHECK(msg.has_aecm_enabled()); - apm_config.echo_canceller.enabled = msg.aec_enabled() || msg.aecm_enabled(); - apm_config.echo_canceller.mobile_mode = msg.aecm_enabled(); + apm_config.echo_canceller.enabled = msg.aec_enabled(); // HPF configs. RTC_CHECK(msg.has_hpf_enabled()); diff --git a/modules/audio_processing/test/echo_canceller_test_tools.cc b/modules/audio_processing/test/echo_canceller_test_tools.cc index 0d0e4077d6b..1e0c84956bc 100644 --- a/modules/audio_processing/test/echo_canceller_test_tools.cc +++ b/modules/audio_processing/test/echo_canceller_test_tools.cc @@ -12,20 +12,20 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/random.h" namespace webrtc { -void RandomizeSampleVector(Random* random_generator, ArrayView v) { +void RandomizeSampleVector(Random* random_generator, std::span v) { RandomizeSampleVector(random_generator, v, /*amplitude=*/32767.f); } void RandomizeSampleVector(Random* random_generator, - ArrayView v, + std::span v, float amplitude) { for (auto& v_k : v) { v_k = 2 * amplitude * random_generator->Rand() - amplitude; @@ -33,7 +33,7 @@ void RandomizeSampleVector(Random* random_generator, } template -void DelayBuffer::Delay(ArrayView x, ArrayView x_delayed) { +void DelayBuffer::Delay(std::span x, std::span x_delayed) { RTC_DCHECK_EQ(x.size(), x_delayed.size()); if (buffer_.empty()) { std::copy(x.begin(), x.end(), x_delayed.begin()); diff --git a/modules/audio_processing/test/echo_canceller_test_tools.h b/modules/audio_processing/test/echo_canceller_test_tools.h index f8a805a7267..cb5ef8514f0 100644 --- a/modules/audio_processing/test/echo_canceller_test_tools.h +++ b/modules/audio_processing/test/echo_canceller_test_tools.h @@ -12,19 +12,19 @@ #define MODULES_AUDIO_PROCESSING_TEST_ECHO_CANCELLER_TEST_TOOLS_H_ #include +#include #include -#include "api/array_view.h" #include "rtc_base/random.h" namespace webrtc { // Randomizes the elements in a vector with values -32767.f:32767.f. -void RandomizeSampleVector(Random* random_generator, ArrayView v); +void RandomizeSampleVector(Random* random_generator, std::span v); // Randomizes the elements in a vector with values -amplitude:amplitude. void RandomizeSampleVector(Random* random_generator, - ArrayView v, + std::span v, float amplitude); // Class for delaying a signal a fixed number of samples. @@ -35,7 +35,7 @@ class DelayBuffer { ~DelayBuffer() = default; // Produces a delayed signal copy of x. - void Delay(ArrayView x, ArrayView x_delayed); + void Delay(std::span x, std::span x_delayed); private: std::vector buffer_; diff --git a/modules/audio_processing/test/echo_canceller_test_tools_unittest.cc b/modules/audio_processing/test/echo_canceller_test_tools_unittest.cc index b22bd452a01..c1340c8bcdc 100644 --- a/modules/audio_processing/test/echo_canceller_test_tools_unittest.cc +++ b/modules/audio_processing/test/echo_canceller_test_tools_unittest.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/random.h" #include "test/gtest.h" @@ -32,8 +32,8 @@ TEST(EchoCancellerTestTools, FloatDelayBuffer) { constexpr size_t kBlockSize = 50; for (size_t k = 0; k < CheckedDivExact(v.size(), kBlockSize); ++k) { delay_buffer.Delay( - ArrayView(&v[k * kBlockSize], kBlockSize), - ArrayView(&v_delayed[k * kBlockSize], kBlockSize)); + std::span(&v[k * kBlockSize], kBlockSize), + std::span(&v_delayed[k * kBlockSize], kBlockSize)); } for (size_t k = kDelay; k < v.size(); ++k) { EXPECT_EQ(v[k - kDelay], v_delayed[k]); @@ -50,8 +50,8 @@ TEST(EchoCancellerTestTools, IntDelayBuffer) { std::vector v_delayed = v; const size_t kBlockSize = 50; for (size_t k = 0; k < CheckedDivExact(v.size(), kBlockSize); ++k) { - delay_buffer.Delay(ArrayView(&v[k * kBlockSize], kBlockSize), - ArrayView(&v_delayed[k * kBlockSize], kBlockSize)); + delay_buffer.Delay(std::span(&v[k * kBlockSize], kBlockSize), + std::span(&v_delayed[k * kBlockSize], kBlockSize)); } for (size_t k = kDelay; k < v.size(); ++k) { EXPECT_EQ(v[k - kDelay], v_delayed[k]); diff --git a/modules/audio_processing/test/fake_recording_device.cc b/modules/audio_processing/test/fake_recording_device.cc index 6706fe56acf..a3f90ff85ab 100644 --- a/modules/audio_processing/test/fake_recording_device.cc +++ b/modules/audio_processing/test/fake_recording_device.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "common_audio/channel_buffer.h" #include "common_audio/include/audio_util.h" #include "modules/audio_processing/agc2/gain_map_internal.h" @@ -43,7 +43,7 @@ class FakeRecordingDeviceWorker { void set_mic_level(const int level) { mic_level_ = level; } void set_undo_mic_level(const int level) { undo_mic_level_ = level; } virtual ~FakeRecordingDeviceWorker() = default; - virtual void ModifyBufferInt16(ArrayView buffer) = 0; + virtual void ModifyBufferInt16(std::span buffer) = 0; virtual void ModifyBufferFloat(ChannelBuffer* buffer) = 0; protected: @@ -62,7 +62,7 @@ class FakeRecordingDeviceIdentity final : public FakeRecordingDeviceWorker { explicit FakeRecordingDeviceIdentity(const int initial_mic_level) : FakeRecordingDeviceWorker(initial_mic_level) {} ~FakeRecordingDeviceIdentity() override = default; - void ModifyBufferInt16(ArrayView /* buffer */) override {} + void ModifyBufferInt16(std::span /* buffer */) override {} void ModifyBufferFloat(ChannelBuffer* /* buffer */) override {} }; @@ -73,7 +73,7 @@ class FakeRecordingDeviceLinear final : public FakeRecordingDeviceWorker { explicit FakeRecordingDeviceLinear(const int initial_mic_level) : FakeRecordingDeviceWorker(initial_mic_level) {} ~FakeRecordingDeviceLinear() override = default; - void ModifyBufferInt16(ArrayView buffer) override { + void ModifyBufferInt16(std::span buffer) override { const size_t number_of_samples = buffer.size(); int16_t* data = buffer.data(); // If an undo level is specified, virtually restore the unmodified @@ -115,7 +115,7 @@ class FakeRecordingDeviceAgc final : public FakeRecordingDeviceWorker { explicit FakeRecordingDeviceAgc(const int initial_mic_level) : FakeRecordingDeviceWorker(initial_mic_level) {} ~FakeRecordingDeviceAgc() override = default; - void ModifyBufferInt16(ArrayView buffer) override { + void ModifyBufferInt16(std::span buffer) override { const float scaling_factor = ComputeAgcLinearFactor(undo_mic_level_, mic_level_); const size_t number_of_samples = buffer.size(); @@ -178,7 +178,7 @@ void FakeRecordingDevice::SetUndoMicLevel(const int level) { worker_->set_undo_mic_level(level); } -void FakeRecordingDevice::SimulateAnalogGain(ArrayView buffer) { +void FakeRecordingDevice::SimulateAnalogGain(std::span buffer) { RTC_DCHECK(worker_); worker_->ModifyBufferInt16(buffer); } diff --git a/modules/audio_processing/test/fake_recording_device.h b/modules/audio_processing/test/fake_recording_device.h index 0fcb4d18ee5..84c34ff74af 100644 --- a/modules/audio_processing/test/fake_recording_device.h +++ b/modules/audio_processing/test/fake_recording_device.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "common_audio/channel_buffer.h" namespace webrtc { @@ -53,7 +53,7 @@ class FakeRecordingDevice final { // If `real_device_level` is a valid level, the unmodified mic signal is // virtually restored. To skip the latter step set `real_device_level` to // an empty value. - void SimulateAnalogGain(ArrayView buffer); + void SimulateAnalogGain(std::span buffer); // Simulates the analog gain. // If `real_device_level` is a valid level, the unmodified mic signal is diff --git a/modules/audio_processing/test/protobuf_utils.cc b/modules/audio_processing/test/protobuf_utils.cc index a9195f299fc..227966e4b3d 100644 --- a/modules/audio_processing/test/protobuf_utils.cc +++ b/modules/audio_processing/test/protobuf_utils.cc @@ -14,33 +14,10 @@ #include #include #include -#include // no-presubmit-check TODO(webrtc:8982) #include "rtc_base/protobuf_utils.h" #include "rtc_base/system/arch.h" -namespace { -// Allocates new memory in the memory owned by the unique_ptr to fit the raw -// message and returns the number of bytes read when having a string stream as -// input. -size_t ReadMessageBytesFromString(std::stringstream* input, - std::unique_ptr* bytes) { - int32_t size = 0; - input->read(reinterpret_cast(&size), sizeof(int32_t)); - int32_t size_read = input->gcount(); - if (size_read != sizeof(int32_t)) - return 0; - if (size <= 0) - return 0; - - *bytes = std::make_unique(size); - input->read(reinterpret_cast(bytes->get()), - size * sizeof((*bytes)[0])); - size_read = input->gcount(); - return size_read == size ? size : 0; -} -} // namespace - namespace webrtc { size_t ReadMessageBytesFromFile(FILE* file, std::unique_ptr* bytes) { @@ -70,15 +47,4 @@ bool ReadMessageFromFile(FILE* file, MessageLite* msg) { return msg->ParseFromArray(bytes.get(), size); } -// Returns true on success, false on error or end of string stream. -bool ReadMessageFromString(std::stringstream* input, MessageLite* msg) { - std::unique_ptr bytes; - size_t size = ReadMessageBytesFromString(input, &bytes); - if (!size) - return false; - - msg->Clear(); - return msg->ParseFromArray(bytes.get(), size); -} - } // namespace webrtc diff --git a/modules/audio_processing/test/protobuf_utils.h b/modules/audio_processing/test/protobuf_utils.h index 2685a4c75a3..77f14913f14 100644 --- a/modules/audio_processing/test/protobuf_utils.h +++ b/modules/audio_processing/test/protobuf_utils.h @@ -15,7 +15,6 @@ #include #include #include -#include // no-presubmit-check TODO(webrtc:8982) #include "rtc_base/protobuf_utils.h" @@ -31,11 +30,6 @@ size_t ReadMessageBytesFromFile(FILE* file, std::unique_ptr* bytes); // Returns true on success, false on error or end-of-file. bool ReadMessageFromFile(FILE* file, MessageLite* msg); -// Returns true on success, false on error or end of string stream. -bool ReadMessageFromString( - std::stringstream* input, // no-presubmit-check TODO(webrtc:8982) - MessageLite* msg); - } // namespace webrtc #endif // MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_ diff --git a/modules/audio_processing/test/test_utils.cc b/modules/audio_processing/test/test_utils.cc index d4b221d6686..24cf1766265 100644 --- a/modules/audio_processing/test/test_utils.cc +++ b/modules/audio_processing/test/test_utils.cc @@ -113,25 +113,6 @@ void ChannelBufferWavWriter::Write(const ChannelBuffer& buffer) { file_->WriteSamples(&interleaved_[0], interleaved_.size()); } -ChannelBufferVectorWriter::ChannelBufferVectorWriter(std::vector* output) - : output_(output) { - RTC_DCHECK(output_); -} - -ChannelBufferVectorWriter::~ChannelBufferVectorWriter() = default; - -void ChannelBufferVectorWriter::Write(const ChannelBuffer& buffer) { - // Account for sample rate changes throughout a simulation. - interleaved_buffer_.resize(buffer.size()); - InterleavedView view(&interleaved_buffer_[0], buffer.num_frames(), - buffer.num_channels()); - Interleave(buffer.channels(), buffer.num_frames(), buffer.num_channels(), - view); - size_t old_size = output_->size(); - output_->resize(old_size + interleaved_buffer_.size()); - FloatToFloatS16(interleaved_buffer_.data(), interleaved_buffer_.size(), - output_->data() + old_size); -} FILE* OpenFile(absl::string_view filename, absl::string_view mode) { std::string filename_str(filename); diff --git a/modules/audio_processing/test/test_utils.h b/modules/audio_processing/test/test_utils.h index e979c859786..114cd13b10d 100644 --- a/modules/audio_processing/test/test_utils.h +++ b/modules/audio_processing/test/test_utils.h @@ -101,25 +101,6 @@ class ChannelBufferWavWriter final { std::vector interleaved_; }; -// Takes a pointer to a vector. Allows appending the samples of channel buffers -// to the given vector, by interleaving the samples and converting them to float -// S16. -class ChannelBufferVectorWriter final { - public: - explicit ChannelBufferVectorWriter(std::vector* output); - ChannelBufferVectorWriter(const ChannelBufferVectorWriter&) = delete; - ChannelBufferVectorWriter& operator=(const ChannelBufferVectorWriter&) = - delete; - ~ChannelBufferVectorWriter(); - - // Creates an interleaved copy of `buffer`, converts the samples to float S16 - // and appends the result to output_. - void Write(const ChannelBuffer& buffer); - - private: - std::vector interleaved_buffer_; - std::vector* output_; -}; // Exits on failure; do not use in unit tests. FILE* OpenFile(absl::string_view filename, absl::string_view mode); diff --git a/modules/audio_processing/three_band_filter_bank.cc b/modules/audio_processing/three_band_filter_bank.cc index a04852a4c7f..0b12b938b81 100644 --- a/modules/audio_processing/three_band_filter_bank.cc +++ b/modules/audio_processing/three_band_filter_bank.cc @@ -35,8 +35,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -109,11 +109,11 @@ const float kDctModulation[ThreeBandFilterBank::kNumNonZeroFilters][kDctSize] = // Filters the input signal `in` with the filter `filter` using a shift by // `in_shift`, taking into account the previous state. -void FilterCore(ArrayView filter, - ArrayView in, +void FilterCore(std::span filter, + std::span in, const int in_shift, - ArrayView out, - ArrayView state) { + std::span out, + std::span state) { constexpr int kMaxInShift = (kStride - 1); RTC_DCHECK_GE(in_shift, 0); RTC_DCHECK_LE(in_shift, kMaxInShift); @@ -176,8 +176,8 @@ ThreeBandFilterBank::~ThreeBandFilterBank() = default; // of `kSparsity`. // 3. Modulating with cosines and accumulating to get the desired band. void ThreeBandFilterBank::Analysis( - ArrayView in, - ArrayView, ThreeBandFilterBank::kNumBands> out) { + std::span in, + std::span, ThreeBandFilterBank::kNumBands> out) { // Initialize the output to zero. for (int band = 0; band < ThreeBandFilterBank::kNumBands; ++band) { RTC_DCHECK_EQ(out[band].size(), kSplitBandSize); @@ -204,10 +204,10 @@ void ThreeBandFilterBank::Analysis( ? index : (index < kZeroFilterIndex2 ? index - 1 : index - 2); - ArrayView filter(kFilterCoeffs[filter_index]); - ArrayView dct_modulation( + std::span filter(kFilterCoeffs[filter_index]); + std::span dct_modulation( kDctModulation[filter_index]); - ArrayView state(state_analysis_[filter_index]); + std::span state(state_analysis_[filter_index]); // Filter. std::array out_subsampled; @@ -231,8 +231,8 @@ void ThreeBandFilterBank::Analysis( // `kSparsity` signals with different delays. // 3. Parallel to serial upsampling by a factor of `kNumBands`. void ThreeBandFilterBank::Synthesis( - ArrayView, ThreeBandFilterBank::kNumBands> in, - ArrayView out) { + std::span, ThreeBandFilterBank::kNumBands> in, + std::span out) { std::fill(out.begin(), out.end(), 0); for (int upsampling_index = 0; upsampling_index < kSubSampling; ++upsampling_index) { @@ -247,10 +247,10 @@ void ThreeBandFilterBank::Synthesis( ? index : (index < kZeroFilterIndex2 ? index - 1 : index - 2); - ArrayView filter(kFilterCoeffs[filter_index]); - ArrayView dct_modulation( + std::span filter(kFilterCoeffs[filter_index]); + std::span dct_modulation( kDctModulation[filter_index]); - ArrayView state(state_synthesis_[filter_index]); + std::span state(state_synthesis_[filter_index]); // Prepare filter input by modulating the banded input. std::array in_subsampled; diff --git a/modules/audio_processing/three_band_filter_bank.h b/modules/audio_processing/three_band_filter_bank.h index 84feb39d074..2ee95f8502a 100644 --- a/modules/audio_processing/three_band_filter_bank.h +++ b/modules/audio_processing/three_band_filter_bank.h @@ -13,8 +13,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -55,13 +54,13 @@ class ThreeBandFilterBank final { // Splits `in` of size kFullBandSize into 3 downsampled frequency bands in // `out`, each of size 160. - void Analysis(ArrayView in, - ArrayView, kNumBands> out); + void Analysis(std::span in, + std::span, kNumBands> out); // Merges the 3 downsampled frequency bands in `in`, each of size 160, into // `out`, which is of size kFullBandSize. - void Synthesis(ArrayView, kNumBands> in, - ArrayView out); + void Synthesis(std::span, kNumBands> in, + std::span out); private: std::array, kNumNonZeroFilters> diff --git a/modules/audio_processing/utility/BUILD.gn b/modules/audio_processing/utility/BUILD.gn index 63ee6d066e3..7a23781ce51 100644 --- a/modules/audio_processing/utility/BUILD.gn +++ b/modules/audio_processing/utility/BUILD.gn @@ -13,10 +13,7 @@ rtc_library("cascaded_biquad_filter") { "cascaded_biquad_filter.cc", "cascaded_biquad_filter.h", ] - deps = [ - "../../../api:array_view", - "../../../rtc_base:checks", - ] + deps = [ "../../../rtc_base:checks" ] } rtc_library("legacy_delay_estimator") { @@ -37,7 +34,6 @@ rtc_library("pffft_wrapper") { "pffft_wrapper.h", ] deps = [ - "../../../api:array_view", "../../../rtc_base:checks", "//third_party/pffft", ] @@ -50,9 +46,7 @@ if (rtc_include_tests) { sources = [ "cascaded_biquad_filter_unittest.cc" ] deps = [ ":cascaded_biquad_filter", - "../../../rtc_base:checks", "../../../test:test_support", - "//api:array_view", "//rtc_base:checks", "//testing/gtest", ] @@ -74,7 +68,6 @@ if (rtc_include_tests) { sources = [ "pffft_wrapper_unittest.cc" ] deps = [ ":pffft_wrapper", - "../../../api:array_view", "../../../test:test_support", "//testing/gtest", "//third_party/pffft", diff --git a/modules/audio_processing/utility/cascaded_biquad_filter.cc b/modules/audio_processing/utility/cascaded_biquad_filter.cc index 53c9d155062..2abbda0d9cd 100644 --- a/modules/audio_processing/utility/cascaded_biquad_filter.cc +++ b/modules/audio_processing/utility/cascaded_biquad_filter.cc @@ -11,9 +11,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -23,7 +23,7 @@ void CascadedBiQuadFilter::BiQuad::BiQuad::Reset() { } CascadedBiQuadFilter::CascadedBiQuadFilter( - ArrayView coefficients) { + std::span coefficients) { for (const auto& single_biquad_coefficients : coefficients) { biquads_.push_back(BiQuad(single_biquad_coefficients)); } @@ -31,8 +31,8 @@ CascadedBiQuadFilter::CascadedBiQuadFilter( CascadedBiQuadFilter::~CascadedBiQuadFilter() = default; -void CascadedBiQuadFilter::Process(ArrayView x, - ArrayView y) { +void CascadedBiQuadFilter::Process(std::span x, + std::span y) { if (!biquads_.empty()) { ApplyBiQuad(x, y, &biquads_[0]); for (size_t k = 1; k < biquads_.size(); ++k) { @@ -43,7 +43,7 @@ void CascadedBiQuadFilter::Process(ArrayView x, } } -void CascadedBiQuadFilter::Process(ArrayView y) { +void CascadedBiQuadFilter::Process(std::span y) { for (auto& biquad : biquads_) { ApplyBiQuad(y, y, &biquad); } @@ -55,8 +55,8 @@ void CascadedBiQuadFilter::Reset() { } } -void CascadedBiQuadFilter::ApplyBiQuad(ArrayView x, - ArrayView y, +void CascadedBiQuadFilter::ApplyBiQuad(std::span x, + std::span y, CascadedBiQuadFilter::BiQuad* biquad) { RTC_DCHECK_EQ(x.size(), y.size()); const float c_a_0 = biquad->coefficients.a[0]; diff --git a/modules/audio_processing/utility/cascaded_biquad_filter.h b/modules/audio_processing/utility/cascaded_biquad_filter.h index 25d8d504ec2..3f1ab71e737 100644 --- a/modules/audio_processing/utility/cascaded_biquad_filter.h +++ b/modules/audio_processing/utility/cascaded_biquad_filter.h @@ -13,10 +13,9 @@ #include +#include #include -#include "api/array_view.h" - namespace webrtc { // Applies a number of biquads in a cascaded manner. The filter implementation @@ -38,21 +37,21 @@ class CascadedBiQuadFilter { }; CascadedBiQuadFilter( - ArrayView coefficients); + std::span coefficients); ~CascadedBiQuadFilter(); CascadedBiQuadFilter(const CascadedBiQuadFilter&) = delete; CascadedBiQuadFilter& operator=(const CascadedBiQuadFilter&) = delete; // Applies the biquads on the values in x in order to form the output in y. - void Process(ArrayView x, ArrayView y); + void Process(std::span x, std::span y); // Applies the biquads on the values in y in an in-place manner. - void Process(ArrayView y); + void Process(std::span y); // Resets the filter to its initial state. void Reset(); private: - void ApplyBiQuad(ArrayView x, - ArrayView y, + void ApplyBiQuad(std::span x, + std::span y, CascadedBiQuadFilter::BiQuad* biquad); std::vector biquads_; diff --git a/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc b/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc index 23e1e878206..c6e14df0232 100644 --- a/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc +++ b/modules/audio_processing/utility/cascaded_biquad_filter_unittest.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "test/gtest.h" @@ -61,7 +61,7 @@ TEST(CascadedBiquadFilter, BlockingConfiguration) { std::vector values = CreateInputWithIncreasingValues(1000); CascadedBiQuadFilter filter( - (ArrayView( + (std::span( kBlockingCoefficients))); filter.Process(values); @@ -78,7 +78,7 @@ TEST(CascadedBiquadFilter, HighPassConfiguration) { } CascadedBiQuadFilter filter( - (ArrayView( + (std::span( kHighPassFilterCoefficients))); filter.Process(values); @@ -90,7 +90,7 @@ TEST(CascadedBiquadFilter, HighPassConfiguration) { // Verifies that the reset functionality works as intended. TEST(CascadedBiquadFilter, HighPassConfigurationResetFunctionality) { CascadedBiQuadFilter filter( - (ArrayView( + (std::span( kHighPassFilterCoefficients))); std::vector values1(100, 1.f); @@ -114,7 +114,7 @@ TEST(CascadedBiquadFilter, TransparentConfiguration) { std::vector output(input.size()); CascadedBiQuadFilter filter( - (ArrayView( + (std::span( kTransparentCoefficients))); filter.Process(input, output); @@ -127,7 +127,7 @@ TEST(CascadedBiquadFilter, CascadedConfiguration) { std::vector output(input.size()); CascadedBiQuadFilter filter( - (ArrayView( + (std::span( kCascadedCoefficients))); filter.Process(input, output); @@ -145,7 +145,7 @@ TEST(CascadedBiquadFilterDeathTest, InputSizeCheckVerification) { std::vector output(input.size() - 1); CascadedBiQuadFilter filter( - (ArrayView( + (std::span( kTransparentCoefficients))); EXPECT_DEATH(filter.Process(input, output), ""); } diff --git a/modules/audio_processing/utility/pffft_wrapper.cc b/modules/audio_processing/utility/pffft_wrapper.cc index 59b590005d4..87f5e9504b0 100644 --- a/modules/audio_processing/utility/pffft_wrapper.cc +++ b/modules/audio_processing/utility/pffft_wrapper.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "third_party/pffft/src/pffft.h" @@ -38,11 +38,11 @@ Pffft::FloatBuffer::~FloatBuffer() { pffft_aligned_free(data_); } -ArrayView Pffft::FloatBuffer::GetConstView() const { +std::span Pffft::FloatBuffer::GetConstView() const { return {data_, size_}; } -ArrayView Pffft::FloatBuffer::GetView() { +std::span Pffft::FloatBuffer::GetView() { return {data_, size_}; } diff --git a/modules/audio_processing/utility/pffft_wrapper.h b/modules/audio_processing/utility/pffft_wrapper.h index 590310e5dc3..0a0a70890fb 100644 --- a/modules/audio_processing/utility/pffft_wrapper.h +++ b/modules/audio_processing/utility/pffft_wrapper.h @@ -13,8 +13,7 @@ #include #include - -#include "api/array_view.h" +#include // Forward declaration. struct PFFFT_Setup; @@ -35,8 +34,8 @@ class Pffft { FloatBuffer& operator=(const FloatBuffer&) = delete; ~FloatBuffer(); - ArrayView GetConstView() const; - ArrayView GetView(); + std::span GetConstView() const; + std::span GetView(); private: friend class Pffft; @@ -68,7 +67,7 @@ class Pffft { // Creates a buffer of the right size. std::unique_ptr CreateBuffer() const; - // TODO(https://crbug.com/webrtc/9577): Overload with ArrayView args. + // TODO(https://crbug.com/webrtc/9577): Overload with std::span args. // Computes the forward fast Fourier transform. void ForwardTransform(const FloatBuffer& in, FloatBuffer* out, bool ordered); // Computes the backward fast Fourier transform. diff --git a/modules/audio_processing/utility/pffft_wrapper_unittest.cc b/modules/audio_processing/utility/pffft_wrapper_unittest.cc index f1ce5209d23..c7704e715d0 100644 --- a/modules/audio_processing/utility/pffft_wrapper_unittest.cc +++ b/modules/audio_processing/utility/pffft_wrapper_unittest.cc @@ -13,8 +13,9 @@ #include #include #include +#include -#include "api/array_view.h" +#include "test/gmock.h" #include "test/gtest.h" #include "third_party/pffft/src/pffft.h" @@ -22,6 +23,8 @@ namespace webrtc { namespace test { namespace { +using ::testing::ElementsAreArray; + constexpr size_t kMaxValidSizeCheck = 1024; constexpr int kFftSizes[] = {16, 32, 64, 96, 128, 160, 192, 256, @@ -41,15 +44,6 @@ double frand() { return std::rand() / static_cast(RAND_MAX); } -void ExpectArrayViewsEquality(ArrayView a, - ArrayView b) { - ASSERT_EQ(a.size(), b.size()); - for (size_t i = 0; i < a.size(); ++i) { - SCOPED_TRACE(i); - EXPECT_EQ(a[i], b[i]); - } -} - // Compares the output of the PFFFT C++ wrapper to that of the C PFFFT. // Bit-exactness is expected. void PffftValidateWrapper(size_t fft_size, bool complex_fft) { @@ -75,8 +69,8 @@ void PffftValidateWrapper(size_t fft_size, bool complex_fft) { auto out_wrapper = pffft_wrapper.CreateBuffer(); // Input and output buffers views. - ArrayView in_view(in, num_floats); - ArrayView out_view(out, num_floats); + std::span in_view(in, num_floats); + std::span out_view(out, num_floats); auto in_wrapper_view = in_wrapper->GetView(); EXPECT_EQ(in_wrapper_view.size(), num_floats); auto out_wrapper_view = out_wrapper->GetConstView(); @@ -91,7 +85,7 @@ void PffftValidateWrapper(size_t fft_size, bool complex_fft) { pffft_transform(pffft_status, in, out, scratch, PFFFT_FORWARD); pffft_wrapper.ForwardTransform(*in_wrapper, out_wrapper.get(), /*ordered=*/false); - ExpectArrayViewsEquality(out_view, out_wrapper_view); + EXPECT_THAT(out_wrapper_view, ElementsAreArray(out_view)); // Copy the FFT results into the input buffers to compute the backward FFT. std::copy(out_view.begin(), out_view.end(), in_view.begin()); @@ -102,7 +96,7 @@ void PffftValidateWrapper(size_t fft_size, bool complex_fft) { pffft_transform(pffft_status, in, out, scratch, PFFFT_BACKWARD); pffft_wrapper.BackwardTransform(*in_wrapper, out_wrapper.get(), /*ordered=*/false); - ExpectArrayViewsEquality(out_view, out_wrapper_view); + EXPECT_THAT(out_wrapper_view, ElementsAreArray(out_view)); pffft_destroy_setup(pffft_status); pffft_aligned_free(in); diff --git a/modules/audio_processing/vad/BUILD.gn b/modules/audio_processing/vad/BUILD.gn index 71e079d3a36..34b3003e6df 100644 --- a/modules/audio_processing/vad/BUILD.gn +++ b/modules/audio_processing/vad/BUILD.gn @@ -35,7 +35,6 @@ rtc_library("vad") { "voice_gmm_tables.h", ] deps = [ - "../../../audio/utility:audio_frame_operations", "../../../common_audio", "../../../common_audio:common_audio_c", "../../../common_audio/third_party/ooura:fft_size_256", @@ -59,7 +58,6 @@ if (rtc_include_tests) { ] deps = [ ":vad", - "../../../common_audio", "../../../test:fileutils", "../../../test:test_support", "//testing/gmock", diff --git a/modules/audio_processing/vad/voice_activity_detector.h b/modules/audio_processing/vad/voice_activity_detector.h index 5754e895238..64c42a478b3 100644 --- a/modules/audio_processing/vad/voice_activity_detector.h +++ b/modules/audio_processing/vad/voice_activity_detector.h @@ -33,7 +33,7 @@ class VoiceActivityDetector { ~VoiceActivityDetector(); // Processes each audio chunk and estimates the voice probability. - // TODO(bugs.webrtc.org/7494): Switch to ArrayView and remove + // TODO(bugs.webrtc.org/7494): Switch to std::span and remove // `sample_rate_hz`. void ProcessChunk(const int16_t* audio, size_t length, int sample_rate_hz); diff --git a/modules/congestion_controller/BUILD.gn b/modules/congestion_controller/BUILD.gn index f45e2e59cf3..dc115682b1d 100644 --- a/modules/congestion_controller/BUILD.gn +++ b/modules/congestion_controller/BUILD.gn @@ -22,9 +22,7 @@ rtc_library("congestion_controller") { "../../api:rtp_parameters", "../../api:sequence_checker", "../../api/environment", - "../../api/transport:network_control", "../../api/units:data_rate", - "../../api/units:data_size", "../../api/units:time_delta", "../../api/units:timestamp", "../../rtc_base:logging", @@ -33,7 +31,6 @@ rtc_library("congestion_controller") { "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/synchronization:mutex", "../../system_wrappers", - "../pacing", "../remote_bitrate_estimator", "../remote_bitrate_estimator:congestion_control_feedback_generator", "../remote_bitrate_estimator:transport_sequence_number_feedback_generator", @@ -68,9 +65,7 @@ if (rtc_include_tests) { "../../test:create_test_field_trials", "../../test:test_support", "../../test/scenario", - "../pacing", "../rtp_rtcp:rtp_rtcp_format", - "goog_cc:estimators", "goog_cc:goog_cc_unittests", "goog_cc_scream_network_controller:goog_cc_scream_network_controller_unittests", "pcc:pcc_unittests", diff --git a/modules/congestion_controller/DEPS b/modules/congestion_controller/DEPS index 4bb4026c37c..2ba1ad5a24d 100644 --- a/modules/congestion_controller/DEPS +++ b/modules/congestion_controller/DEPS @@ -4,7 +4,7 @@ include_rules = [ "+video", ] specific_include_rules = { - "goog_cc_network_control_unittest.cc": [ + "goog_cc_network_control_unittest\\.cc": [ "+call/video_receive_stream.h", ], } diff --git a/modules/congestion_controller/DIR_METADATA b/modules/congestion_controller/DIR_METADATA new file mode 100644 index 00000000000..a904cc9a09e --- /dev/null +++ b/modules/congestion_controller/DIR_METADATA @@ -0,0 +1,3 @@ +buganizer_public: { + component_id: 1565318 # BWE +} \ No newline at end of file diff --git a/modules/congestion_controller/OWNERS b/modules/congestion_controller/OWNERS index 9a836bad069..7a424948a6e 100644 --- a/modules/congestion_controller/OWNERS +++ b/modules/congestion_controller/OWNERS @@ -1,7 +1,5 @@ danilchap@webrtc.org linderborg@webrtc.org -stefan@webrtc.org terelius@webrtc.org -mflodman@webrtc.org yinwa@webrtc.org perkj@webrtc.org diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn index 5d65bc2c6dd..a8c9da49dbc 100644 --- a/modules/congestion_controller/goog_cc/BUILD.gn +++ b/modules/congestion_controller/goog_cc/BUILD.gn @@ -59,9 +59,7 @@ rtc_library("pushback_controller") { ] deps = [ "../../../api:field_trials_view", - "../../../api/transport:network_control", "../../../api/units:data_size", - "../../../rtc_base:checks", "../../../rtc_base/experiments:rate_control_settings", ] } @@ -127,7 +125,6 @@ rtc_library("loss_based_bwe_v2") { "loss_based_bwe_v2.h", ] deps = [ - "../../../api:array_view", "../../../api:field_trials_view", "../../../api/transport:network_control", "../../../api/units:data_rate", @@ -191,7 +188,6 @@ rtc_library("delay_based_bwe") { "../../../rtc_base:race_checker", "../../../rtc_base/experiments:field_trial_parser", "../../../system_wrappers:metrics", - "../../pacing", "../../remote_bitrate_estimator", ] } @@ -211,11 +207,8 @@ rtc_library("probe_controller") { "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../logging:rtc_event_bwe", - "../../../logging:rtc_event_pacing", "../../../rtc_base:checks", "../../../rtc_base:logging", - "../../../rtc_base:macromagic", - "../../../rtc_base:safe_conversions", "../../../rtc_base/experiments:field_trial_parser", "../../../system_wrappers:metrics", "//third_party/abseil-cpp/absl/base:core_headers", @@ -236,7 +229,6 @@ if (rtc_include_tests) { ":estimators", ":goog_cc", "../../../api:libjingle_logging_api", - "../../../api/rtc_event_log", "../../../api/transport:goog_cc", "../../../api/transport:network_control", "../../../api/units:data_rate", @@ -272,14 +264,11 @@ if (rtc_include_tests) { ":alr_detector", ":delay_based_bwe", ":estimators", - ":goog_cc", ":loss_based_bwe_v2", ":probe_controller", ":pushback_controller", ":send_side_bwe", "../../../api:field_trials", - "../../../api:field_trials_view", - "../../../api:network_state_predictor_api", "../../../api/environment", "../../../api/environment:environment_factory", "../../../api/rtc_event_log", @@ -296,9 +285,6 @@ if (rtc_include_tests) { "../../../logging:mocks", "../../../logging:rtc_event_bwe", "../../../rtc_base:checks", - "../../../rtc_base:logging", - "../../../rtc_base:random", - "../../../rtc_base:rtc_base_tests_utils", "../../../rtc_base:stringutils", "../../../rtc_base/experiments:alr_experiment", "../../../system_wrappers", @@ -307,7 +293,6 @@ if (rtc_include_tests) { "../../../test/network:emulated_network", "../../../test/scenario", "../../../test/scenario:column_printer", - "../../pacing", "//testing/gmock", "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/strings:string_view", diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc index a5a0c5a599e..2ba1aa6fdb5 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc @@ -113,9 +113,7 @@ GoogCcNetworkController::GoogCcNetworkController(NetworkControllerConfig config, ? std::make_unique( env_.field_trials()) : nullptr), - bandwidth_estimation_( - std::make_unique(&env_.field_trials(), - &env_.event_log())), + bandwidth_estimation_(&env_.field_trials(), &env_.event_log()), alr_detector_(env_), probe_bitrate_estimator_(new ProbeBitrateEstimator(&env_.event_log())), network_estimator_(std::move(goog_cc_config.network_state_estimator)), @@ -163,7 +161,7 @@ NetworkControlUpdate GoogCcNetworkController::OnNetworkRouteChange( if (!estimated_bitrate) estimated_bitrate = acknowledged_bitrate_estimator_->PeekRate(); } else { - estimated_bitrate = bandwidth_estimation_->target_rate(); + estimated_bitrate = bandwidth_estimation_.target_rate(); } if (estimated_bitrate) { if (msg.constraints.starting_rate) { @@ -182,7 +180,7 @@ NetworkControlUpdate GoogCcNetworkController::OnNetworkRouteChange( network_estimator_->OnRouteChange(msg); delay_based_bwe_.reset(new DelayBasedBwe( &env_.field_trials(), &env_.event_log(), network_state_predictor_.get())); - bandwidth_estimation_->OnRouteChange(); + bandwidth_estimation_.OnRouteChange(); probe_controller_->Reset(msg.at_time); NetworkControlUpdate update; update.probe_cluster_configs = ResetConstraints(msg.constraints); @@ -202,11 +200,8 @@ NetworkControlUpdate GoogCcNetworkController::OnProcessInterval( probe_controller_->EnablePeriodicAlrProbing( *initial_config_->stream_based_config.requests_alr_probing); } - if (initial_config_->stream_based_config.enable_repeated_initial_probing) { - probe_controller_->EnableRepeatedInitialProbing( - *initial_config_->stream_based_config - .enable_repeated_initial_probing); - } + probe_controller_->EnableRepeatedInitialProbing( + initial_config_->stream_based_config.enable_repeated_initial_probing); std::optional total_bitrate = initial_config_->stream_based_config.max_total_allocated_bitrate; if (total_bitrate) { @@ -221,7 +216,7 @@ NetworkControlUpdate GoogCcNetworkController::OnProcessInterval( congestion_window_pushback_controller_->UpdatePacingQueue( msg.pacer_queue->bytes()); } - bandwidth_estimation_->UpdateEstimate(msg.at_time); + bandwidth_estimation_.UpdateEstimate(msg.at_time); probe_controller_->SetAlrStartTime( alr_detector_.GetApplicationLimitedRegionStartTime()); @@ -245,8 +240,7 @@ NetworkControlUpdate GoogCcNetworkController::OnProcessInterval( NetworkControlUpdate GoogCcNetworkController::OnRemoteBitrateReport( RemoteBitrateReport msg) { - bandwidth_estimation_->UpdateReceiverEstimate(msg.receive_time, - msg.bandwidth); + bandwidth_estimation_.UpdateReceiverEstimate(msg.receive_time, msg.bandwidth); return NetworkControlUpdate(); } @@ -258,7 +252,7 @@ NetworkControlUpdate GoogCcNetworkController::OnRoundTripTimeUpdate( RTC_DCHECK(!msg.round_trip_time.IsZero()); if (delay_based_bwe_) delay_based_bwe_->OnRttUpdate(msg.round_trip_time); - bandwidth_estimation_->UpdateRtt(msg.round_trip_time, msg.receive_time); + bandwidth_estimation_.UpdateRtt(msg.round_trip_time, msg.receive_time); return NetworkControlUpdate(); } @@ -272,10 +266,10 @@ NetworkControlUpdate GoogCcNetworkController::OnSentPacket( first_packet_sent_ = true; // Initialize feedback time to send time to allow estimation of RTT until // first feedback is received. - bandwidth_estimation_->UpdatePropagationRtt(sent_packet.send_time, - TimeDelta::Zero()); + bandwidth_estimation_.UpdatePropagationRtt(sent_packet.send_time, + TimeDelta::Zero()); } - bandwidth_estimation_->OnSentPacket(sent_packet); + bandwidth_estimation_.OnSentPacket(sent_packet); if (congestion_window_pushback_controller_) { congestion_window_pushback_controller_->UpdateOutstandingData( @@ -318,7 +312,7 @@ NetworkControlUpdate GoogCcNetworkController::OnStreamsConfig( if (use_min_allocatable_as_lower_bound_) { ClampConstraints(); delay_based_bwe_->SetMinBitrate(min_data_rate_); - bandwidth_estimation_->SetMinMaxBitrate(min_data_rate_, max_data_rate_); + bandwidth_estimation_.SetMinMaxBitrate(min_data_rate_, max_data_rate_); } } if (msg.max_padding_rate && *msg.max_padding_rate != max_padding_rate_) { @@ -365,8 +359,8 @@ std::vector GoogCcNetworkController::ResetConstraints( starting_rate_ = new_constraints.starting_rate; ClampConstraints(); - bandwidth_estimation_->SetBitrates(starting_rate_, min_data_rate_, - max_data_rate_, new_constraints.at_time); + bandwidth_estimation_.SetBitrates(starting_rate_, min_data_rate_, + max_data_rate_, new_constraints.at_time); if (starting_rate_) delay_based_bwe_->SetStartBitrate(*starting_rate_); @@ -381,7 +375,7 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportLossReport( TransportLossReport msg) { int64_t total_packets_delta = msg.packets_received_delta + msg.packets_lost_delta; - bandwidth_estimation_->UpdatePacketsLost( + bandwidth_estimation_.UpdatePacketsLost( msg.packets_lost_delta, total_packets_delta, msg.receive_time); return NetworkControlUpdate(); } @@ -445,8 +439,8 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow) feedback_max_rtts_.pop_front(); // TODO(srte): Use time since last unacknowledged packet. - bandwidth_estimation_->UpdatePropagationRtt(report.feedback_time, - min_propagation_rtt); + bandwidth_estimation_.UpdatePropagationRtt(report.feedback_time, + min_propagation_rtt); } std::optional alr_start_time = @@ -459,8 +453,8 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector( report.SortedByReceiveTime()); auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate(); - bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate, - report.feedback_time); + bandwidth_estimation_.SetAcknowledgedRate(acknowledged_bitrate, + report.feedback_time); for (const auto& feedback : report.SortedByReceiveTime()) { if (feedback.sent_packet.pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe) { @@ -504,15 +498,15 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( if (result.updated) { if (result.probe) { - bandwidth_estimation_->SetSendBitrate(result.target_bitrate, - report.feedback_time); + bandwidth_estimation_.SetSendBitrate(result.target_bitrate, + report.feedback_time); } // Since SetSendBitrate now resets the delay-based estimate, we have to // call UpdateDelayBasedEstimate after SetSendBitrate. - bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time, - result.target_bitrate); + bandwidth_estimation_.UpdateDelayBasedEstimate(report.feedback_time, + result.target_bitrate); } - bandwidth_estimation_->UpdateLossBasedEstimator( + bandwidth_estimation_.UpdateLossBasedEstimator( report, result.delay_detector_state, probe_bitrate, alr_start_time.has_value()); if (result.updated) { @@ -587,10 +581,10 @@ NetworkControlUpdate GoogCcNetworkController::GetNetworkState( void GoogCcNetworkController::MaybeTriggerOnNetworkChanged( NetworkControlUpdate* update, Timestamp at_time) { - uint8_t fraction_loss = bandwidth_estimation_->fraction_loss(); - TimeDelta round_trip_time = bandwidth_estimation_->round_trip_time(); - DataRate loss_based_target_rate = bandwidth_estimation_->target_rate(); - LossBasedState loss_based_state = bandwidth_estimation_->loss_based_state(); + uint8_t fraction_loss = bandwidth_estimation_.fraction_loss(); + TimeDelta round_trip_time = bandwidth_estimation_.round_trip_time(); + DataRate loss_based_target_rate = bandwidth_estimation_.target_rate(); + LossBasedState loss_based_state = bandwidth_estimation_.loss_based_state(); DataRate pushback_target_rate = loss_based_target_rate; double cwnd_reduce_ratio = 0.0; @@ -598,8 +592,8 @@ void GoogCcNetworkController::MaybeTriggerOnNetworkChanged( int64_t pushback_rate = congestion_window_pushback_controller_->UpdateTargetBitrate( loss_based_target_rate.bps()); - pushback_rate = std::max(bandwidth_estimation_->GetMinBitrate(), - pushback_rate); + pushback_rate = + std::max(bandwidth_estimation_.GetMinBitrate(), pushback_rate); pushback_target_rate = DataRate::BitsPerSec(pushback_rate); if (rate_control_settings_.UseCongestionWindowDropFrameOnly()) { cwnd_reduce_ratio = static_cast(loss_based_target_rate.bps() - @@ -640,8 +634,8 @@ void GoogCcNetworkController::MaybeTriggerOnNetworkChanged( auto probes = probe_controller_->SetEstimatedBitrate( loss_based_target_rate, - GetBandwidthLimitedCause(bandwidth_estimation_->loss_based_state(), - bandwidth_estimation_->IsRttAboveLimit(), + GetBandwidthLimitedCause(bandwidth_estimation_.loss_based_state(), + bandwidth_estimation_.IsRttAboveLimit(), delay_based_bwe_->last_state()), at_time); update->probe_cluster_configs.insert(update->probe_cluster_configs.end(), diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.h b/modules/congestion_controller/goog_cc/goog_cc_network_control.h index 2b940def689..618d1725732 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.h +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.h @@ -97,7 +97,7 @@ class GoogCcNetworkController : public NetworkControllerInterface { const std::unique_ptr congestion_window_pushback_controller_; - std::unique_ptr bandwidth_estimation_; + SendSideBandwidthEstimation bandwidth_estimation_; AlrDetector alr_detector_; std::unique_ptr probe_bitrate_estimator_; std::unique_ptr network_estimator_; diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc index 55c78735bc7..9649332bbc3 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" @@ -187,7 +187,7 @@ void LossBasedBweV2::SetMinMaxBitrate(DataRate min_bitrate, } void LossBasedBweV2::UpdateBandwidthEstimate( - ArrayView packet_results, + std::span packet_results, DataRate delay_based_estimate, bool in_alr) { delay_based_estimate_ = delay_based_estimate; @@ -1147,7 +1147,7 @@ void LossBasedBweV2::NewtonsMethodUpdate( } bool LossBasedBweV2::PushBackObservation( - ArrayView packet_results) { + std::span packet_results) { if (packet_results.empty()) { return false; } diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h index b088420f199..ce3219e0ee1 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h @@ -13,10 +13,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" @@ -72,7 +72,7 @@ class LossBasedBweV2 { void SetAcknowledgedBitrate(DataRate acknowledged_bitrate); void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate); - void UpdateBandwidthEstimate(ArrayView packet_results, + void UpdateBandwidthEstimate(std::span packet_results, DataRate delay_based_estimate, bool in_alr); @@ -193,7 +193,7 @@ class LossBasedBweV2 { void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const; // Returns false if no observation was created. - bool PushBackObservation(ArrayView packet_results); + bool PushBackObservation(std::span packet_results); bool IsEstimateIncreasingWhenLossLimited(DataRate old_estimate, DataRate new_estimate); bool IsInLossLimitedState() const; diff --git a/modules/congestion_controller/goog_cc_scream_network_controller/BUILD.gn b/modules/congestion_controller/goog_cc_scream_network_controller/BUILD.gn index a064c4434db..3a63e1c042f 100644 --- a/modules/congestion_controller/goog_cc_scream_network_controller/BUILD.gn +++ b/modules/congestion_controller/goog_cc_scream_network_controller/BUILD.gn @@ -14,11 +14,9 @@ rtc_library("goog_cc_scream_network_controller") { ] deps = [ - "../../../api:field_trials_view", "../../../api/environment", "../../../api/transport:network_control", "../../../rtc_base:logging", - "../../../rtc_base/experiments:field_trial_parser", "../goog_cc", "../scream:scream_network_controller", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -30,17 +28,14 @@ if (rtc_include_tests) { testonly = true sources = [ "goog_cc_scream_network_controller_unittest.cc" ] deps = [ - ":goog_cc_scream_network_controller", "../../../api/environment", "../../../api/transport:ecn_marking", "../../../api/transport:goog_cc", "../../../api/transport:network_control", "../../../api/units:data_rate", "../../../api/units:data_size", - "../../../api/units:time_delta", "../../../test:create_test_environment", "../../../test:test_support", - "../goog_cc", ] } } diff --git a/modules/congestion_controller/pcc/BUILD.gn b/modules/congestion_controller/pcc/BUILD.gn index 5fce915259e..c971116d653 100644 --- a/modules/congestion_controller/pcc/BUILD.gn +++ b/modules/congestion_controller/pcc/BUILD.gn @@ -73,7 +73,6 @@ rtc_library("utility_function") { ] deps = [ ":monitor_interval", - "../../../api/transport:network_control", "../../../api/units:data_rate", "../../../rtc_base:checks", ] @@ -87,7 +86,6 @@ rtc_library("bitrate_controller") { deps = [ ":monitor_interval", ":utility_function", - "../../../api/transport:network_control", "../../../api/units:data_rate", ] } diff --git a/modules/congestion_controller/rtp/BUILD.gn b/modules/congestion_controller/rtp/BUILD.gn index 42c789c1580..23e6a060a14 100644 --- a/modules/congestion_controller/rtp/BUILD.gn +++ b/modules/congestion_controller/rtp/BUILD.gn @@ -19,12 +19,9 @@ rtc_library("control_handler") { "../../../api:sequence_checker", "../../../api/transport:network_control", "../../../api/units:data_rate", - "../../../api/units:data_size", "../../../api/units:time_delta", "../../../rtc_base:checks", "../../../rtc_base:logging", - "../../../rtc_base:safe_conversions", - "../../../rtc_base:safe_minmax", "../../../rtc_base/system:no_unique_address", "../../pacing", ] @@ -39,7 +36,6 @@ rtc_library("transport_feedback") { ] deps = [ - "../..:module_api_public", "../../../api:sequence_checker", "../../../api/transport:ecn_marking", "../../../api/transport:network_control", @@ -52,9 +48,7 @@ rtc_library("transport_feedback") { "../../../rtc_base:network_route", "../../../rtc_base:rtc_numerics", "../../../rtc_base/network:sent_packet", - "../../../rtc_base/synchronization:mutex", "../../../rtc_base/system:no_unique_address", - "../../../system_wrappers", "../../rtp_rtcp:ntp_time_util", "../../rtp_rtcp:rtp_rtcp_format", "//third_party/abseil-cpp/absl/algorithm:container", @@ -75,23 +69,15 @@ if (rtc_include_tests) { ] deps = [ ":transport_feedback", - "../:congestion_controller", - "../../../api:array_view", "../../../api/transport:ecn_marking", "../../../api/transport:network_control", "../../../api/units:data_size", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../logging:mocks", "../../../rtc_base:buffer", - "../../../rtc_base:checks", - "../../../rtc_base:logging", - "../../../rtc_base:safe_conversions", "../../../rtc_base/network:sent_packet", "../../../system_wrappers", "../../../test:test_support", - "../../pacing", - "../../remote_bitrate_estimator", "../../rtp_rtcp:ntp_time_util", "../../rtp_rtcp:rtp_rtcp_format", "//testing/gmock", diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.cc b/modules/congestion_controller/rtp/transport_feedback_adapter.cc index 2248a0d64f2..90af19ef283 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter.cc +++ b/modules/congestion_controller/rtp/transport_feedback_adapter.cc @@ -34,9 +34,29 @@ #include "rtc_base/network_route.h" namespace webrtc { +namespace { constexpr TimeDelta kSendTimeHistoryWindow = TimeDelta::Seconds(60); +class MinMax { + public: + explicit MinMax(int64_t value) : minimum_(value), maximum_(value) {} + + void Update(int64_t value) { + minimum_ = std::min(minimum_, value); + maximum_ = std::max(maximum_, value); + } + + int64_t minimum() const { return minimum_; } + int64_t maximum() const { return maximum_; } + + private: + int64_t minimum_; + int64_t maximum_; +}; + +} // namespace + void InFlightBytesTracker::AddInFlightPacketBytes( const PacketFeedback& packet) { RTC_DCHECK(packet.sent.send_time.IsFinite()); @@ -286,6 +306,7 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback( int failed_lookups = 0; bool supports_ecn = true; std::vector packet_result_vector; + std::optional sequence_number_in_report; for (const rtcp::CongestionControlFeedback::PacketInfo& packet_info : feedback.packets()) { std::optional packet_feedback = RetrievePacketFeedback( @@ -300,6 +321,11 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback( ++ignored_packets; continue; } + if (!sequence_number_in_report.has_value()) { + sequence_number_in_report.emplace(packet_feedback->sent.sequence_number); + } else { + sequence_number_in_report->Update(packet_feedback->sent.sequence_number); + } PacketResult result; result.sent_packet = packet_feedback->sent; if (packet_info.arrival_time_offset.IsFinite()) { @@ -321,6 +347,31 @@ TransportFeedbackAdapter::ProcessCongestionControlFeedback( packet_result_vector.push_back(result); } + // Report packets as lost that are in the same transport sequence number range + // as reported packets, but that are not mentioned in the report. + // Code below relies on the fact that received packets are already erased from + // the `history_`. + if (sequence_number_in_report.has_value()) { + auto begin = history_.lower_bound(sequence_number_in_report->minimum()); + auto end = history_.upper_bound(sequence_number_in_report->maximum()); + for (auto it = begin; it != end; ++it) { + PacketFeedback& packet_feedback = it->second; + if (it->second.previously_reported_lost) { + continue; + } + it->second.previously_reported_lost = true; + PacketResult result; + result.sent_packet = packet_feedback.sent; + result.sent_with_ect1 = packet_feedback.sent_with_ect1; + result.reported_lost_for_the_first_time = true; + result.rtp_packet_info = { + .ssrc = packet_feedback.ssrc, + .rtp_sequence_number = packet_feedback.rtp_sequence_number, + .is_retransmission = packet_feedback.is_retransmission}; + packet_result_vector.push_back(result); + } + } + if (failed_lookups > 0) { RTC_LOG(LS_INFO) << "Failed to lookup send time for " << failed_lookups << " packet" << (failed_lookups > 1 ? "s" : "") diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc index cda73f385b4..cf36947a1c1 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc +++ b/modules/congestion_controller/rtp/transport_feedback_adapter_unittest.cc @@ -10,14 +10,15 @@ #include "modules/congestion_controller/rtp/transport_feedback_adapter.h" +#include #include #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/transport/ecn_marking.h" #include "api/transport/network_types.h" #include "api/units/data_size.h" @@ -144,7 +145,7 @@ RtpPacketToSend CreatePacketToSend(PacketTemplate packet) { } rtcp::TransportFeedback BuildRtcpTransportFeedbackPacket( - ArrayView packets) { + std::span packets) { rtcp::TransportFeedback feedback; feedback.SetBase(packets[0].transport_sequence_number, packets[0].receive_timestamp); @@ -159,10 +160,10 @@ rtcp::TransportFeedback BuildRtcpTransportFeedbackPacket( } rtcp::CongestionControlFeedback BuildRtcpCongestionControlFeedbackPacket( - ArrayView packets) { + std::span packets) { // Assume the feedback was sent when the last packet was received. Timestamp feedback_sent_time = Timestamp::MinusInfinity(); - for (auto it = packets.crbegin(); it != packets.crend(); ++it) { + for (auto it = packets.rbegin(); it != packets.rend(); ++it) { if (it->receive_timestamp.IsFinite()) { feedback_sent_time = it->receive_timestamp; break; @@ -214,7 +215,7 @@ class TransportFeedbackAdapterTest : public ::testing::TestWithParam { bool UseRfc8888CongestionControlFeedback() const { return GetParam(); } std::optional CreateAndProcessFeedback( - ArrayView packets, + std::span packets, TransportFeedbackAdapter& adapter) { if (UseRfc8888CongestionControlFeedback()) { rtcp::CongestionControlFeedback rtcp_feedback = @@ -321,6 +322,53 @@ TEST_P(TransportFeedbackAdapterTest, HandlesDroppedPackets) { adapted_feedback->packet_feedbacks); } +// Send packets on two SSRCs, and drop all packets on one of them. +// Expect TransportFeedbackAdapter to detect such missing packets. +// This test is trivial for TransportFeedback RTCP message where packets are +// identified by transport sequence number, and missing numbers is the primary +// way to report and track dropped packets. +// However for CongestionControl RTCP message that is not natural - when no +// packets are received on certain SSRC, such SSRC is not included into the +// report, yet TransportFeedbackAdapter still can treat such packets as lost. +TEST_P(TransportFeedbackAdapterTest, HandlesDroppedPacketsFromAnSsrc) { + constexpr uint32_t kSsrc1 = 123; + constexpr uint32_t kSsrc2 = 456; + TransportFeedbackAdapter adapter; + + std::vector packets = { + {.ssrc = kSsrc1, + .transport_sequence_number = 1, + .rtp_sequence_number = 101, + .send_timestamp = Timestamp::Millis(1'200), + .receive_timestamp = Timestamp::Millis(100)}, + {.ssrc = kSsrc2, + .transport_sequence_number = 2, + .rtp_sequence_number = 201, + .send_timestamp = Timestamp::Millis(1'210)}, + {.ssrc = kSsrc2, + .transport_sequence_number = 3, + .rtp_sequence_number = 202, + .send_timestamp = Timestamp::Millis(1'220)}, + {.ssrc = kSsrc1, + .transport_sequence_number = 4, + .rtp_sequence_number = 102, + .send_timestamp = Timestamp::Millis(1'230), + .receive_timestamp = Timestamp::Millis(130)}}; + + for (const PacketTemplate& packet : packets) { + adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info, + /*overhead=*/0u, TimeNow()); + adapter.ProcessSentPacket(SentPacketInfo(packet.transport_sequence_number, + packet.send_timestamp.ms())); + } + + std::array received_packets = {packets[0], packets[3]}; + std::optional adapted_feedback = + CreateAndProcessFeedback(received_packets, adapter); + + ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks); +} + TEST_P(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) { TransportFeedbackAdapter adapter; @@ -551,13 +599,66 @@ TEST_P(TransportFeedbackAdapterTest, TransportPacketFeedbackHasDataInFlight) { } std::optional adapted_feedback_1 = - CreateAndProcessFeedback(MakeArrayView(&packets[0], 1), adapter); + CreateAndProcessFeedback(std::span(&packets[0], 1), adapter); std::optional adapted_feedback_2 = - CreateAndProcessFeedback(MakeArrayView(&packets[1], 1), adapter); + CreateAndProcessFeedback(std::span(&packets[1], 1), adapter); EXPECT_EQ(adapted_feedback_1->data_in_flight, packets[1].packet_size); EXPECT_EQ(adapted_feedback_2->data_in_flight, DataSize::Zero()); } +TEST_P(TransportFeedbackAdapterTest, + PacketsInLostFeedbackAreNotReportedAsLost) { + TransportFeedbackAdapter adapter; + + std::vector packets = { + {.transport_sequence_number = 1, + .rtp_sequence_number = 101, + .send_timestamp = Timestamp::Millis(100), + .receive_timestamp = Timestamp::Millis(120)}, + {.transport_sequence_number = 2, + .rtp_sequence_number = 102, + .send_timestamp = Timestamp::Millis(110), + .receive_timestamp = Timestamp::Millis(130)}, + {.transport_sequence_number = 3, + .rtp_sequence_number = 103, + .send_timestamp = Timestamp::Millis(120), + .receive_timestamp = Timestamp::Millis(140)}, + {.transport_sequence_number = 4, + .rtp_sequence_number = 104, + .send_timestamp = Timestamp::Millis(130), + .receive_timestamp = Timestamp::Millis(150)}}; + + for (const PacketTemplate& packet : packets) { + adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info, + /*overhead=*/0u, TimeNow()); + adapter.ProcessSentPacket(SentPacketInfo(packet.transport_sequence_number, + packet.send_timestamp.ms())); + } + + // Feedback 1: Covers packet 1 + std::vector feedback1_packets = {packets[0]}; + CreateAndProcessFeedback(feedback1_packets, adapter); + + // Feedback 2 (Lost network packet, never processed): would cover packet 2. + + // Feedback 3: Covers packet 3 + std::vector feedback3_packets = {packets[2]}; + std::optional adapted_feedback3 = + CreateAndProcessFeedback(feedback3_packets, adapter); + + // Packet 2 is not reported as lost. + EXPECT_FALSE(FindFeedback(adapted_feedback3, 2).has_value()); + + // Feedback 4: Covers packet 4 + std::vector feedback4_packets = {packets[3]}; + std::optional adapted_feedback4 = + CreateAndProcessFeedback(feedback4_packets, adapter); + + // Both CCFB and TWCC never infer packet 2 as lost unless explicitly skipped + // within the sequence number range of a single received feedback packet. + EXPECT_FALSE(FindFeedback(adapted_feedback4, 2).has_value()); +} + TEST(TransportFeedbackAdapterCongestionFeedbackTest, CongestionControlFeedbackResultHasEcn) { TransportFeedbackAdapter adapter; @@ -711,7 +812,7 @@ TEST(TransportFeedbackAdapterCongestionFeedbackTest, const TimeDelta kExpectedRtt = TimeDelta::Millis(20); for (int i = 0; i < 4; i = i + 2) { rtcp::CongestionControlFeedback rtcp_feedback = - BuildRtcpCongestionControlFeedbackPacket(MakeArrayView(&packets[i], 2)); + BuildRtcpCongestionControlFeedbackPacket(std::span(&packets[i], 2)); std::optional adapted_feedback = adapter.ProcessCongestionControlFeedback( rtcp_feedback, @@ -764,6 +865,61 @@ TEST(TransportFeedbackAdapterCongestionFeedbackTest, EXPECT_FALSE(packet_feedback->reported_lost_for_the_first_time); } +TEST(TransportFeedbackAdapterCongestionFeedbackTest, + CongestionControlFeedbackResultReportsImplicitlyLostPacketOnce) { + TransportFeedbackAdapter adapter; + + PacketTemplate packets[] = {{.ssrc = 1, + .transport_sequence_number = 1, + .rtp_sequence_number = 101, + .send_timestamp = Timestamp::Millis(100)}, + {.ssrc = 2, + .transport_sequence_number = 2, + .rtp_sequence_number = 201, + .send_timestamp = Timestamp::Millis(110)}, + {.ssrc = 1, + .transport_sequence_number = 3, + .rtp_sequence_number = 102, + .send_timestamp = Timestamp::Millis(120)}, + {.ssrc = 2, + .transport_sequence_number = 4, + .rtp_sequence_number = 202, + .send_timestamp = Timestamp::Millis(120)}}; + + for (const PacketTemplate& packet : packets) { + adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info, + /*overhead=*/0u, TimeNow()); + + adapter.ProcessSentPacket(SentPacketInfo(packet.transport_sequence_number, + packet.send_timestamp.ms())); + } + + // Produce feedback where 2nd packet is lost. + packets[0].receive_timestamp = Timestamp::Millis(200); + packets[2].receive_timestamp = Timestamp::Millis(220); + std::vector feedback_1 = {packets[0], packets[2]}; + std::optional packet_feedback = FindFeedback( + adapter.ProcessCongestionControlFeedback( + BuildRtcpCongestionControlFeedbackPacket(feedback_1), TimeNow()), + /*transport_sequence_number=*/2); + ASSERT_TRUE(packet_feedback.has_value()); + EXPECT_FALSE(packet_feedback->IsReceived()); + + // Produce feedback where 2nd packet is reported lost and 4th packet is + // received. + packets[3].receive_timestamp = Timestamp::Millis(240); + std::vector feedback_2 = {packets[1], packets[3]}; + rtcp::CongestionControlFeedback rtcp_feedback = + BuildRtcpCongestionControlFeedbackPacket(feedback_2); + // 2nd packet is still lost. + packet_feedback = FindFeedback( + adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow()), + /*transport_sequence_number=*/2); + ASSERT_TRUE(packet_feedback.has_value()); + EXPECT_FALSE(packet_feedback->IsReceived()); + EXPECT_FALSE(packet_feedback->reported_lost_for_the_first_time); +} + TEST(TransportFeedbackAdapterCongestionFeedbackTest, CongestionControlFeedbackResultReportsRecoveredPacketOnce) { TransportFeedbackAdapter adapter; diff --git a/modules/congestion_controller/scream/BUILD.gn b/modules/congestion_controller/scream/BUILD.gn index 1683c6f44f2..d249b028923 100644 --- a/modules/congestion_controller/scream/BUILD.gn +++ b/modules/congestion_controller/scream/BUILD.gn @@ -21,7 +21,6 @@ rtc_library("delay_based_congestion_control") { "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", - "../../../rtc_base:logging", "../../../rtc_base:windowed_min_filter", ] } @@ -47,7 +46,6 @@ rtc_library("scream_v2") { deps = [ ":delay_based_congestion_control", ":scream_v2_parameters", - "../../../api:field_trials_view", "../../../api/environment", "../../../api/transport:ecn_marking", "../../../api/transport:network_control", @@ -70,10 +68,7 @@ rtc_library("scream_network_controller") { deps = [ ":scream_v2", ":scream_v2_parameters", - "../../../api:field_trials_view", "../../../api/environment", - "../../../api/transport:bandwidth_usage", - "../../../api/transport:ecn_marking", "../../../api/transport:network_control", "../../../api/units:data_rate", "../../../api/units:data_size", @@ -82,7 +77,6 @@ rtc_library("scream_network_controller") { "../../../logging:rtc_event_bwe", "../../../rtc_base:checks", "../../../rtc_base:logging", - "../../../rtc_base/experiments:field_trial_parser", ] } @@ -100,7 +94,6 @@ if (rtc_include_tests) { ":scream_v2", ":scream_v2_parameters", "../../../api/environment", - "../../../api/environment:environment_factory", "../../../api/transport:ecn_marking", "../../../api/transport:network_control", "../../../api/units:data_rate", diff --git a/modules/congestion_controller/scream/delay_based_congestion_control.cc b/modules/congestion_controller/scream/delay_based_congestion_control.cc index c58aff0479b..94be767cb1c 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control.cc +++ b/modules/congestion_controller/scream/delay_based_congestion_control.cc @@ -14,6 +14,7 @@ #include #include "api/transport/network_types.h" +#include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -38,21 +39,24 @@ void DelayBasedCongestionControl::OnTransportPacketsFeedback( if (received_packets.empty()) { return; } - TimeDelta one_way_delay_sum; + TimeDelta min_one_way_delay = TimeDelta::PlusInfinity(); + TimeDelta max_one_way_delay = TimeDelta::Zero(); for (const PacketResult& packet : received_packets) { TimeDelta one_way_delay = packet.receive_time - packet.sent_packet.send_time; next_base_delay_ = std::min(next_base_delay_, one_way_delay); - one_way_delay_sum += one_way_delay; min_one_way_delay = std::min(min_one_way_delay, one_way_delay); + max_one_way_delay = std::max(max_one_way_delay, one_way_delay); } // `arrival_time_offset` is null if TWCC is used. We assume feedback was sent // when the last sent packet was received. - TimeDelta rtt_sample = + TimeDelta rtt_sample = std::max( msg.feedback_time - received_packets.back().sent_packet.send_time - - received_packets.back().arrival_time_offset.value_or(TimeDelta::Zero()); + received_packets.back().arrival_time_offset.value_or( + TimeDelta::Zero()), + TimeDelta::Zero()); UpdateSmoothedRtt(rtt_sample); TimeDelta min_queue_delay = min_one_way_delay - min_base_delay(); @@ -63,8 +67,12 @@ void DelayBasedCongestionControl::OnTransportPacketsFeedback( } else { min_queue_delay_above_threshold_start_ = Timestamp::MinusInfinity(); } - UpdateQueueDelayAverage(one_way_delay_sum / - static_cast(received_packets.size())); + UpdateQueueDelayAverage(std::min(min_queue_delay, last_queue_delay_sample_)); + last_queue_delay_sample_ = min_queue_delay; + UpdateQueueDelayMinAverage(min_queue_delay); + UpdateLatencyDifferenceAverage(min_one_way_delay.IsFinite() + ? max_one_way_delay - min_one_way_delay + : TimeDelta::Zero()); if (msg.feedback_time - last_base_delay_update_ >= params_.base_delay_history_update_interval.Get()) { @@ -75,32 +83,47 @@ void DelayBasedCongestionControl::OnTransportPacketsFeedback( } void DelayBasedCongestionControl::UpdateQueueDelayAverage( - TimeDelta one_way_delay) { - TimeDelta current_qdelay = one_way_delay - min_base_delay(); - + TimeDelta min_qdelay_in_feedback) { // `queue_delay_avg_` is updated with a slow attack,fast decay EWMA // filter. - if (current_qdelay < queue_delay_avg_) { - queue_delay_avg_ = current_qdelay; + if (min_qdelay_in_feedback < queue_delay_avg_) { + queue_delay_avg_ = min_qdelay_in_feedback; } else { queue_delay_avg_ = - params_.queue_delay_avg_g.Get() * current_qdelay + + params_.queue_delay_avg_g.Get() * min_qdelay_in_feedback + (1.0 - params_.queue_delay_avg_g.Get()) * queue_delay_avg_; } - queue_delay_dev_norm_ = - params_.queue_delay_dev_avg_g.Get() * - (current_qdelay - queue_delay_avg_) / params_.virtual_rtt.Get() + - (1.0 - params_.queue_delay_dev_avg_g.Get()) * queue_delay_dev_norm_; - RTC_DCHECK(queue_delay_dev_norm_ >= 0.0); +} + +void DelayBasedCongestionControl::UpdateQueueDelayMinAverage( + TimeDelta packet_qdelay) { + RTC_DCHECK(packet_qdelay >= TimeDelta::Zero()); + queue_delay_min_avg_ = + (1.0 - params_.delay_min_and_latency_diff_avg_g.Get()) * + queue_delay_min_avg_ + + params_.delay_min_and_latency_diff_avg_g.Get() * + std::min(packet_qdelay, 2 * params_.queue_delay_min_threshold.Get()); +} + +void DelayBasedCongestionControl::UpdateLatencyDifferenceAverage( + TimeDelta packet_latency_diff) { + RTC_DCHECK(packet_latency_diff >= TimeDelta::Zero()); + latency_difference_avg_ = + (1.0 - params_.delay_min_and_latency_diff_avg_g.Get()) * + latency_difference_avg_ + + params_.delay_min_and_latency_diff_avg_g.Get() * + std::min(packet_latency_diff, + 2 * params_.latency_diff_threshold.Get()); } void DelayBasedCongestionControl::UpdateSmoothedRtt(TimeDelta rtt_sample) { + RTC_DCHECK(rtt_sample >= TimeDelta::Zero()); if (last_smoothed_rtt_.IsZero()) { last_smoothed_rtt_ = rtt_sample; } else { double g = params_.smoothed_rtt_avg_g_up.Get(); if (rtt_sample < last_smoothed_rtt_) { - g = params_.smoothed_l4s_avg_g_down.Get(); + g = params_.smoothed_rtt_avg_g_down.Get(); } last_smoothed_rtt_ = rtt_sample * g + last_smoothed_rtt_ * (1.0 - g); } @@ -116,17 +139,26 @@ void DelayBasedCongestionControl::ResetQueueDelay() { min_queue_delay_above_threshold_start_ = Timestamp::MinusInfinity(); last_update_qdelay_avg_time_ = Timestamp::MinusInfinity(); queue_delay_avg_ = TimeDelta::PlusInfinity(); - queue_delay_dev_norm_ = 0.0; + queue_delay_min_avg_ = TimeDelta::Zero(); + latency_difference_avg_ = TimeDelta::Zero(); } -bool DelayBasedCongestionControl::IsQueueDelayDetected() const { - return queue_delay_avg_ > params_.queue_delay_target.Get() * - params_.queue_delay_increased_threshold.Get(); +double +DelayBasedCongestionControl::ref_window_scale_factor_due_to_avg_min_delay( + bool allow_zero) const { + TimeDelta norm = params_.queue_delay_min_threshold.Get(); + // Reaches 0.1 at norm, and 1.0 at norm / 4 + return std::clamp(0.1 + 1.2 * (norm - queue_delay_min_avg_) / norm, + allow_zero ? 0.0 : 0.1, 1.0); } -bool DelayBasedCongestionControl::ShouldReduceReferenceWindow() const { - return (queue_delay_avg_ > params_.queue_delay_target.Get() * - params_.queue_delay_threshold.Get()); +double +DelayBasedCongestionControl::ref_window_scale_factor_due_to_latency_difference() + const { + TimeDelta norm = params_.latency_diff_threshold.Get(); + // Reaches 0.1 at norm, and 1.0 at norm / 4 + return std::clamp(0.1 + 1.2 * (norm - latency_difference_avg_) / norm, 0.1, + 1.0); } DataSize DelayBasedCongestionControl::UpdateReferenceWindow( @@ -140,20 +172,17 @@ DataSize DelayBasedCongestionControl::UpdateReferenceWindow( return min_allowed_reference_window; } - double backoff = l4s_alpha_v() * params_.queue_delay_threshold.Get(); + double backoff = l4s_alpha_v() / 2.0; // Reduce by 50% if l4s_alpha_v = 1.0; backoff /= std::max(1.0, last_smoothed_rtt_ / params_.virtual_rtt); - backoff *= std::max(0.5, 1.0 - ref_window_mss_ratio); - return std::max(min_allowed_reference_window, (1 - backoff) * ref_window); } double DelayBasedCongestionControl::l4s_alpha_v() const { - return std::clamp( - (queue_delay_avg_ - - params_.queue_delay_target.Get() * params_.queue_delay_threshold.Get()) / - (params_.queue_delay_target.Get() * - params_.queue_delay_threshold.Get()), - 0.0, 1.0); + // 4.2.2.1 + double l4s_alpha_v = + (queue_delay_avg_ - params_.queue_delay_target.Get() / 2) / + (params_.queue_delay_target.Get() / 2); + return std::clamp(l4s_alpha_v, 0.0, 1.0); } } // namespace webrtc diff --git a/modules/congestion_controller/scream/delay_based_congestion_control.h b/modules/congestion_controller/scream/delay_based_congestion_control.h index f1afad3ea9b..91d14831d5f 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control.h +++ b/modules/congestion_controller/scream/delay_based_congestion_control.h @@ -39,14 +39,11 @@ class DelayBasedCongestionControl { min_delay_based_bwe_ = min_delay_based_bwe; } - // Returns true if queue delay is detected, but it may be low and does not - // necessarily mean reference window should be reduced. From 4.2.2.1. - // (queue_qdelay >= queue_delay_target * 0.25) - bool IsQueueDelayDetected() const; - - // Returns true if queue delay is detected and reference window should be - // reduced. From 4.2.2.1. (queue_qdelay >= queue_delay_target*0.5) - bool ShouldReduceReferenceWindow() const; + // Returns true if queue delay is detected and above a threshold. + bool IsQueueDelayDetected() const { + return queue_delay_avg_.IsFinite() && + queue_delay_avg_ > params_.queue_delay_target.Get() / 2; + } DataSize UpdateReferenceWindow(DataSize rew_window, double ref_window_mss_ratio) const; @@ -64,15 +61,23 @@ class DelayBasedCongestionControl { // Resets queue delay estimates to start values. void ResetQueueDelay(); - double scale_increase() const { - return std::clamp(1 - queue_delay_avg_ / (params_.queue_delay_target.Get() * - params_.queue_delay_threshold), - 0.1, 1.0); - } - TimeDelta queue_delay() const { return queue_delay_avg_; } - double queue_delay_dev_norm() const { return queue_delay_dev_norm_; } + TimeDelta queue_delay_min_avg() const { return queue_delay_min_avg_; } + TimeDelta latency_difference_avg() const { return latency_difference_avg_; } + + // Scale factor used for scaling reference window increase/decrease due to + // minimum average queue delay per feedback. The scale factor is 0.1 + // if the delay is higher than the threshold, and increases linearly to 1.0 if + // the delay is lower than the threshold / 4. + double ref_window_scale_factor_due_to_avg_min_delay( + bool allow_zero = false) const; + + // Scale factor used for scaling reference window increase if + // latency differences per feedback is larger than the threshold. If the send + // rate is close to link capacity, the difference between min and max latency + // per feedback increases and the scale factor decreases. + double ref_window_scale_factor_due_to_latency_difference() const; TimeDelta rtt() const { return last_smoothed_rtt_; } @@ -84,6 +89,8 @@ class DelayBasedCongestionControl { } void UpdateSmoothedRtt(TimeDelta rtt_sample); void UpdateQueueDelayAverage(TimeDelta one_way_delay); + void UpdateQueueDelayMinAverage(TimeDelta packet_qdelay); + void UpdateLatencyDifferenceAverage(TimeDelta packet_latency_diff); const ScreamV2Parameters params_; @@ -98,8 +105,10 @@ class DelayBasedCongestionControl { Timestamp min_queue_delay_above_threshold_start_ = Timestamp::MinusInfinity(); TimeDelta last_smoothed_rtt_ = TimeDelta::Zero(); Timestamp last_update_qdelay_avg_time_ = Timestamp::MinusInfinity(); + TimeDelta last_queue_delay_sample_ = TimeDelta::PlusInfinity(); TimeDelta queue_delay_avg_ = TimeDelta::PlusInfinity(); - double queue_delay_dev_norm_ = 0.0; + TimeDelta queue_delay_min_avg_ = TimeDelta::Zero(); + TimeDelta latency_difference_avg_ = TimeDelta::Zero(); }; } // namespace webrtc diff --git a/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc b/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc index a394618abea..eea60d3c7d1 100644 --- a/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc +++ b/modules/congestion_controller/scream/delay_based_congestion_control_unittest.cc @@ -57,7 +57,6 @@ TEST(DelayBasedCongestionControlTest, EXPECT_EQ(delay_controller.rtt(), TimeDelta::Millis(58)); EXPECT_EQ(delay_controller.queue_delay(), TimeDelta::Millis(0)); EXPECT_FALSE(delay_controller.IsQueueDelayDetected()); - EXPECT_FALSE(delay_controller.ShouldReduceReferenceWindow()); } } @@ -82,7 +81,6 @@ TEST(DelayBasedCongestionControlTest, QueueDelayIncreaseIfSendRateIsHigh) { } EXPECT_GT(delay_controller.queue_delay(), TimeDelta::Millis(50)); EXPECT_TRUE(delay_controller.IsQueueDelayDetected()); - EXPECT_TRUE(delay_controller.ShouldReduceReferenceWindow()); } TEST(DelayBasedCongestionControlTest, ReferenceWindowNotChangedOnLowDelay) { @@ -231,6 +229,118 @@ TEST(DelayBasedCongestionControlTest, EXPECT_FALSE(delay_controller.IsQueueDrainedInTime(clock.CurrentTime())); } +TEST(DelayBasedCongestionControlTest, + RefWindowScaleFactorDueToMinAverageQueueDelay) { + SimulatedClock clock(Timestamp::Seconds(1234)); + Environment env = CreateTestEnvironment({.time = &clock}); + ScreamV2Parameters params(env.field_trials()); + DelayBasedCongestionControl delay_controller(params); + + const TimeDelta kQueueDelyMinThreshold = + params.queue_delay_min_threshold.Get(); + + // Helper to establish queue delay. + // Assumes base delay is 100ms. + auto feed_packets = [&](TimeDelta qdelay, int count) { + for (int i = 0; i < count; ++i) { + clock.AdvanceTime(TimeDelta::Millis(10)); + TransportPacketsFeedback msg; + msg.feedback_time = clock.CurrentTime(); + PacketResult packet; + packet.receive_time = clock.CurrentTime(); + packet.sent_packet.send_time = + clock.CurrentTime() - TimeDelta::Millis(100) - qdelay; + packet.sent_packet.sequence_number = i; + msg.packet_feedbacks.push_back(packet); + delay_controller.OnTransportPacketsFeedback(msg); + } + }; + + // Establish base delay (100ms) with qdelay=0. + feed_packets(TimeDelta::Zero(), 100); + EXPECT_DOUBLE_EQ( + delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), 1.0); + + // Drive queue delay to kQueueDelyMinThreshold. + feed_packets(kQueueDelyMinThreshold + TimeDelta::Millis(10), 250); + EXPECT_NEAR(delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), + 0.1, 0.01); + + // Drive queue delay to `0.5 * kQueueDelyMinThreshold`. + // Expected: 0.1 + 1.2 * 0.5 = 0.7 + feed_packets(kQueueDelyMinThreshold / 2, 250); + EXPECT_NEAR(delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), + 0.7, 0.01); + + // Drive queue delay to `0.25 * kQueueDelyMinThreshold`. + // Expected: 0.1 + 1.2 * 0.75 = 1.0. + feed_packets(kQueueDelyMinThreshold / 4, 250); + EXPECT_NEAR(delay_controller.ref_window_scale_factor_due_to_avg_min_delay(), + 1.0, 0.01); +} + +TEST(DelayBasedCongestionControlTest, + RefWindowScaleFactorDueToLatencyDifference) { + SimulatedClock clock(Timestamp::Seconds(1234)); + Environment env = CreateTestEnvironment({.time = &clock}); + ScreamV2Parameters params(env.field_trials()); + DelayBasedCongestionControl delay_controller(params); + + const TimeDelta kLatencyThreshold = params.latency_diff_threshold.Get(); + + // Helper to establish latency differences within feedbacks. + // Assumes a base one-way delay of 100ms. + auto feed_packets = [&](TimeDelta latency_diff, int count) { + for (int i = 0; i < count; ++i) { + clock.AdvanceTime(TimeDelta::Millis(10)); + TransportPacketsFeedback msg; + msg.feedback_time = clock.CurrentTime(); + + PacketResult packet1; + packet1.receive_time = clock.CurrentTime(); + packet1.sent_packet.send_time = + clock.CurrentTime() - TimeDelta::Millis(100); + packet1.sent_packet.sequence_number = i * 2; + + PacketResult packet2; + packet2.receive_time = clock.CurrentTime(); + packet2.sent_packet.send_time = + clock.CurrentTime() - TimeDelta::Millis(100) - latency_diff; + packet2.sent_packet.sequence_number = i * 2 + 1; + + msg.packet_feedbacks.push_back(packet1); + msg.packet_feedbacks.push_back(packet2); + delay_controller.OnTransportPacketsFeedback(msg); + } + }; + + // Establish 0 latency difference. + feed_packets(TimeDelta::Zero(), 100); + EXPECT_DOUBLE_EQ( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), + 1.0); + + // Drive to `kLatencyThreshold`. + feed_packets(kLatencyThreshold + TimeDelta::Millis(10), 250); + EXPECT_NEAR( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), 0.1, + 0.01); + + // Drive to `0.5 * kLatencyThreshold`. + // Expected: 0.1 + 1.2 * 0.5 = 0.7 + feed_packets(kLatencyThreshold / 2, 250); + EXPECT_NEAR( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), 0.7, + 0.01); + + // Drive to `0.25 * kLatencyThreshold`. + // Expected: 0.1 + 1.2 * 0.75 = 1.0. + feed_packets(kLatencyThreshold / 4, 250); + EXPECT_NEAR( + delay_controller.ref_window_scale_factor_due_to_latency_difference(), 1.0, + 0.01); +} + // TODO: bugs.webrtc.org/447037083 - add tests for clock drift in feedback NTP // time. diff --git a/modules/congestion_controller/scream/scream_network_controller.cc b/modules/congestion_controller/scream/scream_network_controller.cc index 5ae247aa36d..c496bc7154c 100644 --- a/modules/congestion_controller/scream/scream_network_controller.cc +++ b/modules/congestion_controller/scream/scream_network_controller.cc @@ -11,14 +11,17 @@ #include "modules/congestion_controller/scream/scream_network_controller.h" #include +#include #include #include #include "api/transport/network_control.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" +#include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/rtc_event_remote_estimate.h" #include "modules/congestion_controller/scream/scream_v2.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -34,9 +37,16 @@ ScreamNetworkController::ScreamNetworkController(NetworkControllerConfig config) config.stream_based_config.enable_repeated_initial_probing), current_pacing_window_(config.default_pacing_time_window), scream_(std::in_place, env_), - target_rate_constraints_(config.constraints), + min_target_rate_( + config.constraints.min_data_rate.value_or(DataRate::Zero())), + max_target_rate_( + config.constraints.max_data_rate.value_or(DataRate::PlusInfinity())), + starting_rate_( + config.constraints.starting_rate.value_or(kDefaultStartRate)), streams_config_(config.stream_based_config), - last_padding_interval_started_(Timestamp::Zero()) { + max_seen_total_allocated_bitrate_( + config.stream_based_config.max_total_allocated_bitrate.value_or( + DataRate::Zero())) { UpdateScreamTargetBitrateConstraints(); } @@ -44,18 +54,20 @@ void ScreamNetworkController::UpdateScreamTargetBitrateConstraints() { // TODO: bugs.webrtc.org/447037083 - We should also consider remote network // state estimates. scream_->SetTargetBitrateConstraints( - target_rate_constraints_.min_data_rate.value_or(DataRate::Zero()), - std::min(target_rate_constraints_.max_data_rate.value_or( - DataRate::PlusInfinity()), - remote_bitrate_report_.value_or(DataRate::PlusInfinity()))); + min_target_rate_, + std::min(max_target_rate_, + remote_bitrate_report_.value_or(DataRate::PlusInfinity())), + starting_rate_); } NetworkControlUpdate ScreamNetworkController::CreateFirstUpdate(Timestamp now) { RTC_DCHECK(network_available_); RTC_DCHECK(!first_update_created_); first_update_created_ = true; - scream_->SetFirstTargetRate( - target_rate_constraints_.starting_rate.value_or(kDefaultStartRate)); + padding_interval_end_time_ = Timestamp::MinusInfinity(); + if (allow_initial_bwe_before_media_) { + initial_bwe_probe_end_time_ = now + params_.initial_probing_duration.Get(); + } NetworkControlUpdate update = CreateUpdate(now); if (allow_initial_bwe_before_media_) { @@ -78,8 +90,7 @@ NetworkControlUpdate ScreamNetworkController::CreateFirstUpdate(Timestamp now) { NetworkControlUpdate ScreamNetworkController::OnNetworkAvailability( NetworkAvailability msg) { network_available_ = msg.network_available; - if (!first_update_created_ && network_available_ && - streams_config_.max_total_allocated_bitrate > DataRate::Zero()) { + if (!first_update_created_ && network_available_) { return CreateFirstUpdate(msg.at_time); } return NetworkControlUpdate(); @@ -88,7 +99,9 @@ NetworkControlUpdate ScreamNetworkController::OnNetworkAvailability( NetworkControlUpdate ScreamNetworkController::OnNetworkRouteChange( NetworkRouteChange msg) { RTC_LOG(LS_INFO) << " OnNetworkRouteChange, resetting ScreamV2."; - target_rate_constraints_ = msg.constraints; + min_target_rate_ = msg.constraints.min_data_rate.value_or(min_target_rate_); + max_target_rate_ = msg.constraints.max_data_rate.value_or(max_target_rate_); + starting_rate_ = msg.constraints.starting_rate.value_or(starting_rate_); scream_.emplace(env_); first_update_created_ = false; UpdateScreamTargetBitrateConstraints(); @@ -101,8 +114,9 @@ NetworkControlUpdate ScreamNetworkController::OnNetworkRouteChange( NetworkControlUpdate ScreamNetworkController::OnProcessInterval( ProcessInterval msg) { - // Scream currently has no need for periodic processing. - return NetworkControlUpdate(); + NetworkControlUpdate update; + update.pacer_config = MaybeCreatePacerConfig(msg.at_time); + return update; } NetworkControlUpdate ScreamNetworkController::OnRemoteBitrateReport( @@ -120,9 +134,8 @@ NetworkControlUpdate ScreamNetworkController::OnRoundTripTimeUpdate( NetworkControlUpdate ScreamNetworkController::OnSentPacket(SentPacket msg) { scream_->OnPacketSent(msg.data_in_flight); - if (msg.data_in_flight > scream_->max_data_in_flight()) { - RTC_LOG(LS_VERBOSE) << " Send window full:" << msg.data_in_flight << " > " - << scream_->max_data_in_flight(); + if (msg.data_in_flight > scream_->max_data_in_flight() || + scream_->delay_based_congestion_control().IsQueueDelayDetected()) { return CreateUpdate(msg.send_time); } return NetworkControlUpdate(); @@ -136,7 +149,13 @@ NetworkControlUpdate ScreamNetworkController::OnReceivedPacket( NetworkControlUpdate ScreamNetworkController::OnStreamsConfig( StreamsConfig msg) { + RTC_LOG_IF(LS_VERBOSE, msg.max_total_allocated_bitrate.has_value()) + << "OnStreamsConfig: max_total_allocated_bitrate=" + << *msg.max_total_allocated_bitrate; streams_config_ = msg; + max_seen_total_allocated_bitrate_ = std::max( + max_seen_total_allocated_bitrate_, + streams_config_.max_total_allocated_bitrate.value_or(DataRate::Zero())); if (!first_update_created_ && network_available_ && streams_config_.max_total_allocated_bitrate > DataRate::Zero()) { return CreateFirstUpdate(msg.at_time); @@ -146,7 +165,9 @@ NetworkControlUpdate ScreamNetworkController::OnStreamsConfig( NetworkControlUpdate ScreamNetworkController::OnTargetRateConstraints( TargetRateConstraints msg) { - target_rate_constraints_ = msg; + min_target_rate_ = msg.min_data_rate.value_or(min_target_rate_); + max_target_rate_ = msg.max_data_rate.value_or(max_target_rate_); + starting_rate_ = msg.starting_rate.value_or(starting_rate_); UpdateScreamTargetBitrateConstraints(); // No need to change target rate immediately. Wait until next feedback. return NetworkControlUpdate(); @@ -160,7 +181,9 @@ NetworkControlUpdate ScreamNetworkController::OnTransportLossReport( NetworkControlUpdate ScreamNetworkController::OnNetworkStateEstimate( NetworkStateEstimate msg) { // TODO: bugs.webrtc.org/447037083 - Implement; - RTC_LOG_F(LS_INFO) << "Not implemented"; + env_.event_log().Log(std::make_unique( + msg.link_capacity_lower, msg.link_capacity_upper)); + return NetworkControlUpdate(); } @@ -184,58 +207,69 @@ NetworkControlUpdate ScreamNetworkController::CreateUpdate(Timestamp now) { target_rate_msg.network_estimate.bwe_period = TimeDelta::Millis(25); update.target_rate = target_rate_msg; } - update.pacer_config = MaybeCreatePacerConfig(); + update.pacer_config = MaybeCreatePacerConfig(now); update.congestion_window = scream_->max_data_in_flight(); return update; } -std::optional ScreamNetworkController::MaybeCreatePacerConfig() { - // Time window used for calculating pacing window if target rate is - // constrained by CE markings. - constexpr TimeDelta kReducedPacingWindow = TimeDelta::Millis(20); - // Threshold used for guessing if target rate is constrained due to CE - // marking. - constexpr double kL4sAlphaThreshold = 0.01; +std::optional ScreamNetworkController::MaybeCreatePacerConfig( + Timestamp now) { + // Allow sending packets in larger bursts if some time has passed since last + // congestion event. + TimeDelta pacing_window = + (env_.clock().CurrentTime() - + scream_->last_reaction_to_congestion_time() > + params_.allow_large_pacing_bursts_after_congestion_time.Get()) + ? default_pacing_window_ + : TimeDelta::Millis(10); - DataRate max_needed_rate = - streams_config_.max_total_allocated_bitrate.value_or(DataRate::Zero()); + bool allow_padding = false; + if (params_.time_between_periodic_padding.Get().IsFinite() && + now - scream_->last_reference_window_decrease_time() > + params_.allow_padding_after_last_congestion_time) { + if (now < padding_interval_end_time_) { + // We are currently inside an active padding duration. + allow_padding = true; + } else if (now - padding_interval_end_time_ >= + params_.time_between_periodic_padding.Get()) { + // Enough time has passed since the end of the last padding interval; + // start a new one. + padding_interval_end_time_ = + now + params_.periodic_padding_duration.Get(); + allow_padding = true; + } + } else if (now < padding_interval_end_time_) { + // Stop padding immediately if a congestion event occurred recently. + padding_interval_end_time_ = now; + } DataRate padding_rate = DataRate::Zero(); - TimeDelta pacing_window = current_pacing_window_; - DataRate target_rate = scream_->target_rate(); - - Timestamp now = env_.clock().CurrentTime(); - if (target_rate < max_needed_rate && - target_rate < target_rate_constraints_.max_data_rate.value_or( - DataRate::PlusInfinity())) { - // Periodically allow padding to be used to reach a target rate close to - // `max_needed_rate`. - if (params_.periodic_padding_interval->IsFinite() && - (now - last_padding_interval_started_ > - params_.periodic_padding_interval.Get())) { - last_padding_interval_started_ = now; - } - if (now - last_padding_interval_started_ < - params_.periodic_padding_duration.Get()) { - padding_rate = target_rate; + if (allow_padding) { + // Padding is allowed. + DataRate max_padding_rate = + std::min({max_target_rate_, max_seen_total_allocated_bitrate_, + 2 * streams_config_.max_total_allocated_bitrate.value_or( + DataRate::Zero()), + remote_bitrate_report_.value_or(DataRate::PlusInfinity())}); + if (max_padding_rate.IsZero() && now < initial_bwe_probe_end_time_) { + // If initial BWE probing is allowed, probe up to the max target rate. + max_padding_rate = max_target_rate_; } + DataRate target_rate = scream_->target_rate(); + padding_rate = + target_rate < max_padding_rate ? target_rate : DataRate::Zero(); } - if (current_pacing_window_ == default_pacing_window_ && - scream_->target_rate() < max_needed_rate && - scream_->l4s_alpha() > kL4sAlphaThreshold) { - // Do stricter pacing if target rate is lower than what is needed and it - // seems like L4S is enabled. Note that once stricter pacing is enabled, - // it is not stopped. - pacing_window = std::min(default_pacing_window_, kReducedPacingWindow); - } DataRate pacing_rate = scream_->pacing_rate(); if (padding_rate != reported_padding_rate_ || pacing_rate != reported_pacing_rate_ || current_pacing_window_ != pacing_window) { + RTC_LOG_IF(LS_VERBOSE, current_pacing_window_ != pacing_window) + << "Pacing window changed: " << pacing_window; reported_padding_rate_ = padding_rate; reported_pacing_rate_ = pacing_rate; current_pacing_window_ = pacing_window; + return PacerConfig::Create(now, pacing_rate, padding_rate, current_pacing_window_); } diff --git a/modules/congestion_controller/scream/scream_network_controller.h b/modules/congestion_controller/scream/scream_network_controller.h index b5630136cc6..2a03ce67411 100644 --- a/modules/congestion_controller/scream/scream_network_controller.h +++ b/modules/congestion_controller/scream/scream_network_controller.h @@ -50,7 +50,7 @@ class ScreamNetworkController : public NetworkControllerInterface { void UpdateScreamTargetBitrateConstraints(); NetworkControlUpdate CreateFirstUpdate(Timestamp now); NetworkControlUpdate CreateUpdate(Timestamp now); - std::optional MaybeCreatePacerConfig(); + std::optional MaybeCreatePacerConfig(Timestamp now); Environment env_; const ScreamV2Parameters params_; @@ -60,11 +60,14 @@ class ScreamNetworkController : public NetworkControllerInterface { bool network_available_ = false; TimeDelta current_pacing_window_; std::optional scream_; - TargetRateConstraints target_rate_constraints_; + DataRate min_target_rate_; + DataRate max_target_rate_; + DataRate starting_rate_; std::optional remote_bitrate_report_; StreamsConfig streams_config_; - - Timestamp last_padding_interval_started_; + DataRate max_seen_total_allocated_bitrate_ = DataRate::Zero(); + Timestamp initial_bwe_probe_end_time_ = Timestamp::MinusInfinity(); + Timestamp padding_interval_end_time_ = Timestamp::MinusInfinity(); // Values last reported in a NetworkControlUpdate. Used for finding out if an // update needs to be reported. diff --git a/modules/congestion_controller/scream/scream_network_controller_unittest.cc b/modules/congestion_controller/scream/scream_network_controller_unittest.cc index d39b46cef32..c5046b3f08d 100644 --- a/modules/congestion_controller/scream/scream_network_controller_unittest.cc +++ b/modules/congestion_controller/scream/scream_network_controller_unittest.cc @@ -14,6 +14,7 @@ #include "api/transport/network_control.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" +#include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/congestion_controller/scream/test/cc_feedback_generator.h" @@ -48,8 +49,8 @@ TEST(ScreamControllerTest, OnNetworkAvailabilityUpdatesTargetRateAndPacerRate) { DataRate::KilobitsPerSec(456); ScreamNetworkController scream_controller(config); - NetworkControlUpdate update = - scream_controller.OnNetworkAvailability({.network_available = true}); + NetworkControlUpdate update = scream_controller.OnNetworkAvailability( + {.at_time = clock.CurrentTime(), .network_available = true}); ASSERT_TRUE(update.has_updates()); ASSERT_TRUE(update.target_rate.has_value()); EXPECT_EQ(update.target_rate->target_rate, config.constraints.starting_rate); @@ -92,7 +93,8 @@ TEST(ScreamControllerTest, config.stream_based_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(1000); ScreamNetworkController scream_controller(config); - scream_controller.OnNetworkAvailability({.network_available = true}); + scream_controller.OnNetworkAvailability( + {.at_time = clock.CurrentTime(), .network_available = true}); CcFeedbackGenerator feedback_generator({}); DataRate send_rate = DataRate::KilobitsPerSec(100); @@ -221,7 +223,10 @@ TEST(ScreamControllerTest, PacingWindowReducedIfCeCongestedStreamsConfigured) { DataRate send_rate = DataRate::KilobitsPerSec(500); for (int i = 0; i < 20; ++i) { TransportPacketsFeedback feedback = - feedback_generator.ProcessUntilNextFeedback(send_rate, clock); + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream_controller.OnSentPacket(packet); + }); update = scream_controller.OnTransportPacketsFeedback(feedback); if (update.target_rate.has_value()) { send_rate = update.target_rate->target_rate; @@ -233,7 +238,7 @@ TEST(ScreamControllerTest, PacingWindowReducedIfCeCongestedStreamsConfigured) { } TEST(ScreamControllerTest, - PacingWindowNotReducedIfDelayCongestedStreamsConfigured) { + PacingWindowReducedIfDelayCongestedStreamsConfigured) { SimulatedClock clock(Timestamp::Seconds(1'234)); Environment env = CreateTestEnvironment({.time = &clock}); CcFeedbackGenerator feedback_generator( @@ -249,9 +254,44 @@ TEST(ScreamControllerTest, NetworkControlUpdate update; DataRate send_rate = DataRate::KilobitsPerSec(500); - for (int i = 0; i < 20; ++i) { + for (int i = 0; i < 30; ++i) { TransportPacketsFeedback feedback = - feedback_generator.ProcessUntilNextFeedback(send_rate, clock); + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream_controller.OnSentPacket(packet); + }); + update = scream_controller.OnTransportPacketsFeedback(feedback); + if (update.target_rate.has_value()) { + send_rate = update.target_rate->target_rate; + } + } + EXPECT_THAT(update.pacer_config, + Optional(Field(&PacerConfig::time_window, + Lt(PacerConfig::kDefaultTimeInterval)))); +} + +TEST(ScreamControllerTest, PacingWindowNotReducedIfNotCongested) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + CcFeedbackGenerator feedback_generator( + {.network_config = {.link_capacity = DataRate::KilobitsPerSec(9000)}, + .send_as_ect1 = false}); // Scream will react to delay increase, not CE. + + NetworkControllerConfig config(env); + ScreamNetworkController scream_controller(config); + + StreamsConfig streams_config; + streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(1000); + scream_controller.OnStreamsConfig(streams_config); + + NetworkControlUpdate update; + DataRate send_rate = DataRate::KilobitsPerSec(500); + for (int i = 0; i < 30; ++i) { + TransportPacketsFeedback feedback = + feedback_generator.ProcessUntilNextFeedback( + send_rate, clock, [&](const SentPacket& packet) { + scream_controller.OnSentPacket(packet); + }); update = scream_controller.OnTransportPacketsFeedback(feedback); if (update.target_rate.has_value()) { send_rate = update.target_rate->target_rate; @@ -274,18 +314,20 @@ TEST(ScreamControllerTest, InitiallyPaddingIsAllowedToReachNeededRate) { StreamsConfig streams_config; streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(1000); scream_controller.OnStreamsConfig(streams_config); + NetworkControlUpdate update = scream_controller.OnNetworkAvailability( + {.at_time = clock.CurrentTime(), .network_available = true}); DataRate send_rate = DataRate::KilobitsPerSec(50); DataRate target_rate = DataRate::Zero(); bool padding_set = false; + Timestamp padding_stop = Timestamp::Zero(); Timestamp start_time = clock.CurrentTime(); while (clock.CurrentTime() < start_time + TimeDelta::Seconds(1)) { TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback( send_rate, clock, [&](SentPacket packet) { scream_controller.OnSentPacket(packet); }); - NetworkControlUpdate update = - scream_controller.OnTransportPacketsFeedback(feedback); + update = scream_controller.OnTransportPacketsFeedback(feedback); if (update.pacer_config.has_value()) { if (update.pacer_config->pad_rate() != DataRate::Zero()) { padding_set = true; @@ -298,6 +340,8 @@ TEST(ScreamControllerTest, InitiallyPaddingIsAllowedToReachNeededRate) { EXPECT_LT( update.pacer_config->pad_rate(), update.target_rate->target_rate + DataRate::KilobitsPerSec(1)); + } else if (padding_set && padding_stop.IsZero()) { + padding_stop = clock.CurrentTime(); } } if (update.target_rate) { @@ -310,61 +354,248 @@ TEST(ScreamControllerTest, InitiallyPaddingIsAllowedToReachNeededRate) { // But not much more, since seen data in flight should limit the target rate // increase. EXPECT_LE(target_rate, 1.5 * (*streams_config.max_total_allocated_bitrate)); + // Padding should stop when target is reached. + EXPECT_LT(padding_stop - start_time, TimeDelta::Seconds(1)); } -TEST(ScreamControllerTest, PeriodicallyAllowPadding) { +TEST(ScreamControllerTest, InitialProbingWithoutMaxTotalAllocatedBitrate) { SimulatedClock clock(Timestamp::Seconds(1'234)); Environment env = CreateTestEnvironment({.time = &clock}); - CcFeedbackGenerator feedback_generator( - {.network_config = {.queue_delay_ms = 10, - .link_capacity = DataRate::KilobitsPerSec(15000)}, - .send_as_ect1 = true}); - NetworkControllerConfig config(env); - ScreamNetworkController scream_controller(config); + config.stream_based_config.enable_repeated_initial_probing = true; + config.constraints.starting_rate = DataRate::KilobitsPerSec(100); + config.constraints.max_data_rate = DataRate::KilobitsPerSec(1000); + // Do not set config.stream_based_config.max_total_allocated_bitrate - StreamsConfig streams_config; - streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(5000); - scream_controller.OnStreamsConfig(streams_config); + ScreamNetworkController scream_controller(config); + NetworkControlUpdate update = scream_controller.OnNetworkAvailability( + {.at_time = clock.CurrentTime(), .network_available = true}); + + ASSERT_TRUE(update.pacer_config.has_value()); + // Padding is allowed during the first 6 seconds even if + // max_total_allocated_bitrate is zero. + EXPECT_EQ(update.pacer_config->pad_rate(), config.constraints.starting_rate); + + // Advance clock past the 6s initial BWE probe window. + clock.AdvanceTime(TimeDelta::Seconds(7)); + update = + scream_controller.OnProcessInterval({.at_time = clock.CurrentTime()}); + // Since max_total_allocated_bitrate is not set, padding should stop after the + // initial probe window. + if (update.pacer_config.has_value()) { + EXPECT_EQ(update.pacer_config->pad_rate(), DataRate::Zero()); + } +} - Timestamp padding_start_1 = Timestamp::Zero(); - Timestamp padding_start_2 = Timestamp::Zero(); +struct PaddingTestResult { + DataRate target_rate; + Timestamp padding_start; + Timestamp padding_stop; +}; + +PaddingTestResult ProcessUntilPaddingStartAndStop( + SimulatedClock& clock, + ScreamNetworkController& scream_controller, + CcFeedbackGenerator& feedback_generator, + bool increase_send_rate = true) { + DataRate target_rate = DataRate::Zero(); + Timestamp padding_start = Timestamp::Zero(); Timestamp padding_stop = Timestamp::Zero(); Timestamp start_time = clock.CurrentTime(); - DataRate send_rate = DataRate::KilobitsPerSec(50); - DataRate padding_rate = DataRate::Zero(); - while (clock.CurrentTime() < start_time + TimeDelta::Seconds(20)) { - // Use a send rate much lower than the target rate just to ramp up very slow - // to be able to test that padding stops and resumes. + + while (clock.CurrentTime() < start_time + TimeDelta::Seconds(10)) { TransportPacketsFeedback feedback = feedback_generator.ProcessUntilNextFeedback( - send_rate + padding_rate / 4, clock, + send_rate, clock, [&](SentPacket packet) { scream_controller.OnSentPacket(packet); }); NetworkControlUpdate update = scream_controller.OnTransportPacketsFeedback(feedback); if (update.pacer_config.has_value()) { - padding_rate = update.pacer_config->pad_rate(); if (update.pacer_config->pad_rate() != DataRate::Zero()) { - if (padding_start_1.IsZero()) { - padding_start_1 = clock.CurrentTime(); + if (padding_start.IsZero()) { + padding_start = clock.CurrentTime(); } - if (!padding_stop.IsZero() && padding_start_2.IsZero()) { - padding_start_2 = clock.CurrentTime(); - } - } else { - if (padding_stop.IsZero()) { - padding_stop = clock.CurrentTime(); + if (increase_send_rate) { + // Set the send rate equal to the padding rate. + send_rate = update.pacer_config->pad_rate(); } + } else if (!padding_start.IsZero() && padding_stop.IsZero()) { + padding_stop = clock.CurrentTime(); } } + if (update.target_rate) { + target_rate = update.target_rate->target_rate; + } + if (!padding_stop.IsZero()) { + break; + } } - TimeDelta padding_duration = padding_stop - padding_start_1; - TimeDelta time_betwee_padding = padding_start_2 - padding_stop; - EXPECT_GT(padding_duration, TimeDelta::Millis(900)); - EXPECT_LT(padding_duration, TimeDelta::Millis(1100)); - EXPECT_GT(time_betwee_padding, TimeDelta::Millis(8900)); - EXPECT_LT(time_betwee_padding, TimeDelta::Millis(11000)); + EXPECT_FALSE(padding_start.IsZero()); + EXPECT_FALSE(padding_stop.IsZero()); + return {.target_rate = target_rate, + .padding_start = padding_start, + .padding_stop = padding_stop}; +} + +TEST(ScreamControllerTest, PaddingStopIfNetworkCongested) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + NetworkControllerConfig config(env); + ScreamNetworkController scream_controller(config); + CcFeedbackGenerator feedback_generator( + {.network_config = {.queue_delay_ms = 10, + .link_capacity = DataRate::KilobitsPerSec(500)}, + .send_as_ect1 = true}); + StreamsConfig streams_config; + streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(1000); + scream_controller.OnStreamsConfig(streams_config); + + PaddingTestResult result = ProcessUntilPaddingStartAndStop( + clock, scream_controller, feedback_generator); + + EXPECT_LT(result.target_rate, DataRate::KilobitsPerSec(720)); + // Padding should stop when congestion is detected. + EXPECT_LT(result.padding_stop - result.padding_start, TimeDelta::Seconds(1)); +} + +TEST(ScreamControllerTest, PeriodicallyAllowPadding) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + CcFeedbackGenerator feedback_generator( + {.network_config = {.queue_delay_ms = 10, + .link_capacity = DataRate::KilobitsPerSec(15000)}, + .send_as_ect1 = true}); + + NetworkControllerConfig config(env); + ScreamNetworkController scream_controller(config); + + StreamsConfig streams_config; + streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(1000); + scream_controller.OnStreamsConfig(streams_config); + + PaddingTestResult result_1 = ProcessUntilPaddingStartAndStop( + clock, scream_controller, feedback_generator, + /*increase_send_rate=*/false); + PaddingTestResult result_2 = ProcessUntilPaddingStartAndStop( + clock, scream_controller, feedback_generator); + + TimeDelta padding_duration = result_1.padding_stop - result_1.padding_start; + TimeDelta time_between_padding = + result_2.padding_start - result_1.padding_stop; + EXPECT_GT(padding_duration, TimeDelta::Millis(2500)); + EXPECT_LT(padding_duration, TimeDelta::Millis(3300)); + + EXPECT_GT(time_between_padding, TimeDelta::Millis(2500)); + EXPECT_LT(time_between_padding, TimeDelta::Millis(3300)); +} + +TEST(ScreamControllerTest, DelayPaddingAfterCongestion) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + NetworkControllerConfig config(env); + ScreamNetworkController scream_controller(config); + CcFeedbackGenerator feedback_generator( + {.network_config = {.queue_delay_ms = 10, + .link_capacity = DataRate::KilobitsPerSec(500)}, + .send_as_ect1 = true}); + StreamsConfig streams_config; + streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(1000); + scream_controller.OnStreamsConfig(streams_config); + + // Padding should start, but then stop since pushing to max allocated causes + // congestion. + PaddingTestResult result_1 = ProcessUntilPaddingStartAndStop( + clock, scream_controller, feedback_generator, + /*increase_send_rate=*/true); + + // Ensure it was stopped quickly when congestion kicked in. + EXPECT_LT(result_1.padding_stop - result_1.padding_start, + TimeDelta::Seconds(1)); + + // Network recovers because increase_send_rate=false keeps sending rate at + // 50kbps. Wait until next padding starts. + PaddingTestResult result_2 = ProcessUntilPaddingStartAndStop( + clock, scream_controller, feedback_generator, + /*increase_send_rate=*/false); + + TimeDelta time_until_padding = result_2.padding_start - result_1.padding_stop; + EXPECT_GT(time_until_padding, TimeDelta::Millis(2500)); + EXPECT_LT(time_until_padding, TimeDelta::Millis(3500)); +} + +TEST(ScreamControllerTest, PadsToMinOf2xCurrentMaxAndEverSeenMax) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + NetworkControllerConfig config(env); + ScreamNetworkController scream_controller(config); + CcFeedbackGenerator feedback_generator( + {.network_config = {.queue_delay_ms = 50, + .link_capacity = DataRate::KilobitsPerSec(5000)}, + .send_as_ect1 = true}); + StreamsConfig streams_config; + streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(1000); + scream_controller.OnStreamsConfig(streams_config); + // Even if max_total_allocated_bitrate is lowered, padding is still allowed up + // to 2x the new max and previous max. + streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(300); + scream_controller.OnStreamsConfig(streams_config); + + PaddingTestResult result_1 = ProcessUntilPaddingStartAndStop( + clock, scream_controller, feedback_generator); + EXPECT_LT(result_1.target_rate, DataRate::KilobitsPerSec(700)); + + streams_config.max_total_allocated_bitrate = DataRate::KilobitsPerSec(800); + scream_controller.OnStreamsConfig(streams_config); + + PaddingTestResult result_2 = ProcessUntilPaddingStartAndStop( + clock, scream_controller, feedback_generator); + EXPECT_LT(result_2.target_rate, DataRate::KilobitsPerSec(1100)); +} + +TEST(ScreamControllerTest, CanSetStartBitrate) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + NetworkControllerConfig config(env); + + config.constraints.starting_rate = DataRate::KilobitsPerSec(3000); + ScreamNetworkController scream_controller(config); + CcFeedbackGenerator feedback_generator( + {.network_config = {.queue_delay_ms = 50, + .link_capacity = DataRate::KilobitsPerSec(5000)}}); + + TransportPacketsFeedback feedback = + feedback_generator.ProcessUntilNextFeedback( + /*send_rate=*/DataRate::KilobitsPerSec(100), clock, + [&](SentPacket packet) { scream_controller.OnSentPacket(packet); }); + NetworkControlUpdate update = + scream_controller.OnTransportPacketsFeedback(feedback); + EXPECT_GE(update.target_rate->target_rate, DataRate::KilobitsPerSec(2980)); + EXPECT_LT(update.target_rate->target_rate, DataRate::KilobitsPerSec(3300)); +} + +TEST(ScreamControllerTest, IgnoreFeedbackWithoutReceivedPackets) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + NetworkControllerConfig config(env); + config.constraints.starting_rate = DataRate::KilobitsPerSec(300); + ScreamNetworkController scream_controller(config); + + TransportPacketsFeedback msg; + msg.feedback_time = clock.CurrentTime(); + PacketResult result; + result.sent_packet.send_time = clock.CurrentTime(); + result.sent_packet.sequence_number = 1; + result.sent_packet.size = DataSize::Bytes(1000); + ASSERT_FALSE(result.IsReceived()); + msg.packet_feedbacks.push_back(result); + + NetworkControlUpdate update = + scream_controller.OnTransportPacketsFeedback(msg); + // Scream should not change the target rate if there are no received packets. + // Since this is the first feedback, the target rate should be the starting + // rate. + EXPECT_EQ(update.target_rate->target_rate, DataRate::KilobitsPerSec(300)); } } // namespace diff --git a/modules/congestion_controller/scream/scream_v2.cc b/modules/congestion_controller/scream/scream_v2.cc index 2286f58e67a..786ee7e7560 100644 --- a/modules/congestion_controller/scream/scream_v2.cc +++ b/modules/congestion_controller/scream/scream_v2.cc @@ -45,7 +45,7 @@ DataSize DataUnitsAckedAndNotMarked(const TransportPacketsFeedback& msg) { bool HasLostPackets(const TransportPacketsFeedback& msg) { for (const auto& packet : msg.PacketsWithFeedback()) { - if (!packet.IsReceived()) { + if (!packet.IsReceived() && packet.reported_lost_for_the_first_time) { return true; } } @@ -67,12 +67,18 @@ ScreamV2::ScreamV2(const Environment& env) ref_window_(params_.min_ref_window.Get()), delay_based_congestion_control_(params_) {} -void ScreamV2::SetTargetBitrateConstraints(DataRate min, DataRate max) { +void ScreamV2::SetTargetBitrateConstraints(DataRate min, + DataRate max, + DataRate start) { RTC_DCHECK_GE(max, min); min_target_bitrate_ = min; max_target_bitrate_ = max; - RTC_LOG_F(LS_INFO) << "min_target_bitrate_=" << min_target_bitrate_ - << " max_target_bitrate_=" << max_target_bitrate_; + if (!first_feedback_processed_) { + target_rate_ = start; + } + RTC_LOG_F(LS_VERBOSE) << "min_target_bitrate_=" << min_target_bitrate_ + << " max_target_bitrate_=" << max_target_bitrate_ + << " start_bitrate_=" << target_rate_; } void ScreamV2::OnPacketSent(DataSize data_in_flight) { @@ -81,10 +87,24 @@ void ScreamV2::OnPacketSent(DataSize data_in_flight) { } void ScreamV2::OnTransportPacketsFeedback(const TransportPacketsFeedback& msg) { + if (msg.ReceivedWithSendInfo().empty()) { + RTC_LOG(LS_INFO) << "No received packets in feedback, ignoring."; + return; + } max_data_in_flight_this_rtt_ = std::max(max_data_in_flight_this_rtt_, msg.data_in_flight); delay_based_congestion_control_.OnTransportPacketsFeedback(msg); + + if (!first_feedback_processed_) { + RTC_LOG(LS_INFO) << "Initial RTT: " + << delay_based_congestion_control_.rtt().ms() + << "ms, Start Bitrate: " << target_rate_.kbps() << "kbps"; + ref_window_ = + std::max(params_.min_ref_window.Get(), + target_rate_ * delay_based_congestion_control_.rtt()); + first_feedback_processed_ = true; + } UpdateFeedbackHoldTime(msg); UpdateL4SAlpha(msg); UpdateRefWindow(msg); @@ -132,8 +152,7 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { bool is_ce = msg.HasPacketWithEcnCe(); bool is_loss = HasLostPackets(msg); bool is_virtual_ce = false; - if (delay_based_congestion_control_.ShouldReduceReferenceWindow()) { - // L4S does not seem to be enabled and queue has grown. + if (delay_based_congestion_control_.IsQueueDelayDetected()) { is_virtual_ce = true; } @@ -153,8 +172,6 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { // per RTT backoff /= std::max( 1.0, delay_based_congestion_control_.rtt() / params_.virtual_rtt); - // Increase stability for very small ref_wnd - backoff *= std::max(0.5, 1.0 - ref_window_mss_ratio()); if (!delay_based_congestion_control_.IsQueueDelayDetected()) { // Scale down backoff if close to the last known max reference window @@ -165,10 +182,9 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { // Counterbalance the limitation in reference window increase when the // queue delay varies. This helps to avoid starvation in the presence // of competing TCP Prague flows. - backoff *= std::max( - 0.1, - (0.1 - delay_based_congestion_control_.queue_delay_dev_norm()) / - 0.1); + backoff *= + std::max(0.1, delay_based_congestion_control_ + .ref_window_scale_factor_due_to_avg_min_delay()); } if (msg.feedback_time - last_reaction_to_congestion_time_ > @@ -193,11 +209,6 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { ref_window_ = delay_based_congestion_control_.UpdateReferenceWindow( ref_window_, ref_window_mss_ratio()); } - - if (allow_ref_window_i_update_) { - ref_window_i_ = ref_window_; - allow_ref_window_i_update_ = false; - } } // Increase ref_window. @@ -209,8 +220,7 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { // Just because there is a CE event, does not mean we send too much. At // rates close to the capacity, it is quite likely that one packet is CE // marked in every feedback. - DataSize increase = - DataUnitsAckedAndNotMarked(msg) * ref_window_mss_ratio(); + double increase_scale_factor = ref_window_mss_ratio(); // Limit increase for small RTTs if (delay_based_congestion_control_.rtt() + feedback_hold_time_ < @@ -218,27 +228,25 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { double rtt_ratio = (delay_based_congestion_control_.rtt() + feedback_hold_time_) / params_.virtual_rtt.Get(); - increase = increase * (rtt_ratio * rtt_ratio); + increase_scale_factor = increase_scale_factor * (rtt_ratio * rtt_ratio); } // Limit increase when close to the last inflection point. - increase = increase * - std::max(0.25, ref_window_scale_factor_close_to_ref_window_i()); - - // Limit increase when the reference window close to max segment size. - increase = increase * std::max(0.5, 1.0 - ref_window_mss_ratio()); - - // Limit increase if L4S not enabled and queue delay is increased. - if (l4s_alpha_ < 0.0001) { - increase = increase * delay_based_congestion_control_.scale_increase(); - } - - // Limit increase further if RTT varies. - increase = - increase * - std::max(0.1, (0.1 - - delay_based_congestion_control_.queue_delay_dev_norm()) / - 0.1); + increase_scale_factor = + increase_scale_factor * + std::max(0.25, ref_window_scale_factor_close_to_ref_window_i()); + + // Put a additional restriction on reference window growth if rtt varies a + // lot. + // Better to enforce a slow increase in reference window and get + // a more stable bitrate. + increase_scale_factor = increase_scale_factor * + delay_based_congestion_control_ + .ref_window_scale_factor_due_to_avg_min_delay(); + increase_scale_factor = + increase_scale_factor * + delay_based_congestion_control_ + .ref_window_scale_factor_due_to_latency_difference(); // Use lower multiplicative scale factor if congestion was detected // recently. @@ -254,22 +262,14 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { post_congestion_scale * ref_window_scale_factor_close_to_ref_window_i(); RTC_DCHECK_GE(multiplicative_scale, 1.0); - increase = increase * multiplicative_scale; - - // Increase ref_window only if bytes in flight is large enough. - // Quite a lot of slack is allowed here to avoid that bitrate locks to low - // values. Increase is inhibited if max target bitrate is reached. - DataSize max_allowed_ref_window = - std::max(params_.max_segment_size.Get() + - std::max(max_data_in_flight_this_rtt_, - max_data_in_flight_prev_rtt_) * - params_.bytes_in_flight_head_room.Get(), - params_.min_ref_window.Get()); - - if (ref_window_ < max_allowed_ref_window) { - ref_window_ = - std::clamp(ref_window_ + increase, params_.min_ref_window.Get(), - max_allowed_ref_window); + increase_scale_factor = increase_scale_factor * multiplicative_scale; + + DataSize increase = DataUnitsAckedAndNotMarked(msg) * increase_scale_factor; + last_ref_window_increase_scale_factor_ = increase_scale_factor; + DataSize max_ref_window = max_allowed_ref_window(); + if (ref_window_ < max_ref_window) { + ref_window_ = std::clamp(ref_window_ + increase, + params_.min_ref_window.Get(), max_ref_window); } } @@ -279,6 +279,13 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { // there is a congestion event. allow_ref_window_i_update_ = true; } + if (previous_ref_window > ref_window_) { + last_ref_window_decrease_time_ = msg.feedback_time; + if (allow_ref_window_i_update_) { + ref_window_i_ = previous_ref_window; + allow_ref_window_i_update_ = false; + } + } RTC_LOG_IF(LS_VERBOSE, previous_ref_window != ref_window_) << "ScreamV2: " @@ -289,8 +296,6 @@ void ScreamV2::UpdateRefWindow(const TransportPacketsFeedback& msg) { << " is_virtual_ce=" << is_virtual_ce << " is_loss=" << is_loss << " smoothed_rtt=" << delay_based_congestion_control_.rtt().ms() << ", queue_delay=" << delay_based_congestion_control_.queue_delay().ms() - << ", queue_delay_dev_norm=" - << delay_based_congestion_control_.queue_delay_dev_norm() << " feedback_hold" << feedback_hold_time_.ms() << ", target_rate =" << target_rate_.kbps(); } @@ -301,13 +306,25 @@ DataSize ScreamV2::max_data_in_flight() const { params_.ref_window_overhead_min.Get() + (params_.ref_window_overhead_max.Get() - params_.ref_window_overhead_min.Get()) * - std::max( - 0.0, - (0.1 - delay_based_congestion_control_.queue_delay_dev_norm()) / - 0.1); + delay_based_congestion_control_ + .ref_window_scale_factor_due_to_avg_min_delay( + /*allow_zero=*/true); + return ref_window_ * ref_window_overhead; } +DataSize ScreamV2::max_allowed_ref_window() const { + // 4.2.2.2. + // Increase ref_window only if bytes in flight is large enough. + // Quite a lot of slack is allowed here to avoid that bitrate locks to low + // values. + return std::max( + params_.max_segment_size.Get() + + std::max(max_data_in_flight_this_rtt_, max_data_in_flight_prev_rtt_) * + params_.bytes_in_flight_head_room.Get(), + params_.min_ref_window.Get()); +} + void ScreamV2::UpdateFeedbackHoldTime(const TransportPacketsFeedback& msg) { const TimeDelta feedback_hold_time = FeedbackHoldTime(msg); if (feedback_hold_time_.IsZero() && @@ -357,6 +374,8 @@ void ScreamV2::UpdateTargetRate(const TransportPacketsFeedback& msg) { drain_queue_start_ = Timestamp::MinusInfinity(); } + // TODO: bugs.webrtc.org/447037083 - Consider implementing 4.4, compensation + // for increased pacer delay. target_rate = std::clamp(target_rate, min_target_bitrate_, max_target_bitrate_); diff --git a/modules/congestion_controller/scream/scream_v2.h b/modules/congestion_controller/scream/scream_v2.h index 1f0d196d093..28eb67276ef 100644 --- a/modules/congestion_controller/scream/scream_v2.h +++ b/modules/congestion_controller/scream/scream_v2.h @@ -37,8 +37,7 @@ class ScreamV2 { explicit ScreamV2(const Environment& env); ~ScreamV2() = default; - void SetTargetBitrateConstraints(DataRate min, DataRate max); - void SetFirstTargetRate(DataRate target_rate) { target_rate_ = target_rate; } + void SetTargetBitrateConstraints(DataRate min, DataRate max, DataRate start); void OnPacketSent(DataSize data_in_flight); void OnTransportPacketsFeedback(const TransportPacketsFeedback& msg); @@ -49,6 +48,7 @@ class ScreamV2 { DataRate pacing_rate() const { return target_rate_ * params_.pacing_factor.Get(); } + TimeDelta rtt() const { return delay_based_congestion_control_.rtt(); } // Max data in flight before the send window is full. @@ -61,9 +61,21 @@ class ScreamV2 { // Last inflection point where ref_window started to decrease. DataSize ref_window_i() const { return ref_window_i_; } + // Returns the maximum allowed reference window based on data in flight during + // the last RTT. + DataSize max_allowed_ref_window() const; + // Returns the average fraction of ECN-CE marked data units per RTT. double l4s_alpha() const { return l4s_alpha_; } + Timestamp last_reference_window_decrease_time() const { + return last_ref_window_decrease_time_; + } + + Timestamp last_reaction_to_congestion_time() const { + return last_reaction_to_congestion_time_; + } + // Exposed for easier logging. const DelayBasedCongestionControl& delay_based_congestion_control() const { return delay_based_congestion_control_; @@ -72,15 +84,13 @@ class ScreamV2 { // Average time feedback is delayed in the receiver. TimeDelta feedback_hold_time() const { return feedback_hold_time_; } - private: - void UpdateL4SAlpha(const TransportPacketsFeedback& msg); - void UpdateRefWindow(const TransportPacketsFeedback& msg); - void UpdateFeedbackHoldTime(const TransportPacketsFeedback& msg); - void UpdateTargetRate(const TransportPacketsFeedback& msg); - // Ratio between `max_segment_size` and `ref_window_`. double ref_window_mss_ratio() const { - return params_.max_segment_size.Get() / ref_window_; + return std::min(1.0, params_.max_segment_size.Get() / ref_window_); + } + + double last_ref_window_increase_scale_factor() const { + return last_ref_window_increase_scale_factor_; } // Scaling factor for reference window adjustment @@ -103,6 +113,12 @@ class ScreamV2 { params_.max_segment_size.Get(); } + private: + void UpdateL4SAlpha(const TransportPacketsFeedback& msg); + void UpdateRefWindow(const TransportPacketsFeedback& msg); + void UpdateFeedbackHoldTime(const TransportPacketsFeedback& msg); + void UpdateTargetRate(const TransportPacketsFeedback& msg); + const Environment env_; const ScreamV2Parameters params_; @@ -121,6 +137,8 @@ class ScreamV2 { // since `ref_window_i_` was last set. bool allow_ref_window_i_update_ = true; + double last_ref_window_increase_scale_factor_ = 1.0; + // `l4s_alpha_` tracks the average fraction of ECN-CE marked data units per // Round-Trip Time. double l4s_alpha_ = 0.0; @@ -138,10 +156,15 @@ class ScreamV2 { // Last received feedback that contained a congestion event that may have // caused a reaction. Timestamp last_reaction_to_congestion_time_ = Timestamp::MinusInfinity(); + // Last time the reference window decreased. This is not the same + // as `last_reaction_to_congestion_time_` since a single CE mark does not + // necessarily cause a reference window decrease. + Timestamp last_ref_window_decrease_time_ = Timestamp::MinusInfinity(); Timestamp drain_queue_start_ = Timestamp::MinusInfinity(); DelayBasedCongestionControl delay_based_congestion_control_; + bool first_feedback_processed_ = false; }; } // namespace webrtc diff --git a/modules/congestion_controller/scream/scream_v2_parameters.cc b/modules/congestion_controller/scream/scream_v2_parameters.cc index aa7ac9ea322..68f6fe393d7 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.cc +++ b/modules/congestion_controller/scream/scream_v2_parameters.cc @@ -18,12 +18,12 @@ namespace webrtc { ScreamV2Parameters::ScreamV2Parameters(const FieldTrialsView& trials) - : min_ref_window("MinRefWindow", DataSize::Bytes(3000)), + : min_ref_window("MinRefWindow", DataSize::Bytes(1000)), l4s_avg_g_up("L4sAvgGUp", 1.0 / 8.0), l4s_avg_g_down("L4sAvgGDown", 1.0 / 128.0), smoothed_rtt_avg_g_up("SmoothedRttAvgGUp", 1.0 / 8.0), - smoothed_l4s_avg_g_down("SmoothedL4sAvgGDown", 1.0 / 128.0), - max_segment_size("MaxSegmentSize", DataSize::Bytes(1000)), + smoothed_rtt_avg_g_down("SmoothedRttAvgGDown", 1.0 / 8.0), + max_segment_size("MaxSegmentSize", DataSize::Bytes(1280)), bytes_in_flight_head_room("BytesInFlightHeadRoom", 1.1), beta_loss("BetaLoss", 0.7), post_congestion_delay_rtts("PostCongestionDelayRtts", 100), @@ -39,28 +39,35 @@ ScreamV2Parameters::ScreamV2Parameters(const FieldTrialsView& trials) "NumberOfRttsBetweenResetRefWindowIOnCongestion", 100), ref_window_overhead_min("RefWinMin", 1.5), - ref_window_overhead_max("RefWinMax", 4.0), + ref_window_overhead_max("RefWinMax", 3.0), queue_delay_avg_g("QDelayAvgG", 1.0 / 4.0), - queue_delay_dev_avg_g("QDelayDevAvgG", 1.0 / 64.0), - + delay_min_and_latency_diff_avg_g("DelayMinAndLatencyDiffAvgG", + 1.0 / 32.0), + queue_delay_min_threshold("QDelayMinThreshold", TimeDelta::Millis(10)), + latency_diff_threshold("LatencyDiffThreshold", TimeDelta::Millis(10)), base_delay_window_length("BaseDelayWindowLength", 10), base_delay_history_update_interval("BaseDelayHistoryUpdateInterval", TimeDelta::Minutes(1)), queue_delay_target("QDelayTarget", TimeDelta::Millis(60)), - queue_delay_increased_threshold("QDelayIncreasedThreshold", 0.25), - queue_delay_threshold("QDelayThreshold", 0.5), queue_delay_drain_threshold("QDelayDrainThreshold", TimeDelta::Millis(5)), queue_delay_drain_period("QDelayDrainPeriod", TimeDelta::Seconds(20)), queue_delay_drain_rtts("QDelayDrainRtts", 5), - periodic_padding_interval("PeriodicPadding", TimeDelta::Seconds(10)), - periodic_padding_duration("PaddingDuration", TimeDelta::Seconds(1)), + time_between_periodic_padding("PeriodicPadding", TimeDelta::Seconds(3)), + periodic_padding_duration("PaddingDuration", TimeDelta::Seconds(3)), + allow_padding_after_last_congestion_time( + "AllowPaddingAfterLastCongestionTimeout", + TimeDelta::Seconds(1)), + initial_probing_duration("InitialProbingDuration", TimeDelta::Seconds(6)), pacing_factor("PacingFactor", 1.1), - feedback_hold_time_avg_g("FeedbackHoldTimeAvgG", 1.0 / 8.0) { + feedback_hold_time_avg_g("FeedbackHoldTimeAvgG", 1.0 / 8.0), + allow_large_pacing_bursts_after_congestion_time( + "AllowLargePacingBurstsAfterCongestionTime", + TimeDelta::Seconds(15)) { ParseFieldTrial({&min_ref_window, &l4s_avg_g_up, &l4s_avg_g_down, &smoothed_rtt_avg_g_up, - &smoothed_l4s_avg_g_down, + &smoothed_rtt_avg_g_down, &max_segment_size, &bytes_in_flight_head_room, &beta_loss, @@ -72,19 +79,22 @@ ScreamV2Parameters::ScreamV2Parameters(const FieldTrialsView& trials) &ref_window_overhead_min, &ref_window_overhead_max, &queue_delay_avg_g, - &queue_delay_dev_avg_g, + &delay_min_and_latency_diff_avg_g, + &queue_delay_min_threshold, + &latency_diff_threshold, &base_delay_window_length, &base_delay_history_update_interval, &queue_delay_target, - &queue_delay_increased_threshold, - &queue_delay_threshold, &queue_delay_drain_threshold, &queue_delay_drain_period, &queue_delay_drain_rtts, - &periodic_padding_interval, + &time_between_periodic_padding, &periodic_padding_duration, + &allow_padding_after_last_congestion_time, + &initial_probing_duration, &pacing_factor, - &feedback_hold_time_avg_g}, + &feedback_hold_time_avg_g, + &allow_large_pacing_bursts_after_congestion_time}, trials.Lookup("WebRTC-Bwe-ScreamV2")); } diff --git a/modules/congestion_controller/scream/scream_v2_parameters.h b/modules/congestion_controller/scream/scream_v2_parameters.h index 85ab4c6f9eb..bd4c1296527 100644 --- a/modules/congestion_controller/scream/scream_v2_parameters.h +++ b/modules/congestion_controller/scream/scream_v2_parameters.h @@ -30,7 +30,7 @@ struct ScreamV2Parameters { // Exponentially Weighted Moving Average (EWMA) factor for smoothed rtt. FieldTrialParameter smoothed_rtt_avg_g_up; - FieldTrialParameter smoothed_l4s_avg_g_down; + FieldTrialParameter smoothed_rtt_avg_g_down; // Maximum Segment Size (MSS) // Size of the largest data segment that a sender is able to transmit. I.e @@ -74,7 +74,16 @@ struct ScreamV2Parameters { // Exponentially Weighted Moving Average (EWMA) factor for updating queue // delay. FieldTrialParameter queue_delay_avg_g; - FieldTrialParameter queue_delay_dev_avg_g; + // Exponentially Weighted Moving Average (EWMA) factor for updating average + // min queue delay and average latency difference. + FieldTrialParameter delay_min_and_latency_diff_avg_g; + + // Thresholds for average min queue delay. Used for limiting ref_window + // increase. + FieldTrialParameter queue_delay_min_threshold; + // Thresholds for average latency difference. Used for limiting ref_window + // increase. + FieldTrialParameter latency_diff_threshold; // Determines the length of the base delay history when estimating one way // delay (owd) @@ -82,19 +91,12 @@ struct ScreamV2Parameters { // Determines how often the base delay history is updated. FieldTrialParameter base_delay_history_update_interval; - // Initial queue delay target. + // Reference window is reduced if average queue delay is above + // `queue_delay_target/2` // TODO: bugs.webrtc.org/447037083 - Consider implementing 4.2.1.4.1. // Competing Flows Compensation. FieldTrialParameter queue_delay_target; - // Queue delay is "detected" if queue delay is higher than - // `queue_delay_target`* `queue_delay_increased_threshold`. - FieldTrialParameter queue_delay_increased_threshold; - - // Reference window should be reduced if average queue delay is above - // `queue_delay_target`* `queue_delay_threshold` - FieldTrialParameter queue_delay_threshold; - // If the minimum queue delay is below this threshold, queues are deamed to be // drained. FieldTrialParameter queue_delay_drain_threshold; @@ -106,11 +108,19 @@ struct ScreamV2Parameters { FieldTrialParameter queue_delay_drain_rtts; // Padding is periodically used in order to increase target rate even if a - // stream does not produce a high enough rate. - FieldTrialParameter periodic_padding_interval; - - // Duration padding is used when periodic padding start. + // stream does not produce a high enough rate. Time between periodic padding + // is at least this long. + FieldTrialParameter time_between_periodic_padding; + // Max duration padding is used when periodic padding start. + // Padding is stopped if congestion occur. FieldTrialParameter periodic_padding_duration; + // Padding is allowed to be used after this duration since the last + // time reference window was reduced but at least `periodic_padding_interval` + // must have passed since last time padding was used. + FieldTrialParameter allow_padding_after_last_congestion_time; + // If initial BWE probing is allowed, allow periodic probing for this duration + // even of no streams have been configured. + FieldTrialParameter initial_probing_duration; // Factor multiplied by the current target rate to decide the pacing rate. FieldTrialParameter pacing_factor; @@ -119,6 +129,12 @@ struct ScreamV2Parameters { // time feedback is delayed by the receiver. I.e the time from a packet is // received until feedback is sent. If zero, this delay is ignored. FieldTrialParameter feedback_hold_time_avg_g; + + // If the time since last reaction to congestion is larger than this, the + // pacing window is increased. I.e. packets are allowed to be sent in larger + // bursts. + FieldTrialParameter + allow_large_pacing_bursts_after_congestion_time; }; } // namespace webrtc diff --git a/modules/congestion_controller/scream/scream_v2_unittest.cc b/modules/congestion_controller/scream/scream_v2_unittest.cc index e485d67deb8..39d89363b7e 100644 --- a/modules/congestion_controller/scream/scream_v2_unittest.cc +++ b/modules/congestion_controller/scream/scream_v2_unittest.cc @@ -61,7 +61,8 @@ TEST(ScreamV2Test, TargetRateIncreaseToMaxOnUnConstrainedNetwork) { Environment env = CreateTestEnvironment({.time = &clock}); ScreamV2 scream(env); const DataRate kMaxDataRate = DataRate::KilobitsPerSec(2000); - scream.SetTargetBitrateConstraints(DataRate::Zero(), kMaxDataRate); + scream.SetTargetBitrateConstraints(DataRate::Zero(), kMaxDataRate, + DataRate::KilobitsPerSec(300)); DataRate send_rate = DataRate::KilobitsPerSec(100); // Configure a feedback generator simulating a network with infinite // capacity but 25ms one way delay. @@ -86,7 +87,8 @@ TEST(ScreamV2Test, Environment env = CreateTestEnvironment({.time = &clock}); ScreamV2 scream(env); const DataRate kMaxDataRate = DataRate::KilobitsPerSec(2000); - scream.SetTargetBitrateConstraints(DataRate::Zero(), kMaxDataRate); + scream.SetTargetBitrateConstraints(DataRate::Zero(), kMaxDataRate, + DataRate::KilobitsPerSec(300)); DataRate send_rate = DataRate::KilobitsPerSec(100); // Configure a feedback generator simulating a network with infinite // capacity but 25ms one way delay. @@ -159,6 +161,43 @@ TEST(ScreamV2Test, ReferenceWindowIncreaseLessPerStepIfCeDetected) { EXPECT_GT(scream_1.ref_window(), scream_2.ref_window()); } +TEST(ScreamV2Test, ReferenceWindowDecreaseIfPacketsAreLostForTheFirstTime) { + SimulatedClock clock(Timestamp::Seconds(1'234)); + Environment env = CreateTestEnvironment({.time = &clock}); + ScreamV2 scream(env); + + TransportPacketsFeedback feedback = + CreateFeedback(clock.CurrentTime(), /*rtt=*/TimeDelta::Millis(10), + /*number_of_ect1_packets=*/20, + /*number_of_packets_in_flight=*/20); + + scream.OnTransportPacketsFeedback(feedback); + DataSize ref_window = scream.ref_window(); + clock.AdvanceTime(TimeDelta::Millis(25)); + + TransportPacketsFeedback loss_feedback = + CreateFeedback(clock.CurrentTime(), /*rtt=*/TimeDelta::Millis(10), + /*number_of_ect1_packets=*/5, + /*number_of_packets_in_flight=*/5); + loss_feedback.packet_feedbacks[3].receive_time = Timestamp::PlusInfinity(); + loss_feedback.packet_feedbacks[3].reported_lost_for_the_first_time = true; + + scream.OnTransportPacketsFeedback(loss_feedback); + EXPECT_LT(scream.ref_window(), ref_window); + ref_window = scream.ref_window(); + + clock.AdvanceTime(TimeDelta::Millis(25)); + TransportPacketsFeedback loss_feedback2 = + CreateFeedback(clock.CurrentTime(), /*rtt=*/TimeDelta::Millis(10), + /*number_of_ect1_packets=*/5, + /*number_of_packets_in_flight=*/5); + loss_feedback2.packet_feedbacks[0].receive_time = Timestamp::PlusInfinity(); + loss_feedback2.packet_feedbacks[0].reported_lost_for_the_first_time = false; + + scream.OnTransportPacketsFeedback(loss_feedback2); + EXPECT_GE(scream.ref_window(), ref_window); +} + TEST(ScreamV2Test, ReferenceWindowIncreaseToDataInflight) { SimulatedClock clock(Timestamp::Seconds(1'234)); Environment env = CreateTestEnvironment({.time = &clock}); @@ -178,9 +217,9 @@ TEST(ScreamV2Test, ReferenceWindowIncreaseToDataInflight) { clock.AdvanceTime(feedback_interval); } // Target rate can increase up to 1.1 * data_in_flight + Max Segment Size( - // default 1000 bytes) when no max target rate has been set. + // default 1280 bytes) when no max target rate has been set. EXPECT_EQ(scream.ref_window(), - 1.1 * feedback.data_in_flight + DataSize::Bytes(1000)); + 1.1 * feedback.data_in_flight + DataSize::Bytes(1280)); } TEST(ScreamV2Test, CalculatesL4sAlpha) { diff --git a/modules/congestion_controller/scream/test/cc_feedback_generator.cc b/modules/congestion_controller/scream/test/cc_feedback_generator.cc index b754a2045b7..1da5c346233 100644 --- a/modules/congestion_controller/scream/test/cc_feedback_generator.cc +++ b/modules/congestion_controller/scream/test/cc_feedback_generator.cc @@ -146,6 +146,7 @@ CcFeedbackGenerator::MaybeSendFeedback(Timestamp time) { PacketResult packet_result; packet_result.sent_packet.send_time = packets_in_flight_.front().send_time(); + packet_result.reported_lost_for_the_first_time = true; packet_result.sent_packet.size = packets_in_flight_.front().packet_size(); packets_in_flight_.pop_front(); feedback.packet_feedbacks.push_back(packet_result); diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index 2d80d0ba781..6b15ad7b80a 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/BUILD.gn @@ -28,6 +28,8 @@ rtc_library("primitives") { "desktop_geometry.h", "desktop_region.cc", "desktop_region.h", + "frame_texture.cc", + "frame_texture.h", "shared_desktop_frame.cc", "shared_desktop_frame.h", "shared_memory.cc", @@ -41,6 +43,7 @@ rtc_library("primitives") { "../../rtc_base:refcount", "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", + "//third_party/abseil-cpp/absl/base:nullability", "//third_party/libyuv", ] } @@ -66,7 +69,6 @@ if (rtc_include_tests) { ":desktop_capture_mock", ":primitives", ":screen_drawer", - "../../api:array_view", "../../rtc_base:base64", "../../rtc_base:threading", "../../test:test_support", @@ -95,6 +97,8 @@ if (rtc_include_tests) { sources = [ "linux/wayland/shared_screencast_stream_unittest.cc", + "linux/wayland/test/test_egl_dmabuf.cc", + "linux/wayland/test/test_egl_dmabuf.h", "linux/wayland/test/test_screencast_stream_provider.cc", "linux/wayland/test/test_screencast_stream_provider.h", ] @@ -109,6 +113,7 @@ if (rtc_include_tests) { "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:random", + "../../rtc_base:stringutils", "../../rtc_base:timeutils", "../portal", @@ -137,6 +142,7 @@ if (rtc_include_tests) { "desktop_and_cursor_composer_unittest.cc", "desktop_capturer_differ_wrapper_unittest.cc", "desktop_frame_rotation_unittest.cc", + "desktop_frame_texture_unittest.cc", "desktop_frame_unittest.cc", "desktop_geometry_unittest.cc", "desktop_region_unittest.cc", @@ -156,7 +162,6 @@ if (rtc_include_tests) { ":desktop_capture", ":desktop_capture_mock", ":primitives", - "../../api:array_view", "../../api/units:time_delta", "../../api/units:timestamp", "../../rtc_base:checks", @@ -282,6 +287,8 @@ rtc_library("desktop_capture") { defines = [] deps = [ "../../media:video_common", + "../../rtc_base/containers:flat_map", + "../../rtc_base/containers:flat_set", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", @@ -586,6 +593,8 @@ rtc_library("desktop_capture") { if (rtc_enable_win_wgc) { sources += [ + "win/dxgi_desktop_frame.cc", + "win/dxgi_desktop_frame.h", "win/wgc_capture_session.cc", "win/wgc_capture_session.h", "win/wgc_capture_source.cc", diff --git a/modules/desktop_capture/DEPS b/modules/desktop_capture/DEPS index b1847ad32ec..74327f736e2 100644 --- a/modules/desktop_capture/DEPS +++ b/modules/desktop_capture/DEPS @@ -5,19 +5,19 @@ include_rules = [ ] specific_include_rules = { - "desktop_frame_cgimage\.h": [ + "desktop_frame_cgimage\\.h": [ "+sdk/objc", ], - "desktop_frame_iosurface\.h": [ + "desktop_frame_iosurface\\.h": [ "+sdk/objc", ], - "desktop_frame_provider\.h": [ + "desktop_frame_provider\\.h": [ "+sdk/objc", ], - "screen_capturer_mac\.mm": [ + "screen_capturer_mac\\.mm": [ "+sdk/objc", ], - "screen_capturer_sck\.mm": [ + "screen_capturer_sck\\.mm": [ "+absl/strings/str_format.h", "+sdk/objc", ], diff --git a/modules/desktop_capture/cropping_window_capturer.cc b/modules/desktop_capture/cropping_window_capturer.cc index b0cbc70a04d..3479044d22a 100644 --- a/modules/desktop_capture/cropping_window_capturer.cc +++ b/modules/desktop_capture/cropping_window_capturer.cc @@ -47,6 +47,9 @@ void CroppingWindowCapturer::SetSharedMemoryFactory( void CroppingWindowCapturer::CaptureFrame() { if (ShouldUseScreenCapturer()) { + // We record the window position here at capture time; it may differ at + // frame delivery time. + last_window_rect_ = GetWindowRectInVirtualScreen(); if (!screen_capturer_) { screen_capturer_ = DesktopCapturer::CreateRawScreenCapturer(options_); if (excluded_window_) { @@ -74,6 +77,7 @@ bool CroppingWindowCapturer::GetSourceList(SourceList* sources) { bool CroppingWindowCapturer::SelectSource(SourceId id) { if (window_capturer_->SelectSource(id)) { selected_window_ = id; + last_window_rect_ = {}; return true; } return false; @@ -98,15 +102,14 @@ void CroppingWindowCapturer::OnCaptureResult( return; } - DesktopRect window_rect = GetWindowRectInVirtualScreen(); - if (window_rect.is_empty()) { + if (last_window_rect_.is_empty()) { RTC_LOG(LS_WARNING) << "Window rect is empty"; callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); return; } std::unique_ptr cropped_frame = - CreateCroppedDesktopFrame(std::move(screen_frame), window_rect); + CreateCroppedDesktopFrame(std::move(screen_frame), last_window_rect_); if (!cropped_frame) { RTC_LOG(LS_WARNING) << "Window is outside of the captured display"; diff --git a/modules/desktop_capture/cropping_window_capturer.h b/modules/desktop_capture/cropping_window_capturer.h index 56478030b14..fe95fc4e6f6 100644 --- a/modules/desktop_capture/cropping_window_capturer.h +++ b/modules/desktop_capture/cropping_window_capturer.h @@ -77,6 +77,11 @@ class RTC_EXPORT CroppingWindowCapturer : public DesktopCapturer, std::unique_ptr screen_capturer_; SourceId selected_window_; WindowId excluded_window_; + + // The window rectangle in the virtual screen, relative to the top-left corner + // of the virtual screen. This is the rectangle used to crop the frame. + // It is updated in CaptureFrame() and used in OnCaptureResult(). + DesktopRect last_window_rect_; }; } // namespace webrtc diff --git a/modules/desktop_capture/desktop_capture_options.h b/modules/desktop_capture/desktop_capture_options.h index bc1342a648a..3ce4678f141 100644 --- a/modules/desktop_capture/desktop_capture_options.h +++ b/modules/desktop_capture/desktop_capture_options.h @@ -227,6 +227,13 @@ class RTC_EXPORT DesktopCaptureOptions { void set_wgc_include_secondary_windows(bool include) { wgc_include_secondary_windows_ = include; } + + // This flag enables native texture of frame with the WGC capturer. + // The flag has no effect if the allow_wgc_capturer flag is false. + bool allow_wgc_using_texture() const { return allow_wgc_using_texture_; } + void set_allow_wgc_using_texture(bool allow) { + allow_wgc_using_texture_ = allow; + } #endif // defined(RTC_ENABLE_WIN_WGC) #endif // defined(WEBRTC_WIN) @@ -285,6 +292,7 @@ class RTC_EXPORT DesktopCaptureOptions { bool allow_wgc_zero_hertz_ = false; bool wgc_require_border_ = false; bool wgc_include_secondary_windows_ = false; + bool allow_wgc_using_texture_ = false; #endif #endif #if defined(WEBRTC_USE_X11) diff --git a/modules/desktop_capture/desktop_frame.cc b/modules/desktop_capture/desktop_frame.cc index 1f0f6047b27..bfba612c829 100644 --- a/modules/desktop_capture/desktop_frame.cc +++ b/modules/desktop_capture/desktop_frame.cc @@ -29,9 +29,11 @@ DesktopFrame::DesktopFrame(DesktopSize size, int stride, FourCC pixel_format, uint8_t* data, - SharedMemory* shared_memory) + SharedMemory* shared_memory, + FrameTexture* texture) : data_(data), shared_memory_(shared_memory), + texture_(texture), size_(size), stride_(stride), pixel_format_(pixel_format), @@ -134,6 +136,7 @@ float DesktopFrame::scale_factor() const { } uint8_t* DesktopFrame::GetFrameDataAtPos(const DesktopVector& pos) const { + RTC_DCHECK(data()); return data() + stride() * pos.y() + DesktopFrame::kBytesPerPixel * pos.x(); } @@ -160,6 +163,7 @@ void DesktopFrame::MoveFrameInfoFrom(DesktopFrame* other) { } bool DesktopFrame::FrameDataIsBlack() const { + RTC_DCHECK(data()); if (size().is_empty()) return false; @@ -172,6 +176,7 @@ bool DesktopFrame::FrameDataIsBlack() const { } void DesktopFrame::SetFrameDataToBlack() { + RTC_DCHECK(data()); const uint8_t kBlackPixelValue = 0x00; memset(data(), kBlackPixelValue, stride() * size().height()); } diff --git a/modules/desktop_capture/desktop_frame.h b/modules/desktop_capture/desktop_frame.h index 104fe63d05d..bda6b7f1f95 100644 --- a/modules/desktop_capture/desktop_frame.h +++ b/modules/desktop_capture/desktop_frame.h @@ -17,9 +17,11 @@ #include #include +#include "absl/base/nullability.h" #include "media/base/video_common.h" #include "modules/desktop_capture/desktop_geometry.h" #include "modules/desktop_capture/desktop_region.h" +#include "modules/desktop_capture/frame_texture.h" #include "modules/desktop_capture/shared_memory.h" #include "rtc_base/system/rtc_export.h" @@ -28,6 +30,8 @@ namespace webrtc { const float kStandardDPI = 96.0f; // DesktopFrame represents a video frame captured from the screen. +// TODO(crbug.com/40929600): Make data-dependent methods virtual so +// texture-backed frames can provide their own implementations. class RTC_EXPORT DesktopFrame { public: // DesktopFrame objects always hold BGRA data. @@ -62,8 +66,11 @@ class RTC_EXPORT DesktopFrame { // The pixel format the `DesktopFrame` is stored in. FourCC pixel_format() const { return pixel_format_; } - // Data buffer used for the frame. - uint8_t* data() const { return data_; } + // Data buffer used for the frame. May be nullptr for texture-backed frames. + uint8_t* absl_nullable data() const { return data_; } + + // Texture used for the frame. + FrameTexture* texture() const { return texture_; } // SharedMemory used for the buffer or NULL if memory is allocated on the // heap. The result is guaranteed to be deleted only after the frame is @@ -172,13 +179,15 @@ class RTC_EXPORT DesktopFrame { int stride, FourCC pixel_format, uint8_t* data, - SharedMemory* shared_memory); + SharedMemory* shared_memory, + FrameTexture* texture = nullptr); // Ownership of the buffers is defined by the classes that inherit from this // class. They must guarantee that the buffer is not deleted before the frame // is deleted. uint8_t* const data_; SharedMemory* const shared_memory_; + FrameTexture* texture_; private: const DesktopSize size_; diff --git a/modules/desktop_capture/desktop_frame_rotation.cc b/modules/desktop_capture/desktop_frame_rotation.cc index a6b4d1619dd..a7ea388a927 100644 --- a/modules/desktop_capture/desktop_frame_rotation.cc +++ b/modules/desktop_capture/desktop_frame_rotation.cc @@ -99,13 +99,13 @@ void RotateDesktopFrame(const DesktopFrame& source, const DesktopVector& target_offset, DesktopFrame* target) { RTC_DCHECK(target); - RTC_DCHECK(DesktopRect::MakeSize(source.size()).ContainsRect(source_rect)); + RTC_CHECK(DesktopRect::MakeSize(source.size()).ContainsRect(source_rect)); // TODO(bugs.webrtc.org/436974448): Support other pixel formats. RTC_CHECK_EQ(FOURCC_ARGB, source.pixel_format()); // The rectangle in `target`. const DesktopRect target_rect = RotateAndOffsetRect(source_rect, source.size(), rotation, target_offset); - RTC_DCHECK(DesktopRect::MakeSize(target->size()).ContainsRect(target_rect)); + RTC_CHECK(DesktopRect::MakeSize(target->size()).ContainsRect(target_rect)); if (target_rect.is_empty()) { return; diff --git a/modules/desktop_capture/desktop_frame_texture_unittest.cc b/modules/desktop_capture/desktop_frame_texture_unittest.cc new file mode 100644 index 00000000000..6e0f7900797 --- /dev/null +++ b/modules/desktop_capture/desktop_frame_texture_unittest.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include "modules/desktop_capture/desktop_frame.h" +#include "modules/desktop_capture/desktop_geometry.h" +#include "modules/desktop_capture/frame_texture.h" +#include "modules/desktop_capture/shared_desktop_frame.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +// Returns a non-null, non-invalid sentinel FrameTexture::Handle for testing. +// On Windows Handle is void*, on other platforms it is int. +FrameTexture::Handle MakeHandle() { + constexpr uintptr_t kValue = 0x1234; +#if defined(WEBRTC_WIN) + return reinterpret_cast(kValue); +#else + return static_cast(kValue); +#endif +} + +// A concrete FrameTexture subclass for testing purposes. Unlike the +// platform-specific DXGIFrameTexture, this doesn't own any real GPU resources. +class FakeFrameTexture : public FrameTexture { + public: + explicit FakeFrameTexture(Handle handle) : FrameTexture(handle) {} + ~FakeFrameTexture() override = default; +}; + +// A texture-backed DesktopFrame with nullptr data, mimicking DXGIDesktopFrame. +class TextureDesktopFrame : public DesktopFrame { + public: + TextureDesktopFrame(DesktopSize size, + std::unique_ptr texture) + : DesktopFrame(size, + /*stride=*/0, + FOURCC_ARGB, + /*data=*/nullptr, + /*shared_memory=*/nullptr, + texture.get()), + owned_texture_(std::move(texture)) {} + + ~TextureDesktopFrame() override = default; + + private: + std::unique_ptr owned_texture_; +}; + +} // namespace + +TEST(FrameTextureTest, StoresHandle) { + const FrameTexture::Handle handle = MakeHandle(); + FakeFrameTexture texture(handle); + EXPECT_EQ(texture.handle(), handle); +} + +TEST(DesktopFrameTest, DefaultTextureIsNull) { + auto frame = std::make_unique(DesktopSize(10, 10)); + EXPECT_EQ(frame->texture(), nullptr); +} + +TEST(DesktopFrameTest, TextureFrameHasValidTexture) { + const FrameTexture::Handle handle = MakeHandle(); + auto texture = std::make_unique(handle); + FrameTexture* raw_texture = texture.get(); + + auto frame = std::make_unique(DesktopSize(10, 10), + std::move(texture)); + EXPECT_EQ(frame->data(), nullptr); + EXPECT_NE(frame->texture(), nullptr); + EXPECT_EQ(frame->texture(), raw_texture); + EXPECT_EQ(frame->texture()->handle(), handle); +} + +TEST(DesktopFrameTest, TextureFrameHasNullData) { + // Verify that a texture-backed frame with nullptr data() still works for + // metadata access (size, texture handle, etc.). + const FrameTexture::Handle handle = MakeHandle(); + auto texture = std::make_unique(handle); + + auto frame = std::make_unique(DesktopSize(10, 10), + std::move(texture)); + EXPECT_EQ(frame->data(), nullptr); + EXPECT_NE(frame->texture(), nullptr); + EXPECT_EQ(frame->texture()->handle(), handle); + EXPECT_EQ(frame->size().width(), 10); + EXPECT_EQ(frame->size().height(), 10); + + // Metadata operations should work on texture-only frames. + frame->set_dpi(DesktopVector(96, 96)); + EXPECT_EQ(frame->dpi().x(), 96); + frame->set_capture_time_ms(123); + EXPECT_EQ(frame->capture_time_ms(), 123); + frame->mutable_updated_region()->SetRect(DesktopRect::MakeWH(10, 10)); + EXPECT_FALSE(frame->updated_region().is_empty()); +} + +TEST(SharedDesktopFrameTest, TexturePropagatedToSharedFrame) { + const FrameTexture::Handle handle = MakeHandle(); + auto texture = std::make_unique(handle); + FrameTexture* raw_texture = texture.get(); + + auto frame = std::make_unique(DesktopSize(10, 10), + std::move(texture)); + auto shared_frame = SharedDesktopFrame::Wrap(std::move(frame)); + ASSERT_NE(shared_frame, nullptr); + EXPECT_EQ(shared_frame->texture(), raw_texture); + EXPECT_EQ(shared_frame->texture()->handle(), handle); +} + +TEST(SharedDesktopFrameTest, TexturePropagatedToClone) { + const FrameTexture::Handle handle = MakeHandle(); + auto texture = std::make_unique(handle); + FrameTexture* raw_texture = texture.get(); + + auto frame = std::make_unique(DesktopSize(10, 10), + std::move(texture)); + auto shared_frame = SharedDesktopFrame::Wrap(std::move(frame)); + auto clone = shared_frame->Share(); + ASSERT_NE(clone, nullptr); + EXPECT_EQ(clone->texture(), raw_texture); + EXPECT_EQ(clone->texture()->handle(), handle); + EXPECT_TRUE(shared_frame->ShareFrameWith(*clone)); +} + +TEST(SharedDesktopFrameTest, NullTextureForNormalFrame) { + auto frame = std::make_unique(DesktopSize(10, 10)); + auto shared_frame = SharedDesktopFrame::Wrap(std::move(frame)); + ASSERT_NE(shared_frame, nullptr); + EXPECT_EQ(shared_frame->texture(), nullptr); +} + +} // namespace webrtc diff --git a/modules/desktop_capture/desktop_frame_unittest.cc b/modules/desktop_capture/desktop_frame_unittest.cc index ea3b2bb14ae..a29cc8eed3e 100644 --- a/modules/desktop_capture/desktop_frame_unittest.cc +++ b/modules/desktop_capture/desktop_frame_unittest.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/desktop_capture/desktop_geometry.h" #include "test/gtest.h" @@ -77,7 +77,7 @@ void RunTest(const TestData& test) { } } -void RunTests(ArrayView tests) { +void RunTests(std::span tests) { for (const TestData& test : tests) { SCOPED_TRACE(test.description); diff --git a/test/fuzzers/fuzz_data_helper.cc b/modules/desktop_capture/frame_texture.cc similarity index 59% rename from test/fuzzers/fuzz_data_helper.cc rename to modules/desktop_capture/frame_texture.cc index 5e8019d46dc..c9934c7a705 100644 --- a/test/fuzzers/fuzz_data_helper.cc +++ b/modules/desktop_capture/frame_texture.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. * * Use of 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,16 +8,11 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "test/fuzzers/fuzz_data_helper.h" - -#include - -#include "api/array_view.h" +#include "modules/desktop_capture/frame_texture.h" namespace webrtc { -namespace test { -FuzzDataHelper::FuzzDataHelper(ArrayView data) : data_(data) {} +FrameTexture::FrameTexture(Handle handle) : handle_(handle) {} +FrameTexture::~FrameTexture() = default; -} // namespace test } // namespace webrtc diff --git a/modules/desktop_capture/frame_texture.h b/modules/desktop_capture/frame_texture.h new file mode 100644 index 00000000000..4051b319e10 --- /dev/null +++ b/modules/desktop_capture/frame_texture.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_FRAME_TEXTURE_H_ +#define MODULES_DESKTOP_CAPTURE_FRAME_TEXTURE_H_ + +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// FrameTexture is a base class for platform-specific texture handles. It stores +// the texture handle. +class RTC_EXPORT FrameTexture { + public: + // Platform-specific handle type for the GPU texture resource. + // On Windows, this is a HANDLE (e.g., DXGI shared resource handle). + // On other platforms, this is an integer file descriptor. +#if defined(WEBRTC_WIN) + typedef void* Handle; + static constexpr Handle kInvalidHandle = nullptr; +#else + typedef int Handle; + static constexpr Handle kInvalidHandle = -1; +#endif + // Platform-specific handle of the texture. + Handle handle() const { return handle_; } + + virtual ~FrameTexture(); + + FrameTexture(const FrameTexture&) = delete; + FrameTexture& operator=(const FrameTexture&) = delete; + + protected: + explicit FrameTexture(Handle handle); + + Handle handle_; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_FRAME_TEXTURE_H_ diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.cc b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc index eced910495d..10bfd88d143 100644 --- a/modules/desktop_capture/linux/wayland/egl_dmabuf.cc +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc @@ -35,6 +35,7 @@ #include #include +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "modules/desktop_capture/desktop_geometry.h" #include "rtc_base/checks.h" @@ -549,7 +550,42 @@ std::vector EglDrmDevice::QueryDmaBufModifiers(uint32_t format) { // Support modifier-less buffers modifiers.push_back(DRM_FORMAT_MOD_INVALID); - return modifiers; + + // Filter out failed modifiers + MutexLock lock(&failed_modifiers_lock_); + auto it = failed_modifiers_.find(format); + if (it == failed_modifiers_.end()) { + return modifiers; + } + + const auto& failed_set = it->second; + + // Special case: if DRM_FORMAT_MOD_INVALID is in the failed set, + // it means all modifiers failed for this format (older PipeWire) + if (failed_set.count(DRM_FORMAT_MOD_INVALID) > 0) { + return {}; + } + + std::vector filtered; + for (uint64_t modifier : modifiers) { + if (failed_set.count(modifier) == 0) { + filtered.push_back(modifier); + } + } + + return filtered; +} + +void EglDrmDevice::MarkModifierFailed(uint32_t format, uint64_t modifier) { + MutexLock lock(&failed_modifiers_lock_); + failed_modifiers_[format].insert(modifier); +} + +void EglDrmDevice::MarkModifierFailed(uint64_t modifier) { + for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, + SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { + MarkModifierFailed(format, modifier); + } } RTC_NO_SANITIZE("cfi-icall") @@ -709,24 +745,33 @@ bool EglDrmDevice::ImageFromDmaBuf(const DesktopSize& size, return !error; } +std::unique_ptr EglDmaBuf::CreateDefault() { + auto instance = absl::WrapUnique(new EglDmaBuf()); + if (!instance->Initialize()) { + RTC_LOG(LS_WARNING) << "EglDmaBuf initialization failed"; + return nullptr; + } + return instance; +} + RTC_NO_SANITIZE("cfi-icall") -EglDmaBuf::EglDmaBuf() { +bool EglDmaBuf::Initialize() { if (!LoadEGL()) { RTC_LOG(LS_ERROR) << "Unable to load EGL entry functions."; CloseLibrary(g_lib_egl); - return; + return false; } if (!LoadGL()) { RTC_LOG(LS_ERROR) << "Failed to load OpenGL entry functions."; CloseLibrary(g_lib_gl); - return; + return false; } std::vector client_extensions = GetClientExtensions(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (client_extensions.empty()) { - return; + return false; } bool has_platform_base_ext = false; @@ -749,11 +794,13 @@ EglDmaBuf::EglDmaBuf() { if (!has_platform_base_ext || !has_platform_gbm_ext || !has_khr_platform_gbm_ext) { RTC_LOG(LS_ERROR) << "One of required EGL extensions is missing"; - return; + return false; } CreatePlatformDevice(); EnumerateDrmDevices(); + + return GetRenderDevice() != nullptr; } // BUG: crbug.com/1290566 @@ -851,6 +898,11 @@ void EglDmaBuf::EnumerateDrmDevices() { } EglDrmDevice* EglDmaBuf::GetRenderDevice() { + if (auto it = devices_.find(preferred_render_device_id_); + it != devices_.end()) { + return it->second.get(); + } + if (default_platform_device_) { return default_platform_device_.get(); } @@ -858,7 +910,44 @@ EglDrmDevice* EglDmaBuf::GetRenderDevice() { if (!devices_.empty()) { return devices_.begin()->second.get(); } + + return nullptr; +} + +EglDrmDevice* EglDmaBuf::GetRenderDevice(dev_t id) { + if (auto it = devices_.find(id); it != devices_.end()) { + return it->second.get(); + } + return nullptr; } +bool EglDmaBuf::SetPreferredRenderDevice(dev_t device_id) { + if (device_id == DEVICE_ID_INVALID) { + RTC_LOG(LS_ERROR) << "Cannot set invalid device ID as render device"; + return false; + } + + auto it = devices_.find(device_id); + if (it == devices_.end()) { + RTC_LOG(LS_ERROR) << "Device ID " << device_id << " not found"; + return false; + } + + preferred_render_device_id_ = device_id; + RTC_LOG(LS_INFO) << "Render device set to device ID: " << major(device_id) + << ":" << minor(device_id); + + return true; +} + +std::vector EglDmaBuf::GetDevices() const { + std::vector device_ids; + device_ids.reserve(devices_.size()); + for (const auto& [device_id, device] : devices_) { + device_ids.push_back(device_id); + } + return device_ids; +} + } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.h b/modules/desktop_capture/linux/wayland/egl_dmabuf.h index ee32f96b52c..54068f92ec5 100644 --- a/modules/desktop_capture/linux/wayland/egl_dmabuf.h +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.h @@ -24,6 +24,10 @@ #include #include "modules/desktop_capture/desktop_geometry.h" +#include "rtc_base/containers/flat_map.h" +#include "rtc_base/containers/flat_set.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -45,23 +49,27 @@ class EglDrmDevice { EglDrmDevice(EGLDisplay display, dev_t device_id = DEVICE_ID_INVALID); EglDrmDevice(std::string render_node, dev_t device_id = DEVICE_ID_INVALID); - - ~EglDrmDevice(); + virtual ~EglDrmDevice(); bool EnsureInitialized(); bool IsInitialized() const { return initialized_; } dev_t GetDeviceId() const { return device_id_; } - bool ImageFromDmaBuf(const DesktopSize& size, - uint32_t format, - const std::vector& plane_datas, - uint64_t modifiers, - const DesktopVector& offset, - const DesktopSize& buffer_size, - uint8_t* data); - std::vector QueryDmaBufModifiers(uint32_t format); + virtual bool ImageFromDmaBuf(const DesktopSize& size, + uint32_t format, + const std::vector& plane_datas, + uint64_t modifiers, + const DesktopVector& offset, + const DesktopSize& buffer_size, + uint8_t* data); + virtual std::vector QueryDmaBufModifiers(uint32_t format); + + void MarkModifierFailed(uint32_t format, uint64_t modifier); + void MarkModifierFailed(uint64_t modifier); private: + friend class TestEglDrmDevice; + EGLStruct egl_; bool initialized_ = false; bool has_image_dma_buf_import_ext_ = false; @@ -80,12 +88,24 @@ class EglDrmDevice { GLuint fbo_ = 0; GLuint texture_ = 0; + + // Map of format -> failed modifiers that didn't work during import + // The lock is needed for concurrent read/write in case a frame import + // fails, we started to negotiate a new format, but meanwhile can still + // receive a new frame and fail again, leading to again marking modifier + // as failed. + Mutex failed_modifiers_lock_; + flat_map> failed_modifiers_ + RTC_GUARDED_BY(failed_modifiers_lock_); }; +// Base class for EGL DMA-BUF implementations. +// Provides shared device management logic for both real and test +// implementations. class EglDmaBuf { public: - EglDmaBuf(); - ~EglDmaBuf() = default; + static std::unique_ptr CreateDefault(); + virtual ~EglDmaBuf() = default; // Returns the DRM device to use for querying DMA-BUF modifiers and importing // frames. Device selection follows this priority order: @@ -96,13 +116,29 @@ class EglDmaBuf { // uses EGL device enumeration to discover available DRM devices // 3. nullptr - if no devices are available EglDrmDevice* GetRenderDevice(); + // Returns the DRM device given the id or nullptr in case the device is not + // found + EglDrmDevice* GetRenderDevice(dev_t id); + std::vector GetDevices() const; + + bool SetPreferredRenderDevice(dev_t device_id); + + protected: + EglDmaBuf() = default; + + // Initializes EGL/DRM devices. + // Returns true if at least one device is available, false otherwise. + virtual bool Initialize(); private: + friend class TestEglDmaBuf; + bool CreatePlatformDevice(); void EnumerateDrmDevices(); std::map> devices_; std::unique_ptr default_platform_device_; + dev_t preferred_render_device_id_ = DEVICE_ID_INVALID; }; } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/restore_token_manager.cc b/modules/desktop_capture/linux/wayland/restore_token_manager.cc index ff843c20473..101a5af4b39 100644 --- a/modules/desktop_capture/linux/wayland/restore_token_manager.cc +++ b/modules/desktop_capture/linux/wayland/restore_token_manager.cc @@ -13,6 +13,7 @@ #include #include "modules/desktop_capture/desktop_capturer.h" +#include "rtc_base/synchronization/mutex.h" namespace webrtc { @@ -24,15 +25,18 @@ RestoreTokenManager& RestoreTokenManager::GetInstance() { void RestoreTokenManager::AddToken(DesktopCapturer::SourceId id, const std::string& token) { + MutexLock lock(&mutex_); restore_tokens_.insert({id, token}); } std::string RestoreTokenManager::GetToken(DesktopCapturer::SourceId id) { + MutexLock lock(&mutex_); const std::string token = restore_tokens_[id]; return token; } DesktopCapturer::SourceId RestoreTokenManager::GetUnusedId() { + MutexLock lock(&mutex_); return ++last_source_id_; } diff --git a/modules/desktop_capture/linux/wayland/restore_token_manager.h b/modules/desktop_capture/linux/wayland/restore_token_manager.h index 06d30715651..120a4b2fe84 100644 --- a/modules/desktop_capture/linux/wayland/restore_token_manager.h +++ b/modules/desktop_capture/linux/wayland/restore_token_manager.h @@ -15,6 +15,8 @@ #include #include "modules/desktop_capture/desktop_capturer.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -35,9 +37,11 @@ class RestoreTokenManager { RestoreTokenManager() = default; ~RestoreTokenManager() = default; - DesktopCapturer::SourceId last_source_id_ = 0; + Mutex mutex_; + DesktopCapturer::SourceId last_source_id_ RTC_GUARDED_BY(mutex_) = 0; - std::unordered_map restore_tokens_; + std::unordered_map restore_tokens_ + RTC_GUARDED_BY(mutex_); }; } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc index 14f5e6f349c..a590cacbe63 100644 --- a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc +++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,9 +26,7 @@ #include #include -#include "absl/strings/string_view.h" -#include "rtc_base/string_encode.h" -#include "rtc_base/string_to_number.h" +#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h" #if !PW_CHECK_VERSION(0, 3, 29) #define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) @@ -38,97 +37,27 @@ namespace webrtc { -PipeWireVersion PipeWireVersion::Parse(const absl::string_view& version) { - std::vector parsed_version = split(version, '.'); +constexpr uint32_t kSupportedPixelFormats[] = { + SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx, + SPA_VIDEO_FORMAT_RGBx}; - if (parsed_version.size() != 3) { - return {}; - } - - std::optional major = StringToNumber(parsed_version.at(0)); - std::optional minor = StringToNumber(parsed_version.at(1)); - std::optional micro = StringToNumber(parsed_version.at(2)); - - // Return invalid version if we failed to parse it - if (!major || !minor || !micro) { - return {}; - } - - return {.major = major.value(), - .minor = minor.value(), - .micro = micro.value(), - .full_version = std::string(version)}; -} - -bool PipeWireVersion::operator>=(const PipeWireVersion& other) { - if (!major && !minor && !micro) { - return false; - } - - return std::tie(major, minor, micro) >= - std::tie(other.major, other.minor, other.micro); -} - -bool PipeWireVersion::operator<=(const PipeWireVersion& other) { - if (!major && !minor && !micro) { - return false; - } - - return std::tie(major, minor, micro) <= - std::tie(other.major, other.minor, other.micro); -} - -absl::string_view PipeWireVersion::ToStringView() const { - return full_version; -} - -spa_pod* BuildFormat(spa_pod_builder* builder, - uint32_t format, - const std::vector& modifiers, - const struct spa_rectangle* resolution, - const struct spa_fraction* frame_rate) { - spa_pod_frame frames[2]; - spa_rectangle pw_min_screen_bounds = spa_rectangle{.width = 1, .height = 1}; - spa_rectangle pw_max_screen_bounds = - spa_rectangle{.width = UINT32_MAX, .height = UINT32_MAX}; - spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, - SPA_PARAM_EnumFormat); +void BuildBaseFormatParams(spa_pod_builder* builder, + uint32_t format, + const struct spa_rectangle* resolution, + const struct spa_fraction* frame_rate) { spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); - if (!modifiers.empty()) { - if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { - spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY); - spa_pod_builder_long(builder, modifiers[0]); - } else { - spa_pod_builder_prop( - builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); - spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); - - // modifiers from the array - bool first = true; - for (int64_t val : modifiers) { - spa_pod_builder_long(builder, val); - // Add the first modifier twice as the very first value is the default - // option - if (first) { - spa_pod_builder_long(builder, val); - first = false; - } - } - spa_pod_builder_pop(builder, &frames[1]); - } - } - if (resolution) { spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(resolution), 0); } else { + spa_rectangle pw_min_screen_bounds = spa_rectangle{.width = 1, .height = 1}; + spa_rectangle pw_max_screen_bounds = + spa_rectangle{.width = UINT32_MAX, .height = UINT32_MAX}; spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, &pw_min_screen_bounds, @@ -147,7 +76,74 @@ spa_pod* BuildFormat(spa_pod_builder* builder, frame_rate, &pw_min_frame_rate, frame_rate), 0); } - return static_cast(spa_pod_builder_pop(builder, &frames[0])); +} + +void BuildBaseFormat(spa_pod_builder* builder, + const struct spa_rectangle* resolution, + const struct spa_fraction* frame_rate, + std::vector& params) { + for (uint32_t format : kSupportedPixelFormats) { + struct spa_pod_frame frame; + spa_pod_builder_push_object(builder, &frame, SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat); + BuildBaseFormatParams(builder, format, resolution, frame_rate); + params.push_back( + static_cast(spa_pod_builder_pop(builder, &frame))); + } +} + +void BuildFullFormat(spa_pod_builder* builder, + EglDrmDevice* render_device, + const struct spa_rectangle* resolution, + const struct spa_fraction* frame_rate, + std::vector& params) { + for (uint32_t format : kSupportedPixelFormats) { + bool need_fallback_format = false; + struct spa_pod_frame frame; + spa_pod_builder_push_object(builder, &frame, SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat); + BuildBaseFormatParams(builder, format, resolution, frame_rate); + if (render_device) { + auto modifiers = render_device->QueryDmaBufModifiers(format); + + if (modifiers.size()) { + if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(builder, modifiers[0]); + } else { + struct spa_pod_frame modifier_frame; + spa_pod_builder_prop( + builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + spa_pod_builder_push_choice(builder, &modifier_frame, SPA_CHOICE_Enum, + 0); + + // Add the first modifier twice as the very first value is the + // default option + spa_pod_builder_long(builder, modifiers[0]); + // Add modifiers from the array + for (int64_t val : modifiers) { + spa_pod_builder_long(builder, val); + } + spa_pod_builder_pop(builder, &modifier_frame); + } + + need_fallback_format = true; + } + } + + params.push_back( + static_cast(spa_pod_builder_pop(builder, &frame))); + + if (need_fallback_format) { + spa_pod_builder_push_object(builder, &frame, SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat); + BuildBaseFormatParams(builder, format, resolution, frame_rate); + params.push_back( + static_cast(spa_pod_builder_pop(builder, &frame))); + } + } } } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h index 66b9759f263..4e3492e859e 100644 --- a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h +++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h @@ -15,8 +15,6 @@ #include -#include "absl/strings/string_view.h" - struct spa_pod; struct spa_pod_builder; struct spa_rectangle; @@ -24,31 +22,41 @@ struct spa_fraction; namespace webrtc { -struct PipeWireVersion { - static PipeWireVersion Parse(const absl::string_view& version); - - // Returns whether current version is newer or same as required version - bool operator>=(const PipeWireVersion& other); - // Returns whether current version is older or same as required version - bool operator<=(const PipeWireVersion& other); - - absl::string_view ToStringView() const; - - int major = 0; - int minor = 0; - int micro = 0; - std::string full_version; -}; - -// Returns a spa_pod used to build PipeWire stream format using given -// arguments. Modifiers are optional value and when present they will be -// used with SPA_POD_PROP_FLAG_MANDATORY and SPA_POD_PROP_FLAG_DONT_FIXATE -// flags. -spa_pod* BuildFormat(spa_pod_builder* builder, - uint32_t format, - const std::vector& modifiers, +class EglDrmDevice; + +// Builds base video format parameters. The format parameter consists of: +// - SPA_FORMAT_mediaType with SPA_MEDIA_TYPE_video +// - SPA_FORMAT_mediaSubtype with SPA_MEDIA_SUBTYPE_raw +// - SPA_FORMAT_VIDEO_format with the specified format +// - SPA_FORMAT_VIDEO_size and SPA_FORMAT_VIDEO_framerate based on the +// provided resolution and frame_rate arguments (if non-null) +void BuildBaseFormatParams(spa_pod_builder* builder, + uint32_t format, + const struct spa_rectangle* resolution, + const struct spa_fraction* frame_rate); + +// Builds minimum video format parameters for all supported pixel formats: +// - SPA_VIDEO_FORMAT_BGRA +// - SPA_VIDEO_FORMAT_RGBA +// - SPA_VIDEO_FORMAT_BGRx +// - SPA_VIDEO_FORMAT_RGBx +// Each format is added as a separate parameter to the params vector. +void BuildBaseFormat(spa_pod_builder* builder, + const struct spa_rectangle* resolution, + const struct spa_fraction* frame_rate, + std::vector& params); + +// Builds full video format parameters. Full video format consists of all the +// base parameters (media type, subtype, format, size, framerate), and also +// adds DMA-BUF modifiers from the provided render device. Modifiers are used +// with SPA_POD_PROP_FLAG_MANDATORY and SPA_POD_PROP_FLAG_DONT_FIXATE flags. +// A fallback format (without modifiers) is also provided in case the producer +// doesn't support DMA-BUFs. +void BuildFullFormat(spa_pod_builder* builder, + EglDrmDevice* render_device, const struct spa_rectangle* resolution, - const struct spa_fraction* frame_rate); + const struct spa_fraction* frame_rate, + std::vector& params); } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc index f0c87d98ea0..1aa8be5505a 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc @@ -81,6 +81,7 @@ constexpr PipeWireVersion kDropSingleModifierMinVersion = {.major = 0, class SharedScreenCastStreamPrivate { public: SharedScreenCastStreamPrivate(); + explicit SharedScreenCastStreamPrivate(std::unique_ptr egl_dmabuf); ~SharedScreenCastStreamPrivate(); bool StartScreenCastStream(uint32_t stream_node_id, @@ -97,6 +98,10 @@ class SharedScreenCastStreamPrivate { void SetObserver(SharedScreenCastStream::Observer* observer) { observer_ = observer; } + void SetSharedMemoryFactory( + std::unique_ptr shared_memory_factory) { + shared_memory_factory_ = std::move(shared_memory_factory); + } void StopScreenCastStream(); std::unique_ptr CaptureFrame(); std::unique_ptr CaptureCursor(); @@ -123,13 +128,13 @@ class SharedScreenCastStreamPrivate { Mutex latest_frame_lock_ RTC_ACQUIRED_AFTER(queue_lock_); SharedDesktopFrame* latest_available_frame_ RTC_GUARDED_BY(&latest_frame_lock_) = nullptr; - std::unique_ptr mouse_cursor_; - DesktopVector mouse_cursor_position_ = DesktopVector(-1, -1); + std::unique_ptr mouse_cursor_ RTC_GUARDED_BY(&latest_frame_lock_); + DesktopVector mouse_cursor_position_ RTC_GUARDED_BY(&latest_frame_lock_) = + DesktopVector(-1, -1); int64_t modifier_; std::unique_ptr egl_dmabuf_; - // List of modifiers we query as supported by the graphics card/driver - std::vector modifiers_; + std::unique_ptr shared_memory_factory_; // PipeWire types std::unique_ptr pw_initializer_; @@ -315,6 +320,15 @@ void SharedScreenCastStreamPrivate::OnStreamParamChanged( that->stream_size_ = DesktopSize(that->spa_video_format_.size.width, that->spa_video_format_.size.height); + if (that->observer_) { + that->observer_->OnFormatChanged( + that->spa_video_format_.format, that->spa_video_format_.size.width, + that->spa_video_format_.size.height, + that->spa_video_format_.framerate.num / + that->spa_video_format_.framerate.denom, + that->modifier_); + } + if (RTC_LOG_CHECK_LEVEL(LS_INFO)) { StringBuilder sb; sb << "PipeWire stream format changed:\n"; @@ -417,7 +431,6 @@ void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) { PipeWireThreadLoopLock thread_loop_lock(that->pw_main_loop_); uint8_t buffer[4096] = {}; - spa_pod_builder builder = spa_pod_builder{.data = buffer, .size = sizeof(buffer)}; @@ -425,19 +438,9 @@ void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) { struct spa_rectangle resolution = SPA_RECTANGLE(that->width_, that->height_); struct spa_fraction frame_rate = SPA_FRACTION(that->frame_rate_, 1); - - for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, - SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { - if (!that->modifiers_.empty()) { - params.push_back( - BuildFormat(&builder, format, that->modifiers_, - that->width_ && that->height_ ? &resolution : nullptr, - &frame_rate)); - } - params.push_back(BuildFormat( - &builder, format, /*modifiers=*/{}, - that->width_ && that->height_ ? &resolution : nullptr, &frame_rate)); - } + BuildFullFormat(&builder, that->egl_dmabuf_->GetRenderDevice(), + that->width_ && that->height_ ? &resolution : nullptr, + &frame_rate, params); pw_stream_update_params(that->pw_stream_, params.data(), params.size()); } @@ -445,6 +448,10 @@ void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) { SharedScreenCastStreamPrivate::SharedScreenCastStreamPrivate() {} +SharedScreenCastStreamPrivate::SharedScreenCastStreamPrivate( + std::unique_ptr egl_dmabuf) + : egl_dmabuf_(std::move(egl_dmabuf)) {} + SharedScreenCastStreamPrivate::~SharedScreenCastStreamPrivate() { StopAndCleanupStream(); } @@ -465,7 +472,9 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( RTC_LOG(LS_ERROR) << "Unable to open PipeWire library"; return false; } - egl_dmabuf_ = std::make_unique(); + if (!egl_dmabuf_) { + egl_dmabuf_ = EglDmaBuf::CreateDefault(); + } pw_stream_node_id_ = stream_node_id; @@ -537,42 +546,35 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_, this); - uint8_t buffer[4096] = {}; - - spa_pod_builder builder = - spa_pod_builder{.data = buffer, .size = sizeof(buffer)}; - std::vector params; - const bool has_required_pw_client_version = - pw_client_version_ >= kDmaBufModifierMinVersion; - const bool has_required_pw_server_version = + // Modifiers can be used with PipeWire >= 0.3.33 + const bool has_dmabuf_support = + egl_dmabuf_ && pw_client_version_ >= kDmaBufModifierMinVersion && pw_server_version_ >= kDmaBufModifierMinVersion; + + struct spa_fraction default_frame_rate = SPA_FRACTION(frame_rate_, 1); struct spa_rectangle resolution; bool set_resolution = false; if (width && height) { resolution = SPA_RECTANGLE(width, height); set_resolution = true; } - struct spa_fraction default_frame_rate = SPA_FRACTION(frame_rate_, 1); - for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, - SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { - // Modifiers can be used with PipeWire >= 0.3.33 - if (has_required_pw_client_version && has_required_pw_server_version) { - auto render_device = egl_dmabuf_->GetRenderDevice(); - if (render_device) { - modifiers_ = render_device->QueryDmaBufModifiers(format); - } - if (!modifiers_.empty()) { - params.push_back(BuildFormat(&builder, format, modifiers_, - set_resolution ? &resolution : nullptr, - &default_frame_rate)); - } - } + uint8_t buffer[4096] = {}; + spa_pod_builder builder = + spa_pod_builder{.data = buffer, .size = sizeof(buffer)}; + + RTC_LOG(LS_INFO) << "DMABufs are " + << (has_dmabuf_support ? "supported" : "not supported"); - params.push_back(BuildFormat(&builder, format, /*modifiers=*/{}, - set_resolution ? &resolution : nullptr, - &default_frame_rate)); + std::vector params; + if (has_dmabuf_support) { + BuildFullFormat(&builder, egl_dmabuf_->GetRenderDevice(), + set_resolution ? &resolution : nullptr, + &default_frame_rate, params); + } else { + BuildBaseFormat(&builder, set_resolution ? &resolution : nullptr, + &default_frame_rate, params); } if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, pw_stream_node_id_, @@ -652,14 +654,14 @@ void SharedScreenCastStreamPrivate::StopAndCleanupStream() { pw_stream_destroy(pw_stream_); pw_stream_ = nullptr; - { - MutexLock lock(&queue_lock_); - queue_.Reset(); - } { MutexLock latest_frame_lock(&latest_frame_lock_); latest_available_frame_ = nullptr; } + { + MutexLock lock(&queue_lock_); + queue_.Reset(); + } } if (pw_core_) { @@ -680,7 +682,7 @@ std::unique_ptr SharedScreenCastStreamPrivate::CaptureFrame() { MutexLock latest_frame_lock(&latest_frame_lock_); - if (!pw_stream_ || !latest_available_frame_) { + if (!latest_available_frame_) { return std::unique_ptr{}; } @@ -694,6 +696,7 @@ SharedScreenCastStreamPrivate::CaptureFrame() { } std::unique_ptr SharedScreenCastStreamPrivate::CaptureCursor() { + MutexLock latest_frame_lock(&latest_frame_lock_); if (!mouse_cursor_) { return nullptr; } @@ -702,6 +705,7 @@ std::unique_ptr SharedScreenCastStreamPrivate::CaptureCursor() { } DesktopVector SharedScreenCastStreamPrivate::CaptureCursorPosition() { + MutexLock latest_frame_lock(&latest_frame_lock_); return mouse_cursor_position_; } @@ -773,20 +777,28 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { mouse_frame->CopyPixelsFrom( bitmap_data, bitmap->stride, DesktopRect::MakeWH(bitmap->size.width, bitmap->size.height)); - mouse_cursor_ = std::make_unique( - mouse_frame, DesktopVector(cursor->hotspot.x, cursor->hotspot.y)); + { + MutexLock latest_frame_lock(&latest_frame_lock_); + mouse_cursor_ = std::make_unique( + mouse_frame, + DesktopVector(cursor->hotspot.x, cursor->hotspot.y)); + } if (observer_) { observer_->OnCursorShapeChanged(); } } - mouse_cursor_position_.set(cursor->position.x, cursor->position.y); + { + MutexLock latest_frame_lock(&latest_frame_lock_); + mouse_cursor_position_.set(cursor->position.x, cursor->position.y); + } if (observer_) { observer_->OnCursorPositionChanged(); } } else { // Indicate an invalid cursor + MutexLock latest_frame_lock(&latest_frame_lock_); mouse_cursor_position_.set(-1, -1); } } @@ -895,8 +907,15 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { if (!queue_.current_frame() || !queue_.current_frame()->size().equals(frame_size_)) { - std::unique_ptr frame(new BasicDesktopFrame( - DesktopSize(frame_size_.width(), frame_size_.height()), FOURCC_ARGB)); + std::unique_ptr frame; + if (shared_memory_factory_) { + frame = SharedMemoryDesktopFrame::Create( + DesktopSize(frame_size_.width(), frame_size_.height()), FOURCC_ARGB, + shared_memory_factory_.get()); + } else { + frame = std::make_unique( + DesktopSize(frame_size_.width(), frame_size_.height()), FOURCC_ARGB); + } queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame))); } @@ -1033,13 +1052,20 @@ bool SharedScreenCastStreamPrivate::ProcessDMABuffer( stream_size_, spa_video_format_.format, plane_datas, modifier_, offset, frame.size(), frame.data()); if (!imported) { - RTC_LOG(LS_ERROR) << "Dropping DMA-BUF modifier: " << modifier_ - << " and trying to renegotiate stream parameters"; + RTC_LOG(LS_ERROR) + << "DMA-BUF modifier " << modifier_ << " failed for format " + << spa_video_format_.format << " (" + << spa_debug_type_find_name(spa_type_video_format, + spa_video_format_.format) + << "), marking as failed and renegotiating stream parameters"; if (pw_server_version_ >= kDropSingleModifierMinVersion) { - std::erase(modifiers_, modifier_); + render_device->MarkModifierFailed(spa_video_format_.format, modifier_); } else { - modifiers_.clear(); + // For older PipeWire versions, mark all modifiers as failed for this + // format + render_device->MarkModifierFailed(spa_video_format_.format, + DRM_FORMAT_MOD_INVALID); } pw_loop_signal_event(pw_thread_loop_get_loop(pw_main_loop_), renegotiate_); @@ -1062,14 +1088,26 @@ void SharedScreenCastStreamPrivate::ConvertRGBxToBGRx(uint8_t* frame, SharedScreenCastStream::SharedScreenCastStream() : private_(std::make_unique()) {} +SharedScreenCastStream::SharedScreenCastStream( + std::unique_ptr egl_dmabuf) + : private_(std::make_unique( + std::move(egl_dmabuf))) {} + SharedScreenCastStream::~SharedScreenCastStream() {} -webrtc::scoped_refptr -SharedScreenCastStream::CreateDefault() { +scoped_refptr SharedScreenCastStream::CreateDefault() { // Explicit new, to access non-public constructor. return scoped_refptr(new SharedScreenCastStream()); } +scoped_refptr +SharedScreenCastStream::CreateWithEglDmaBuf( + std::unique_ptr egl_dmabuf) { + // Explicit new, to access non-public constructor. + return scoped_refptr( + new SharedScreenCastStream(std::move(egl_dmabuf))); +} + bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id) { return private_->StartScreenCastStream(stream_node_id, kInvalidPipeWireFd); } @@ -1104,6 +1142,11 @@ void SharedScreenCastStream::SetObserver( private_->SetObserver(observer); } +void SharedScreenCastStream::SetSharedMemoryFactory( + std::unique_ptr shared_memory_factory) { + private_->SetSharedMemoryFactory(std::move(shared_memory_factory)); +} + void SharedScreenCastStream::StopScreenCastStream() { private_->StopScreenCastStream(); } diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.h b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h index 6b827115c43..48d5004bb1a 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.h +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h @@ -26,6 +26,7 @@ namespace webrtc { class SharedScreenCastStreamPrivate; +class EglDmaBuf; class RTC_EXPORT SharedScreenCastStream : public RefCountedNonVirtual { @@ -40,7 +41,14 @@ class RTC_EXPORT SharedScreenCastStream virtual void OnBufferCorruptedData() = 0; virtual void OnEmptyBuffer() = 0; virtual void OnStreamConfigured() = 0; - virtual void OnFrameRateChanged(uint32_t frame_rate) = 0; + // TODO: https://crbug.com/474141343 - Adapt Chrome Remote Desktop to + // SharedScreencastStream::Observer API changes + virtual void OnFrameRateChanged(uint32_t frame_rate) {} + virtual void OnFormatChanged(uint32_t format, + uint32_t width, + uint32_t height, + uint32_t frame_rate, + uint64_t modifier) {} protected: Observer() = default; @@ -48,6 +56,8 @@ class RTC_EXPORT SharedScreenCastStream }; static scoped_refptr CreateDefault(); + static scoped_refptr CreateWithEglDmaBuf( + std::unique_ptr egl_dmabuf); bool StartScreenCastStream(uint32_t stream_node_id); bool StartScreenCastStream(uint32_t stream_node_id, @@ -60,6 +70,8 @@ class RTC_EXPORT SharedScreenCastStream void UpdateScreenCastStreamFrameRate(uint32_t frame_rate); void SetUseDamageRegion(bool use_damage_region); void SetObserver(SharedScreenCastStream::Observer* observer); + void SetSharedMemoryFactory( + std::unique_ptr shared_memory_factory); void StopScreenCastStream(); // Below functions return the most recent information we get from a @@ -88,6 +100,7 @@ class RTC_EXPORT SharedScreenCastStream protected: SharedScreenCastStream(); + explicit SharedScreenCastStream(std::unique_ptr egl_dmabuf); private: friend class SharedScreenCastStreamPrivate; diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream_unittest.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream_unittest.cc index d9456c8924d..d51305be435 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream_unittest.cc +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream_unittest.cc @@ -10,6 +10,7 @@ #include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" +#include #include #include @@ -17,6 +18,7 @@ #include "api/scoped_refptr.h" #include "api/units/time_delta.h" +#include "modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.h" #include "modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.h" #include "modules/desktop_capture/rgba_color.h" #include "modules/desktop_capture/shared_desktop_frame.h" @@ -72,10 +74,16 @@ class MAYBE_PipeWireStreamTest : public ::testing::Test, MOCK_METHOD(void, OnBufferCorruptedData, (), (override)); MOCK_METHOD(void, OnEmptyBuffer, (), (override)); MOCK_METHOD(void, OnStreamConfigured, (), (override)); - MOCK_METHOD(void, OnFrameRateChanged, (uint32_t), (override)); + MOCK_METHOD(void, + OnFormatChanged, + (uint32_t, uint32_t, uint32_t, uint32_t, uint64_t), + (override)); void SetUp() override { - shared_screencast_stream_ = SharedScreenCastStream::CreateDefault(); + auto shared_screencast_egl_dmabuf = TestEglDmaBuf::CreateDefault(); + shared_screencast_egl_dmabuf_ = shared_screencast_egl_dmabuf.get(); + shared_screencast_stream_ = SharedScreenCastStream::CreateWithEglDmaBuf( + std::move(shared_screencast_egl_dmabuf)); shared_screencast_stream_->SetObserver(this); test_screencast_stream_provider_ = std::make_unique(this, kWidth, kHeight); @@ -93,6 +101,7 @@ class MAYBE_PipeWireStreamTest : public ::testing::Test, protected: uint recorded_frames_ = 0; bool streaming_ = false; + TestEglDmaBuf* shared_screencast_egl_dmabuf_ = nullptr; std::unique_ptr test_screencast_stream_provider_; scoped_refptr shared_screencast_stream_; @@ -114,7 +123,13 @@ TEST_F(MAYBE_PipeWireStreamTest, TestPipeWire) { EXPECT_CALL(*this, OnStartStreaming).WillOnce([&waitStartStreamingEvent] { waitStartStreamingEvent.Set(); }); - EXPECT_CALL(*this, OnFrameRateChanged(60)).Times(1); // Default frame rate. + // Default format is using BGRA pixel format, 800x600 resolution with 60fps + // framerate and defaulting to DRM_FORMAT_MOD_LINEAR modifier. + // This is called twice, because first format changed is not fixated on a + // particular modifier from the producer side. + EXPECT_CALL(*this, OnFormatChanged(SPA_VIDEO_FORMAT_BGRA, 800, 640, 60, + DRM_FORMAT_MOD_LINEAR)) + .Times(2); // Default frame rate. // Give it some time to connect, the order between these shouldn't matter, but // we need to be sure we are connected before we proceed to work with frames. @@ -124,7 +139,7 @@ TEST_F(MAYBE_PipeWireStreamTest, TestPipeWire) { waitStartStreamingEvent.Wait(kShortWait); Event frameRetrievedEvent; - EXPECT_CALL(*this, OnFrameRecorded).Times(8); + EXPECT_CALL(*this, OnFrameRecorded).Times(7); EXPECT_CALL(*this, OnDesktopFrameChanged) .Times(3) .WillRepeatedly([&frameRetrievedEvent] { frameRetrievedEvent.Set(); }); @@ -197,17 +212,9 @@ TEST_F(MAYBE_PipeWireStreamTest, TestPipeWire) { blue_color, TestScreenCastStreamProvider::CorruptedData); corruptedDataFrameEvent.Wait(kShortWait); - Event emptyFrameEvent; - EXPECT_CALL(*this, OnEmptyBuffer).WillOnce([&emptyFrameEvent] { - emptyFrameEvent.Set(); - }); - - test_screencast_stream_provider_->RecordFrame( - blue_color, TestScreenCastStreamProvider::EmptyData); - emptyFrameEvent.Wait(kShortWait); - // Update stream parameters. - EXPECT_CALL(*this, OnFrameRateChanged(0)) + EXPECT_CALL(*this, OnFormatChanged(SPA_VIDEO_FORMAT_BGRA, 800, 640, 0, + DRM_FORMAT_MOD_LINEAR)) .Times(1) .WillOnce([&waitStreamParamChangedEvent1] { waitStreamParamChangedEvent1.Set(); @@ -222,15 +229,16 @@ TEST_F(MAYBE_PipeWireStreamTest, TestPipeWire) { waitStartStreamingEvent2.Set(); }); Event emptyFrameEvent2; - EXPECT_CALL(*this, OnEmptyBuffer).WillOnce([&emptyFrameEvent2] { + EXPECT_CALL(*this, OnBufferCorruptedData).WillOnce([&emptyFrameEvent2] { emptyFrameEvent2.Set(); }); waitStartStreamingEvent2.Wait(kShortWait); test_screencast_stream_provider_->RecordFrame( - red_color, TestScreenCastStreamProvider::EmptyData); + red_color, TestScreenCastStreamProvider::CorruptedData); emptyFrameEvent2.Wait(kShortWait); - EXPECT_CALL(*this, OnFrameRateChanged(22)) + EXPECT_CALL(*this, OnFormatChanged(SPA_VIDEO_FORMAT_BGRA, 800, 640, 22, + DRM_FORMAT_MOD_LINEAR)) .Times(1) .WillOnce([&waitStreamParamChangedEvent2] { waitStreamParamChangedEvent2.Set(); @@ -245,12 +253,12 @@ TEST_F(MAYBE_PipeWireStreamTest, TestPipeWire) { waitStartStreamingEvent3.Set(); }); Event emptyFrameEvent3; - EXPECT_CALL(*this, OnEmptyBuffer).WillOnce([&emptyFrameEvent3] { + EXPECT_CALL(*this, OnBufferCorruptedMetadata).WillOnce([&emptyFrameEvent3] { emptyFrameEvent3.Set(); }); waitStartStreamingEvent3.Wait(kShortWait); test_screencast_stream_provider_->RecordFrame( - red_color, TestScreenCastStreamProvider::EmptyData); + red_color, TestScreenCastStreamProvider::CorruptedMetadata); emptyFrameEvent3.Wait(kShortWait); // Test disconnection from stream @@ -258,4 +266,138 @@ TEST_F(MAYBE_PipeWireStreamTest, TestPipeWire) { shared_screencast_stream_->StopScreenCastStream(); } +TEST_F(MAYBE_PipeWireStreamTest, TestModifierFallback) { + // Set expectations for initial connection with DRM_FORMAT_MOD_LINEAR + Event waitConnectEvent; + Event waitStartStreamingEvent; + + EXPECT_CALL(*this, OnStreamReady(_)) + .WillOnce(Invoke(this, &MAYBE_PipeWireStreamTest::StartScreenCastStream)); + EXPECT_CALL(*this, OnStreamConfigured).WillOnce([&waitConnectEvent] { + waitConnectEvent.Set(); + }); + EXPECT_CALL(*this, OnBufferAdded).Times(AtLeast(1)); + EXPECT_CALL(*this, OnStartStreaming).WillOnce([&waitStartStreamingEvent] { + waitStartStreamingEvent.Set(); + }); + EXPECT_CALL(*this, OnFormatChanged(SPA_VIDEO_FORMAT_BGRA, 800, 640, 60, + DRM_FORMAT_MOD_LINEAR)) + .Times(AtLeast(1)); + + waitConnectEvent.Wait(kLongWait); + waitStartStreamingEvent.Wait(kShortWait); + + // Mark DRM_FORMAT_MOD_LINEAR as failed, expect renegotiation to + // kTestFailingModifier + Event waitRenegotiation1; + EXPECT_CALL(*this, OnStopStreaming); + EXPECT_CALL(*this, + OnFormatChanged(SPA_VIDEO_FORMAT_BGRA, 800, 640, 60, + static_cast(kTestFailingModifier))) + .Times(AtLeast(1)) + .WillRepeatedly([&waitRenegotiation1] { waitRenegotiation1.Set(); }); + EXPECT_CALL(*this, OnBufferAdded).Times(AtLeast(1)); + Event waitStartStreaming2; + EXPECT_CALL(*this, OnStartStreaming).WillOnce([&waitStartStreaming2] { + waitStartStreaming2.Set(); + }); + + // Mark modifier as failed in both producer and consumer + auto render_device = shared_screencast_egl_dmabuf_->GetRenderDevice(); + if (render_device) { + render_device->MarkModifierFailed(DRM_FORMAT_MOD_LINEAR); + } + test_screencast_stream_provider_->MarkModifierFailed(DRM_FORMAT_MOD_LINEAR); + waitRenegotiation1.Wait(kShortWait); + waitStartStreaming2.Wait(kShortWait); + + // Try to record frame with kTestFailingModifier - should fail on + // import + Event frameFailedEvent; + EXPECT_CALL(*this, OnFailedToProcessBuffer).WillOnce([&frameFailedEvent] { + frameFailedEvent.Set(); + }); + EXPECT_CALL(*this, OnFrameRecorded); + + RgbaColor red_color(0, 0, 255); + test_screencast_stream_provider_->RecordFrame(red_color); + frameFailedEvent.Wait(kShortWait); + + // Mark kTestFailingModifier as failed, expect renegotiation to + // kTestSuccessModifier + Event waitRenegotiation2; + EXPECT_CALL(*this, OnStopStreaming); + EXPECT_CALL(*this, OnFormatChanged(SPA_VIDEO_FORMAT_BGRA, 800, 640, 60, + kTestSuccessModifier)) + .Times(AtLeast(1)) + .WillRepeatedly([&waitRenegotiation2] { waitRenegotiation2.Set(); }); + EXPECT_CALL(*this, OnBufferAdded).Times(AtLeast(1)); + Event waitStartStreaming3; + EXPECT_CALL(*this, OnStartStreaming).WillOnce([&waitStartStreaming3] { + waitStartStreaming3.Set(); + }); + + // Mark modifier as failed in both producer and consumer + if (render_device) { + render_device->MarkModifierFailed(kTestFailingModifier); + } + test_screencast_stream_provider_->MarkModifierFailed(kTestFailingModifier); + waitRenegotiation2.Wait(kShortWait); + waitStartStreaming3.Wait(kShortWait); + + // Record frame with kTestSuccessModifier + Event frameSuccessEvent; + EXPECT_CALL(*this, OnFrameRecorded); + EXPECT_CALL(*this, OnDesktopFrameChanged).WillOnce([&frameSuccessEvent] { + frameSuccessEvent.Set(); + }); + + RgbaColor green_color(0, 255, 0); + test_screencast_stream_provider_->RecordFrame(green_color); + frameSuccessEvent.Wait(kShortWait); + + std::unique_ptr frame = + shared_screencast_stream_->CaptureFrame(); + ASSERT_NE(frame, nullptr); + EXPECT_EQ(RgbaColor(frame->data()), green_color); + + // Mark kTestSuccessModifier as failed, expect fallback to MemFd (no + // modifier) + Event waitRenegotiation3; + EXPECT_CALL(*this, OnStopStreaming); + // When falling back to MemFd, modifier should be DRM_FORMAT_MOD_INVALID + EXPECT_CALL(*this, OnFormatChanged(SPA_VIDEO_FORMAT_BGRA, 800, 640, 60, + DRM_FORMAT_MOD_INVALID)) + .Times(AtLeast(1)) + .WillOnce([&waitRenegotiation3] { waitRenegotiation3.Set(); }); + EXPECT_CALL(*this, OnBufferAdded).Times(AtLeast(1)); + Event waitStartStreaming4; + EXPECT_CALL(*this, OnStartStreaming).WillOnce([&waitStartStreaming4] { + waitStartStreaming4.Set(); + }); + + if (render_device) { + render_device->MarkModifierFailed(kTestSuccessModifier); + } + test_screencast_stream_provider_->MarkModifierFailed(kTestSuccessModifier); + waitRenegotiation3.Wait(kShortWait); + waitStartStreaming4.Wait(kShortWait); + + // Record empty frame with MemFd + Event emptyFrameEvent; + EXPECT_CALL(*this, OnFrameRecorded); + EXPECT_CALL(*this, OnEmptyBuffer).WillOnce([&emptyFrameEvent] { + emptyFrameEvent.Set(); + }); + + RgbaColor blue_color(255, 0, 0); + test_screencast_stream_provider_->RecordFrame( + blue_color, TestScreenCastStreamProvider::EmptyData); + emptyFrameEvent.Wait(kShortWait); + + // Test disconnection from stream + EXPECT_CALL(*this, OnStopStreaming); + shared_screencast_stream_->StopScreenCastStream(); +} + } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.cc b/modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.cc new file mode 100644 index 00000000000..36be3fa8ee0 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.cc @@ -0,0 +1,134 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.h" + +#include +#include +#include + +#include +#include +#include + +#include "modules/portal/pipewire_utils.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +TestEglDrmDevice::TestEglDrmDevice(dev_t device_id) + : EglDrmDevice(EGL_NO_DISPLAY, device_id) { + device_id_ = device_id; + initialized_ = true; +} + +bool TestEglDrmDevice::ImageFromDmaBuf( + const DesktopSize& size, + uint32_t format, + const std::vector& plane_datas, + uint64_t modifier, + const DesktopVector& offset, + const DesktopSize& buffer_size, + uint8_t* data) { + if (modifier == kTestFailingModifier) { + RTC_LOG(LS_INFO) + << "TestEglDrmDevice: Simulating import failure for modifier " + << modifier; + return false; + } + + if (plane_datas.empty()) { + RTC_LOG(LS_ERROR) << "TestEglDrmDevice: No plane data provided"; + return false; + } + + const PlaneData& plane = plane_datas[0]; + + if (plane.fd < 0) { + RTC_LOG(LS_ERROR) << "TestEglDrmDevice: Invalid file descriptor"; + return false; + } + + const int kBytesPerPixel = 4; + const size_t plane_stride = plane.stride; + const size_t buffer_size_bytes = plane_stride * size.height(); + + uint8_t* map = static_cast( + mmap(nullptr, buffer_size_bytes, PROT_READ, MAP_SHARED, plane.fd, 0)); + ScopedBuf scoped_buf; + scoped_buf.initialize(map, buffer_size_bytes, plane.fd, true); + + if (!scoped_buf) { + RTC_LOG(LS_ERROR) << "TestEglDrmDevice: Failed to mmap DMA-BUF"; + return false; + } + + uint8_t* src = scoped_buf.get() + plane.offset; + const size_t dst_stride = buffer_size.width() * kBytesPerPixel; + + for (int y = 0; y < size.height(); ++y) { + memcpy(data + y * dst_stride + offset.x() * kBytesPerPixel, + src + y * plane_stride + offset.x() * kBytesPerPixel, + size.width() * kBytesPerPixel); + } + + RTC_LOG(LS_INFO) + << "TestEglDrmDevice: Successfully read DMA-BUF with modifier " + << modifier << " (" << size.width() << "x" << size.height() + << ", stride=" << plane_stride << ")"; + return true; +} + +std::vector TestEglDrmDevice::QueryDmaBufModifiers(uint32_t format) { + std::vector modifiers = { + DRM_FORMAT_MOD_LINEAR, // Always available + kTestFailingModifier, // Modifier that will fail on import + kTestSuccessModifier // Test modifier that works + }; + + MutexLock lock(&failed_modifiers_lock_); + auto it = failed_modifiers_.find(format); + if (it != failed_modifiers_.end()) { + const auto& failed = it->second; + modifiers.erase(std::remove_if(modifiers.begin(), modifiers.end(), + [&failed](uint64_t modifier) { + return failed.find(modifier) != + failed.end(); + }), + modifiers.end()); + } + + RTC_LOG(LS_INFO) << "TestEglDrmDevice: Returning " << modifiers.size() + << " modifiers for format " << format; + return modifiers; +} + +std::unique_ptr TestEglDmaBuf::CreateDefault() { + auto instance = std::unique_ptr(new TestEglDmaBuf()); + if (!instance->Initialize()) { + RTC_LOG(LS_ERROR) << "TestEglDmaBuf initialization failed"; + return nullptr; + } + return instance; +} + +bool TestEglDmaBuf::Initialize() { + const dev_t kTestDeviceId = makedev(10, 0); + + auto test_device = std::make_unique(kTestDeviceId); + devices_[kTestDeviceId] = std::move(test_device); + + RTC_LOG(LS_INFO) << "TestEglDmaBuf: Created test DRM device with ID " + << major(kTestDeviceId) << ":" << minor(kTestDeviceId); + + return true; +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.h b/modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.h new file mode 100644 index 00000000000..79933c48751 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.h @@ -0,0 +1,61 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_TEST_TEST_EGL_DMABUF_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_TEST_TEST_EGL_DMABUF_H_ + +#include +#include +#include + +#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h" + +namespace webrtc { + +// Test DMA-BUF modifier constants +// Using vendor ID 0xFE. This is not a standard reserved ID in drm_fourcc.h, +// but it is unlikely to conflict with real hardware vendors currently allocated +// in the 0x00-0x0F range. +constexpr uint64_t kTestFailingModifier = 0xFE00000000000001ULL; +constexpr uint64_t kTestSuccessModifier = 0xFE00000000000002ULL; + +// Test EGL DRM device for testing DMA-BUF functionality. +// Simulates DMA-BUF operations without requiring real EGL/GBM. +class TestEglDrmDevice : public EglDrmDevice { + public: + explicit TestEglDrmDevice(dev_t device_id = DEVICE_ID_INVALID); + ~TestEglDrmDevice() override = default; + + bool ImageFromDmaBuf(const DesktopSize& size, + uint32_t format, + const std::vector& plane_datas, + uint64_t modifier, + const DesktopVector& offset, + const DesktopSize& buffer_size, + uint8_t* data) override; + + std::vector QueryDmaBufModifiers(uint32_t format) override; +}; + +// Test EGL DMA-BUF manager for testing. +// Creates a single test device for testing DMA-BUF negotiation and fallback. +class TestEglDmaBuf : public EglDmaBuf { + public: + static std::unique_ptr CreateDefault(); + ~TestEglDmaBuf() override = default; + + protected: + TestEglDmaBuf() = default; + bool Initialize() override; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_TEST_TEST_EGL_DMABUF_H_ diff --git a/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc b/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc index 26d8302d1f5..e1eaa369b8f 100644 --- a/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc +++ b/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.cc @@ -12,9 +12,11 @@ #include "modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.h" #include +#include #include #include #include +#include #include #include #include @@ -30,10 +32,12 @@ #include #include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h" +#include "modules/desktop_capture/linux/wayland/test/test_egl_dmabuf.h" #include "modules/desktop_capture/rgba_color.h" #include "modules/portal/pipewire_utils.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" namespace webrtc { @@ -44,7 +48,7 @@ TestScreenCastStreamProvider::TestScreenCastStreamProvider(Observer* observer, uint32_t height) : observer_(observer), width_(width), height_(height) { if (!InitializePipeWire()) { - RTC_LOG(LS_ERROR) << "Unable to open PipeWire"; + RTC_LOG(LS_ERROR) << "Unable to open PipeWire library"; return; } @@ -55,12 +59,12 @@ TestScreenCastStreamProvider::TestScreenCastStreamProvider(Observer* observer, pw_context_ = pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0); if (!pw_context_) { - RTC_LOG(LS_ERROR) << "PipeWire test: Failed to create PipeWire context"; + RTC_LOG(LS_ERROR) << "Failed to create PipeWire context"; return; } if (pw_thread_loop_start(pw_main_loop_) < 0) { - RTC_LOG(LS_ERROR) << "PipeWire test: Failed to start main PipeWire loop"; + RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; return; } @@ -74,12 +78,14 @@ TestScreenCastStreamProvider::TestScreenCastStreamProvider(Observer* observer, pw_stream_events_.state_changed = &OnStreamStateChanged; pw_stream_events_.param_changed = &OnStreamParamChanged; + egl_dmabuf_ = TestEglDmaBuf::CreateDefault(); + { PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_); pw_core_ = pw_context_connect(pw_context_, nullptr, 0); if (!pw_core_) { - RTC_LOG(LS_ERROR) << "PipeWire test: Failed to connect PipeWire context"; + RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; return; } @@ -88,31 +94,28 @@ TestScreenCastStreamProvider::TestScreenCastStreamProvider(Observer* observer, pw_stream_ = pw_stream_new(pw_core_, "webrtc-test-stream", nullptr); if (!pw_stream_) { - RTC_LOG(LS_ERROR) << "PipeWire test: Failed to create PipeWire stream"; + RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; return; } pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_, this); - uint8_t buffer[2048] = {}; - + uint8_t buffer[4096] = {}; spa_pod_builder builder = spa_pod_builder{.data = buffer, .size = sizeof(buffer)}; std::vector params; - spa_rectangle resolution = SPA_RECTANGLE(uint32_t(width_), uint32_t(height_)); struct spa_fraction default_frame_rate = SPA_FRACTION(60, 1); - params.push_back(BuildFormat(&builder, SPA_VIDEO_FORMAT_BGRx, - /*modifiers=*/{}, &resolution, - &default_frame_rate)); + BuildFullFormat(&builder, egl_dmabuf_->GetRenderDevice(), &resolution, + &default_frame_rate, params); auto flags = pw_stream_flags(PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_ALLOC_BUFFERS); if (pw_stream_connect(pw_stream_, PW_DIRECTION_OUTPUT, SPA_ID_INVALID, flags, params.data(), params.size()) != 0) { - RTC_LOG(LS_ERROR) << "PipeWire test: Could not connect receiving stream."; + RTC_LOG(LS_ERROR) << "Could not connect sending stream."; pw_stream_destroy(pw_stream_); pw_stream_ = nullptr; return; @@ -123,11 +126,14 @@ TestScreenCastStreamProvider::TestScreenCastStreamProvider(Observer* observer, } TestScreenCastStreamProvider::~TestScreenCastStreamProvider() { - if (pw_main_loop_) { - pw_thread_loop_stop(pw_main_loop_); + if (!pw_main_loop_) { + return; } + pw_thread_loop_stop(pw_main_loop_); + if (pw_stream_) { + pw_stream_disconnect(pw_stream_); pw_stream_destroy(pw_stream_); } @@ -139,9 +145,28 @@ TestScreenCastStreamProvider::~TestScreenCastStreamProvider() { pw_context_destroy(pw_context_); } - if (pw_main_loop_) { - pw_thread_loop_destroy(pw_main_loop_); + pw_thread_loop_destroy(pw_main_loop_); +} + +void TestScreenCastStreamProvider::MarkModifierFailed(uint64_t modifier) { + auto render_device = egl_dmabuf_->GetRenderDevice(); + if (render_device) { + render_device->MarkModifierFailed(modifier); } + + // Start stream negotiation again + uint8_t buffer[4096] = {}; + spa_pod_builder builder = + spa_pod_builder{.data = buffer, .size = sizeof(buffer)}; + + std::vector params; + spa_rectangle resolution = SPA_RECTANGLE(uint32_t(width_), uint32_t(height_)); + struct spa_fraction default_frame_rate = SPA_FRACTION(60, 1); + BuildFullFormat(&builder, egl_dmabuf_->GetRenderDevice(), &resolution, + &default_frame_rate, params); + + PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_); + pw_stream_update_params(pw_stream_, params.data(), params.size()); } void TestScreenCastStreamProvider::RecordFrame(RgbaColor rgba_color, @@ -149,36 +174,64 @@ void TestScreenCastStreamProvider::RecordFrame(RgbaColor rgba_color, const char* error; if (pw_stream_get_state(pw_stream_, &error) != PW_STREAM_STATE_STREAMING) { if (error) { - RTC_LOG(LS_ERROR) - << "PipeWire test: Failed to record frame: stream is not active: " - << error; + RTC_LOG(LS_ERROR) << "Failed to record frame: stream is not active: " + << error; } } struct pw_buffer* buffer = pw_stream_dequeue_buffer(pw_stream_); if (!buffer) { - RTC_LOG(LS_ERROR) << "PipeWire test: No available buffer"; + RTC_LOG(LS_ERROR) << "No available buffer"; return; } struct spa_buffer* spa_buffer = buffer->buffer; struct spa_data* spa_data = spa_buffer->datas; - uint8_t* data = static_cast(spa_data->data); - if (!data) { - RTC_LOG(LS_ERROR) - << "PipeWire test: Failed to record frame: invalid buffer data"; + + const int stride = SPA_ROUND_UP_N(width_ * kBytesPerPixel, 4); + const size_t buffer_size = height_ * stride; + + uint8_t* data = nullptr; + ScopedBuf scoped_buf; + + if (spa_data->type == SPA_DATA_DmaBuf) { + uint8_t* map = + static_cast(mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, + MAP_SHARED, spa_data->fd, 0)); + scoped_buf.initialize(map, buffer_size, spa_data->fd, true); + if (!scoped_buf) { + RTC_LOG(LS_ERROR) << "Failed to mmap DMA-BUF for recording"; + pw_stream_queue_buffer(pw_stream_, buffer); + return; + } + data = scoped_buf.get(); + } else if (spa_data->type == SPA_DATA_MemFd) { + data = static_cast(spa_data->data); + if (!data) { + RTC_LOG(LS_ERROR) << "Failed to record frame: invalid buffer data"; + pw_stream_queue_buffer(pw_stream_, buffer); + return; + } + } else { + RTC_LOG(LS_ERROR) << "Unsupported buffer type"; pw_stream_queue_buffer(pw_stream_, buffer); return; } - const int stride = SPA_ROUND_UP_N(width_ * kBytesPerPixel, 4); - spa_data->chunk->offset = 0; - spa_data->chunk->size = height_ * stride; + spa_data->chunk->size = buffer_size; spa_data->chunk->stride = stride; - // Produce a frame with given defect - if (frame_defect == EmptyData) { + if (frame_defect == None) { + uint32_t color = rgba_color.ToUInt32(); + for (uint32_t i = 0; i < height_; i++) { + uint32_t* column = reinterpret_cast(data); + for (uint32_t j = 0; j < width_; j++) { + column[j] = color; + } + data += stride; + } + } else if (frame_defect == EmptyData) { spa_data->chunk->size = 0; } else if (frame_defect == CorruptedData) { spa_data->chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; @@ -189,15 +242,6 @@ void TestScreenCastStreamProvider::RecordFrame(RgbaColor rgba_color, if (spa_header) { spa_header->flags = SPA_META_HEADER_FLAG_CORRUPTED; } - } else { - uint32_t color = rgba_color.ToUInt32(); - for (uint32_t i = 0; i < height_; i++) { - uint32_t* column = reinterpret_cast(data); - for (uint32_t j = 0; j < width_; j++) { - column[j] = color; - } - data += stride; - } } pw_stream_queue_buffer(pw_stream_, buffer); @@ -228,7 +272,7 @@ void TestScreenCastStreamProvider::OnCoreError(void* data, static_cast(data); RTC_DCHECK(that); - RTC_LOG(LS_ERROR) << "PipeWire test: PipeWire remote error: " << message; + RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message; } // static @@ -243,8 +287,7 @@ void TestScreenCastStreamProvider::OnStreamStateChanged( switch (state) { case PW_STREAM_STATE_ERROR: - RTC_LOG(LS_ERROR) << "PipeWire test: PipeWire stream state error: " - << error_message; + RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message; break; case PW_STREAM_STATE_PAUSED: if (that->pw_node_id_ == 0 && that->pw_stream_) { @@ -282,31 +325,94 @@ void TestScreenCastStreamProvider::OnStreamParamChanged( static_cast(data); RTC_DCHECK(that); - RTC_LOG(LS_INFO) << "PipeWire test: PipeWire stream format changed."; if (!format || id != SPA_PARAM_Format) { return; } + that->spa_video_format_ = {}; spa_format_video_raw_parse(format, &that->spa_video_format_); - auto stride = SPA_ROUND_UP_N(that->width_ * kBytesPerPixel, 4); - - uint8_t buffer[1024] = {}; - auto builder = spa_pod_builder{.data = buffer, .size = sizeof(buffer)}; + const struct spa_pod_prop* prop_modifier = + spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier); + const bool has_modifier = prop_modifier != nullptr; + that->modifier_ = + has_modifier ? that->spa_video_format_.modifier : DRM_FORMAT_MOD_INVALID; + + if (prop_modifier && (prop_modifier->flags & SPA_POD_PROP_FLAG_DONT_FIXATE)) { + const struct spa_pod* pod_modifier = &prop_modifier->value; + uint64_t* modifiers = + static_cast(SPA_POD_CHOICE_VALUES(pod_modifier)); + uint32_t n_modifiers = SPA_POD_CHOICE_N_VALUES(pod_modifier); + + if (n_modifiers > 0) { + uint64_t chosen_modifier = modifiers[0]; + + RTC_LOG(LS_INFO) << "Fixating on modifier: " << chosen_modifier; + + uint8_t buffer[4096] = {}; + struct spa_pod_builder builder = + SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct spa_pod_frame frame; + std::vector params; + + spa_pod_builder_push_object(&builder, &frame, SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat); + spa_pod_builder_add( + &builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_VIDEO_format, SPA_POD_Id(that->spa_video_format_.format), + SPA_FORMAT_VIDEO_size, + SPA_POD_Rectangle(&that->spa_video_format_.size), 0); + spa_pod_builder_prop(&builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(&builder, chosen_modifier); + + params.push_back( + reinterpret_cast(spa_pod_builder_pop(&builder, &frame))); + + spa_rectangle resolution = SPA_RECTANGLE(that->width_, that->height_); + struct spa_fraction default_frame_rate = SPA_FRACTION(60, 1); + BuildFullFormat(&builder, that->egl_dmabuf_->GetRenderDevice(), + &resolution, &default_frame_rate, params); + + pw_stream_update_params(that->pw_stream_, params.data(), params.size()); + return; + } + } - // Setup buffers and meta header for new format. + const int buffer_types = + has_modifier ? (1 << SPA_DATA_DmaBuf) : (1 << SPA_DATA_MemFd); + + if (RTC_LOG_CHECK_LEVEL(LS_INFO)) { + StringBuilder sb; + sb << "PipeWire stream format changed:\n"; + sb << " Format: " << that->spa_video_format_.format << " (" + << spa_debug_type_find_name(spa_type_video_format, + that->spa_video_format_.format) + << ")\n"; + if (has_modifier) { + sb << " Modifier: " << that->modifier_ << "\n"; + } + sb << " Size: " << that->spa_video_format_.size.width << " x " + << that->spa_video_format_.size.height << "\n"; + sb << " Framerate: " << that->spa_video_format_.framerate.num << "/" + << that->spa_video_format_.framerate.denom << "\n"; + sb << " Buffer Type:"; + if (buffer_types & (1 << SPA_DATA_DmaBuf)) { + sb << " DmaBuf\n"; + } else if (buffer_types & (1 << SPA_DATA_MemFd)) { + sb << " MemFd\n"; + } + RTC_LOG(LS_INFO) << sb.str(); + } + uint8_t buffer[4096] = {}; + auto builder = spa_pod_builder{.data = buffer, .size = sizeof(buffer)}; std::vector params; - const int buffer_types = (1 << SPA_DATA_MemFd); - spa_rectangle resolution = SPA_RECTANGLE(that->width_, that->height_); - params.push_back(reinterpret_cast(spa_pod_builder_add_object( &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, - SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&resolution), - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(16, 2, 16), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_stride, - SPA_POD_Int(stride), SPA_PARAM_BUFFERS_size, - SPA_POD_Int(stride * that->height_), SPA_PARAM_BUFFERS_align, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_align, SPA_POD_Int(16), SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(buffer_types)))); params.push_back(reinterpret_cast(spa_pod_builder_add_object( @@ -317,55 +423,99 @@ void TestScreenCastStreamProvider::OnStreamParamChanged( pw_stream_update_params(that->pw_stream_, params.data(), params.size()); } -// static -void TestScreenCastStreamProvider::OnStreamAddBuffer(void* data, - pw_buffer* buffer) { - TestScreenCastStreamProvider* that = - static_cast(data); - RTC_DCHECK(that); - - struct spa_data* spa_data = buffer->buffer->datas; +static bool CreateMemFDBuffer(struct spa_data* spa_data, size_t size) { + spa_data[0].mapoffset = 0; + spa_data[0].flags = SPA_DATA_FLAG_READWRITE; + spa_data[0].maxsize = size; + spa_data[0].type = SPA_DATA_MemFd; + spa_data[0].fd = + memfd_create("pipewire-test-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (spa_data[0].fd == kInvalidPipeWireFd) { + RTC_LOG(LS_ERROR) << "Can't create memfd"; + return false; + } - spa_data->mapoffset = 0; - spa_data->flags = SPA_DATA_FLAG_READWRITE; + if (ftruncate(spa_data[0].fd, spa_data[0].maxsize) < 0) { + RTC_LOG(LS_ERROR) << "Can't truncate to " << spa_data[0].maxsize; + close(spa_data[0].fd); + spa_data[0].fd = kInvalidPipeWireFd; + return false; + } - if (!(spa_data[0].type & (1 << SPA_DATA_MemFd))) { - RTC_LOG(LS_ERROR) - << "PipeWire test: Client doesn't support memfd buffer data type"; - return; + unsigned int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; + if (fcntl(spa_data[0].fd, F_ADD_SEALS, seals) == -1) { + RTC_LOG(LS_ERROR) << "Failed to add seals"; + return false; } - const int stride = SPA_ROUND_UP_N(that->width_ * kBytesPerPixel, 4); - spa_data->maxsize = stride * that->height_; - spa_data->type = SPA_DATA_MemFd; - spa_data->fd = - memfd_create("pipewire-test-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (spa_data->fd == kInvalidPipeWireFd) { - RTC_LOG(LS_ERROR) << "PipeWire test: Can't create memfd"; - return; + spa_data[0].data = mmap(nullptr, spa_data[0].maxsize, PROT_READ | PROT_WRITE, + MAP_SHARED, spa_data[0].fd, spa_data[0].mapoffset); + if (spa_data[0].data == MAP_FAILED) { + RTC_LOG(LS_ERROR) << "Failed to mmap memory"; + close(spa_data[0].fd); + spa_data[0].fd = kInvalidPipeWireFd; + return false; } - spa_data->mapoffset = 0; + return true; +} - if (ftruncate(spa_data->fd, spa_data->maxsize) < 0) { - RTC_LOG(LS_ERROR) << "PipeWire test: Can't truncate to" - << spa_data->maxsize; - return; +static bool CreateDmaBufBuffer(struct spa_data* spa_data, + size_t size, + uint32_t stride) { + int fd = + memfd_create("pipewire-test-dmabuf", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd == kInvalidPipeWireFd) { + RTC_LOG(LS_ERROR) << "Can't create memfd for DMA-BUF"; + return false; } - unsigned int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; - if (fcntl(spa_data->fd, F_ADD_SEALS, seals) == -1) { - RTC_LOG(LS_ERROR) << "PipeWire test: Failed to add seals"; + if (ftruncate(fd, size) < 0) { + RTC_LOG(LS_ERROR) << "Can't truncate DMA-BUF to " << size; + close(fd); + return false; } - spa_data->data = mmap(nullptr, spa_data->maxsize, PROT_READ | PROT_WRITE, - MAP_SHARED, spa_data->fd, spa_data->mapoffset); - if (spa_data->data == MAP_FAILED) { - RTC_LOG(LS_ERROR) << "PipeWire test: Failed to mmap memory"; + spa_data[0].type = SPA_DATA_DmaBuf; + spa_data[0].flags = SPA_DATA_FLAG_READWRITE; + spa_data[0].fd = fd; + spa_data[0].mapoffset = 0; + spa_data[0].maxsize = size; + spa_data[0].data = nullptr; // DMA-BUF is not mmap'd by producer + spa_data[0].chunk->offset = 0; + spa_data[0].chunk->size = size; + spa_data[0].chunk->stride = stride; + spa_data[0].chunk->flags = SPA_CHUNK_FLAG_NONE; + + return true; +} + +// static +void TestScreenCastStreamProvider::OnStreamAddBuffer(void* data, + pw_buffer* buffer) { + TestScreenCastStreamProvider* that = + static_cast(data); + RTC_DCHECK(that); + + struct spa_buffer* spa_buffer = buffer->buffer; + struct spa_data* spa_data = spa_buffer->datas; + const int stride = SPA_ROUND_UP_N(that->width_ * kBytesPerPixel, 4); + const size_t buffer_size = stride * that->height_; + + if (spa_data[0].type & (1 << SPA_DATA_DmaBuf)) { + if (CreateDmaBufBuffer(spa_data, buffer_size, stride)) { + that->observer_->OnBufferAdded(); + RTC_LOG(LS_INFO) << "DMA-BUF buffer created successfully: fd=" + << spa_data[0].fd << " size=" << buffer_size; + } + } else if (spa_data[0].type & (1 << SPA_DATA_MemFd)) { + if (CreateMemFDBuffer(spa_data, buffer_size)) { + that->observer_->OnBufferAdded(); + RTC_LOG(LS_INFO) << "Memfd buffer created successfully: " + << spa_data[0].data << " size=" << spa_data[0].maxsize; + } } else { - that->observer_->OnBufferAdded(); - RTC_LOG(LS_INFO) << "PipeWire test: Memfd created successfully: " - << spa_data->data << spa_data->maxsize; + RTC_LOG(LS_INFO) << "Unsupported buffer type"; } } @@ -378,8 +528,16 @@ void TestScreenCastStreamProvider::OnStreamRemoveBuffer(void* data, struct spa_buffer* spa_buffer = buffer->buffer; struct spa_data* spa_data = spa_buffer->datas; - if (spa_data && spa_data->type == SPA_DATA_MemFd) { + + if (!spa_data) { + return; + } + + if (spa_data->type == SPA_DATA_MemFd) { munmap(spa_data->data, spa_data->maxsize); + } + + if (spa_data->fd >= 0) { close(spa_data->fd); } } diff --git a/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.h b/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.h index bf88c37ea0e..2262d49200d 100644 --- a/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.h +++ b/modules/desktop_capture/linux/wayland/test/test_screencast_stream_provider.h @@ -24,6 +24,8 @@ namespace webrtc { +class EglDmaBuf; + class TestScreenCastStreamProvider { public: class Observer { @@ -48,6 +50,7 @@ class TestScreenCastStreamProvider { uint32_t PipeWireNodeId(); + void MarkModifierFailed(uint64_t modifier); void RecordFrame(RgbaColor rgba_color, FrameDefect frame_defect = None); void StartStreaming(); void StopStreaming(); @@ -76,7 +79,11 @@ class TestScreenCastStreamProvider { pw_core_events pw_core_events_ = {}; pw_stream_events pw_stream_events_ = {}; - struct spa_video_info_raw spa_video_format_; + struct spa_video_info_raw spa_video_format_ = {}; + uint64_t modifier_ = 0; + + // Test EGL DMA-BUF for testing + std::unique_ptr egl_dmabuf_; // PipeWire callbacks static void OnCoreError(void* data, diff --git a/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc b/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc index 732aa4d29d4..72f2716e033 100644 --- a/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc +++ b/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc @@ -30,6 +30,8 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" +namespace webrtc { + namespace { // WindowCapturer returns window IDs of X11 windows with WM_STATE attribute. @@ -64,8 +66,6 @@ Window GetTopLevelWindow(Display* display, Window window) { } // namespace -namespace webrtc { - MouseCursorMonitorX11::MouseCursorMonitorX11( const DesktopCaptureOptions& options, Window window) diff --git a/modules/desktop_capture/linux/x11/screen_capturer_x11.cc b/modules/desktop_capture/linux/x11/screen_capturer_x11.cc index 9affb03a553..c12bc761166 100644 --- a/modules/desktop_capture/linux/x11/screen_capturer_x11.cc +++ b/modules/desktop_capture/linux/x11/screen_capturer_x11.cc @@ -17,10 +17,12 @@ #include #include #include +// X11 creates a CurrentTime macro, which causes compilation errors when +// including webrtc::Clock. +#undef CurrentTime #include #include -#include #include #include diff --git a/modules/desktop_capture/linux/x11/window_capturer_x11.cc b/modules/desktop_capture/linux/x11/window_capturer_x11.cc index fca67dfaec9..fcc4113b421 100644 --- a/modules/desktop_capture/linux/x11/window_capturer_x11.cc +++ b/modules/desktop_capture/linux/x11/window_capturer_x11.cc @@ -15,6 +15,9 @@ #include #include #include +// X11 creates a CurrentTime macro, which causes compilation errors when +// including webrtc::Clock. +#undef CurrentTime #include #include @@ -22,6 +25,7 @@ #include #include "api/scoped_refptr.h" +#include "media/base/video_common.h" #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_capturer.h" diff --git a/modules/desktop_capture/mac/full_screen_mac_application_handler.cc b/modules/desktop_capture/mac/full_screen_mac_application_handler.cc index a43797f8a68..ce7e4d4db8e 100644 --- a/modules/desktop_capture/mac/full_screen_mac_application_handler.cc +++ b/modules/desktop_capture/mac/full_screen_mac_application_handler.cc @@ -13,7 +13,11 @@ #include #include +#include +#include #include +#include +#include #include #include "absl/strings/match.h" diff --git a/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/modules/desktop_capture/mouse_cursor_monitor_mac.mm index 248a7827623..3e6a6cb3c66 100644 --- a/modules/desktop_capture/mouse_cursor_monitor_mac.mm +++ b/modules/desktop_capture/mouse_cursor_monitor_mac.mm @@ -154,7 +154,8 @@ DesktopVector hotspot( // crbug.com/632995.) After 10.12, OSX may report 2X cursor on non-Retina // screen. (See crbug.com/671436.) So scaling the cursor if needed. CGImageRef scaled_cg_image = nil; - if (CGImageGetWidth(cg_image) != static_cast(size.width())) { + if (CGImageGetWidth(cg_image) != static_cast(size.width()) || + CGImageGetHeight(cg_image) != static_cast(size.height())) { scaled_cg_image = CreateScaledCGImage(cg_image, size.width(), size.height()); if (scaled_cg_image != nil) { @@ -163,6 +164,7 @@ DesktopVector hotspot( } if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 || CGImageGetWidth(cg_image) != static_cast(size.width()) || + CGImageGetHeight(cg_image) != static_cast(size.height()) || CGImageGetBitsPerComponent(cg_image) != 8) { if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image); return; diff --git a/modules/desktop_capture/screen_capturer_integration_test.cc b/modules/desktop_capture/screen_capturer_integration_test.cc index a1f61e343ab..34996fca10a 100644 --- a/modules/desktop_capture/screen_capturer_integration_test.cc +++ b/modules/desktop_capture/screen_capturer_integration_test.cc @@ -14,11 +14,11 @@ #include #include // TODO(zijiehe): Remove once flaky has been resolved. #include +#include #include #include #include -#include "api/array_view.h" #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame.h" @@ -216,7 +216,7 @@ class ScreenCapturerIntegrationTest : public ::testing::Test { // The else if statement is for debugging purpose only, // which should be removed after flakiness of // ScreenCapturerIntegrationTest has been resolved. - ArrayView frame_data( + std::span frame_data( frame->data(), frame->size().height() * frame->stride()); std::string result = Base64Encode(frame_data); std::cout << frame->size().width() << " x " << frame->size().height() diff --git a/modules/desktop_capture/shared_desktop_frame.cc b/modules/desktop_capture/shared_desktop_frame.cc index 57a98d7d031..d1472640fa4 100644 --- a/modules/desktop_capture/shared_desktop_frame.cc +++ b/modules/desktop_capture/shared_desktop_frame.cc @@ -54,7 +54,8 @@ SharedDesktopFrame::SharedDesktopFrame(scoped_refptr core) (*core)->stride(), (*core)->pixel_format(), (*core)->data(), - (*core)->shared_memory()), + (*core)->shared_memory(), + (*core)->texture()), core_(core) { CopyFrameInfoFrom(*(core_->get())); } diff --git a/modules/desktop_capture/win/dxgi_desktop_frame.cc b/modules/desktop_capture/win/dxgi_desktop_frame.cc new file mode 100644 index 00000000000..2a929c373d6 --- /dev/null +++ b/modules/desktop_capture/win/dxgi_desktop_frame.cc @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/win/dxgi_desktop_frame.h" + +#include +#include +#include + +#include + +#include "modules/desktop_capture/win/desktop_capture_utils.h" +#include "rtc_base/logging.h" + +using Microsoft::WRL::ComPtr; + +namespace webrtc { + +namespace { + +class DXGIFrameTexture : public FrameTexture { + public: + DXGIFrameTexture(Handle handle, ComPtr prevent_release) + : FrameTexture(handle), prevent_release_(std::move(prevent_release)) {} + + ~DXGIFrameTexture() override { + if (handle_ != kInvalidHandle) { + CloseHandle(handle_); + } + } + + private: + // Prevents the WGC frame pool from recycling the captured texture. Typically + // holds a reference to the IDirect3D11CaptureFrame. When this pointer (and + // hence this DXGIFrameTexture) is destroyed, the capture frame is released + // and the frame pool may reuse the underlying texture. + ComPtr prevent_release_; +}; + +} // namespace + +DXGIDesktopFrame::DXGIDesktopFrame(DesktopSize size, + int stride, + uint8_t* data, + std::unique_ptr frame_texture) + : DesktopFrame(size, + stride, + FOURCC_ARGB, + data, + /*shared_memory=*/nullptr, + frame_texture.get()), + owned_frame_texture_(std::move(frame_texture)) {} + +// static +std::unique_ptr DXGIDesktopFrame::Create( + DesktopSize size, + ComPtr texture, + ComPtr prevent_release) { + ComPtr dxgi_resource; + HRESULT hr = texture.As(&dxgi_resource); + if (FAILED(hr)) { + RTC_LOG(LS_ERROR) << "Failed to query IDXGIResource1: " + << desktop_capture::utils::ComErrorToString(hr); + return nullptr; + } + + HANDLE texture_handle; + hr = dxgi_resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, + nullptr, &texture_handle); + if (FAILED(hr)) { + RTC_LOG(LS_ERROR) << "Failed to create shared handle for texture: " + << desktop_capture::utils::ComErrorToString(hr); + return nullptr; + } + + auto frame_texture = std::make_unique( + texture_handle, std::move(prevent_release)); + return std::unique_ptr( + new DXGIDesktopFrame(size, 0, nullptr, std::move(frame_texture))); +} + +} // namespace webrtc diff --git a/modules/desktop_capture/win/dxgi_desktop_frame.h b/modules/desktop_capture/win/dxgi_desktop_frame.h new file mode 100644 index 00000000000..6ab7b71660b --- /dev/null +++ b/modules/desktop_capture/win/dxgi_desktop_frame.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_DESKTOP_FRAME_H_ +#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_DESKTOP_FRAME_H_ + +#include +#include + +#include + +#include "modules/desktop_capture/desktop_frame.h" +#include "modules/desktop_capture/frame_texture.h" + +namespace webrtc { + +// DesktopFrame implementation used by DXGI captures on Windows. +// Frame texture is stored in the handle. +class DXGIDesktopFrame : public DesktopFrame { + public: + DXGIDesktopFrame(const DXGIDesktopFrame&) = delete; + DXGIDesktopFrame& operator=(const DXGIDesktopFrame&) = delete; + + // Creates a DXGIDesktopFrame. If `prevent_release` is provided (typically + // an IDirect3D11CaptureFrame), it prevents the WGC frame pool from recycling + // the underlying texture while downstream consumers still reference this + // frame. + static std::unique_ptr Create( + DesktopSize size, + Microsoft::WRL::ComPtr texture, + Microsoft::WRL::ComPtr prevent_release); + + private: + DXGIDesktopFrame(DesktopSize size, + int stride, + uint8_t* data, + std::unique_ptr frame_texture); + + const std::unique_ptr owned_frame_texture_; +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_DESKTOP_FRAME_H_ diff --git a/modules/desktop_capture/win/dxgi_output_duplicator.cc b/modules/desktop_capture/win/dxgi_output_duplicator.cc index f07a55af46f..946401aaf11 100644 --- a/modules/desktop_capture/win/dxgi_output_duplicator.cc +++ b/modules/desktop_capture/win/dxgi_output_duplicator.cc @@ -229,6 +229,19 @@ bool DxgiOutputDuplicator::Duplicate(Context* context, // triggers screen flickering? const DesktopFrame& source = texture_->AsDesktopFrame(); + // During a resolution transition, DXGI might deliver a frame with the new + // size before the controller detects the change and reinitializes. + // `unrotated_size_` is cached at initialization and only updated when + // this object is recreated. We abort capture on mismatch to force + // reinitialization by the |DxgiDuplicatorController|. + if (!source.size().equals(unrotated_size_)) { + RTC_LOG(LS_WARNING) << "Captured frame size " << source.size().width() + << "x" << source.size().height() + << " does not match expected size " + << unrotated_size_.width() << "x" + << unrotated_size_.height(); + return false; + } if (rotation_ != Rotation::CLOCK_WISE_0) { for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); it.Advance()) { diff --git a/modules/desktop_capture/win/full_screen_win_application_handler.cc b/modules/desktop_capture/win/full_screen_win_application_handler.cc index c7548b00078..7a245c96f84 100644 --- a/modules/desktop_capture/win/full_screen_win_application_handler.cc +++ b/modules/desktop_capture/win/full_screen_win_application_handler.cc @@ -36,10 +36,16 @@ void RecordFullScreenDetectorResult(FullScreenDetectorResult result) { static_cast(FullScreenDetectorResult::kMaxValue)); } -void RecordFullScreenFindEditorResult(FullScreenFindEditorResult result) { - RTC_HISTOGRAM_ENUMERATION( - "WebRTC.Screenshare.FullScreenFindEditorResult", static_cast(result), - static_cast(FullScreenFindEditorResult::kMaxValue)); +void MaybeRecordFullScreenFindEditorResult( + FullScreenFindEditorResult result, + std::optional& previous_result) { + if (previous_result != result) { + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Screenshare.FullScreenFindEditorResult", + static_cast(result), + static_cast(FullScreenFindEditorResult::kMaxValue)); + previous_result = result; + } } namespace webrtc { @@ -246,15 +252,17 @@ DesktopCapturer::SourceId FullScreenPowerPointHandler::FindEditorWindow( } if (editor_ids.size() != 1) { - RecordFullScreenFindEditorResult( - FullScreenFindEditorResult::kFailureDueToSameTitleWindows); + MaybeRecordFullScreenFindEditorResult( + FullScreenFindEditorResult::kFailureDueToSameTitleWindows, + previous_find_editor_result_); // If `editor_ids` has more than one id, then there are multiple open // editors with the same title as the full screen slide show and then // there's no way of knowing which editor has opened the slide show. return 0; } - RecordFullScreenFindEditorResult(FullScreenFindEditorResult::kSuccess); + MaybeRecordFullScreenFindEditorResult(FullScreenFindEditorResult::kSuccess, + previous_find_editor_result_); return *editor_ids.begin(); } diff --git a/modules/desktop_capture/win/full_screen_win_application_handler.h b/modules/desktop_capture/win/full_screen_win_application_handler.h index 0accd62e9ea..3a51cea7e06 100644 --- a/modules/desktop_capture/win/full_screen_win_application_handler.h +++ b/modules/desktop_capture/win/full_screen_win_application_handler.h @@ -15,6 +15,7 @@ #include #include +#include #include #include "modules/desktop_capture/desktop_capturer.h" @@ -82,6 +83,9 @@ class FullScreenPowerPointHandler : public FullScreenApplicationHandler { mutable bool was_slide_show_created_after_capture_started_; mutable FullScreenDetectorResult full_screen_detector_result_; + + mutable std::optional + previous_find_editor_result_; }; std::unique_ptr diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc index 3969654bce7..9a887d7c0ed 100644 --- a/modules/desktop_capture/win/wgc_capture_session.cc +++ b/modules/desktop_capture/win/wgc_capture_session.cc @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "modules/desktop_capture/win/wgc_capture_session.h" + #include #include #include @@ -26,8 +28,8 @@ #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/desktop_geometry.h" #include "modules/desktop_capture/shared_desktop_frame.h" +#include "modules/desktop_capture/win/dxgi_desktop_frame.h" #include "modules/desktop_capture/win/screen_capture_utils.h" -#include "modules/desktop_capture/win/wgc_capture_session.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/thread.h" @@ -39,6 +41,7 @@ using Microsoft::WRL::ComPtr; namespace WGC = ABI::Windows::Graphics::Capture; +using IClosable = ABI::Windows::Foundation::IClosable; namespace webrtc { namespace { @@ -166,6 +169,36 @@ WgcCaptureSession::WgcCaptureSession(intptr_t source_id, WgcCaptureSession::~WgcCaptureSession() { RemoveEventHandlers(); + if (frame_pool_) { + ComPtr closable; + HRESULT hr = frame_pool_.As(&closable); + if (FAILED(hr)) { + RTC_LOG(LS_WARNING) << "Failed to query frame pool as IClosable: " << hr; + return; + } + hr = closable->Close(); + if (FAILED(hr)) { + RTC_LOG(LS_WARNING) << "Failed to close frame pool: " << hr; + } + } +} + +bool WgcCaptureSession::MayContainCursor() const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + if (!session_) { + return false; + } + // IsCursorCaptureEnabled was introduced in IGraphicsCaptureSession2. + // Default to true (cursor captured) if the interface is not available. + ComPtr session2; + HRESULT hr = session_->QueryInterface( + ABI::Windows::Graphics::Capture::IID_IGraphicsCaptureSession2, &session2); + if (FAILED(hr)) { + return true; + } + boolean cursor_enabled = true; + session2->get_IsCursorCaptureEnabled(&cursor_enabled); + return cursor_enabled; } HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) { @@ -236,8 +269,17 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) { return hr; } + allow_zero_hertz_ = options.allow_wgc_zero_hertz(); + // Texture mode requires Windows 11 24H2+ where WGC natively skips static + // frames, since we cannot perform CPU-side frame comparison on GPU textures. + allow_using_texture_ = + options.allow_wgc_using_texture() && DoesWgcSkipStaticFrames(); + RTC_LOG(LS_INFO) << "WGC capture session is outputing " + << (allow_using_texture_ ? "GPU texture" : "CPU memory") + << "."; + hr = frame_pool_statics2->CreateFreeThreaded( - direct3d_device_.Get(), kPixelFormat, kNumBuffers, size_, &frame_pool_); + direct3d_device_.Get(), kPixelFormat, num_buffers(), size_, &frame_pool_); if (FAILED(hr)) { RecordStartCaptureResult(StartCaptureResult::kCreateFramePoolFailed); return hr; @@ -249,7 +291,11 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) { return hr; } - if (!options.prefer_cursor_embedded()) { + // We cannot do cursor composition on texture for now, so we need to enable + // cursor embedding when texture is allowed. + const bool embed_cursor = + options.prefer_cursor_embedded() || allow_using_texture_; + if (!embed_cursor) { ComPtr session2; if (SUCCEEDED(session_->QueryInterface( ABI::Windows::Graphics::Capture::IID_IGraphicsCaptureSession2, @@ -282,8 +328,6 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) { options.wgc_include_secondary_windows()); } - allow_zero_hertz_ = options.allow_wgc_zero_hertz(); - hr = session_->StartCapture(); if (FAILED(hr)) { RTC_LOG(LS_ERROR) << "Failed to start CaptureSession: " << hr; @@ -303,7 +347,7 @@ bool WgcCaptureSession::WaitForFirstFrame() { ComPtr capture_frame = nullptr; // Flush the `frame_pool_` buffers so that we can receive the most recent // frames. - for (int i = 0; i < kNumBuffers; ++i) { + for (int i = 0; i < num_buffers(); ++i) { HRESULT hr = frame_pool_->TryGetNextFrame(&capture_frame); if (FAILED(hr)) { RTC_LOG(LS_ERROR) << "TryGetNextFrame failed: " << hr; @@ -443,6 +487,9 @@ HRESULT WgcCaptureSession::CreateMappedTexture( UINT width, UINT height) { RTC_DCHECK_RUN_ON(&sequence_checker_); + if (allow_using_texture_) { + return S_OK; + } D3D11_TEXTURE2D_DESC src_desc; src_texture->GetDesc(&src_desc); @@ -517,14 +564,6 @@ HRESULT WgcCaptureSession::ProcessFrame() { return hr; } - if (!mapped_texture_) { - hr = CreateMappedTexture(texture_2D); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kCreateMappedTextureFailed); - return hr; - } - } - // We need to copy `texture_2D` into `mapped_texture_` as the latter has the // D3D11_CPU_ACCESS_READ flag set, which lets us access the image data. // Otherwise it would only be readable by the GPU. @@ -541,21 +580,38 @@ HRESULT WgcCaptureSession::ProcessFrame() { // If the size changed, we must resize `mapped_texture_` and `frame_pool_` to // fit the new size. This must be done before `CopySubresourceRegion` so that // the textures are the same size. - if (SizeHasChanged(new_size, size_)) { + const bool needs_resize = SizeHasChanged(new_size, size_); + + if (!mapped_texture_ || needs_resize) { hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height); if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed); + RecordGetFrameResult(GetFrameResult::kCreateMappedTextureFailed); return hr; } + } + if (needs_resize) { hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat, - kNumBuffers, new_size); + num_buffers(), new_size); if (FAILED(hr)) { RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed); + // On failure, `frame_pool_` remains at `size_`. Reset `mapped_texture_` + // because `mapped_texture_` and `frame_pool_` are now at inconsistent + // sizes. Clearing it forces a consistent recreation on the next frame. + // Note that it's not sufficient to simply leave `mapped_texture_` at it's + // current size, because if the window were to be resized back to `size_`, + // then we would erroneously think we don't need to re-create the texture. + mapped_texture_.Reset(); return hr; } } + if (allow_using_texture_) { + ComPtr prevent_release; + capture_frame.As(&prevent_release); + return ProcessTexture(texture_2D, std::move(prevent_release)); + } + // If the size has changed since the last capture, we must be sure to use // the smaller dimensions. Otherwise we might overrun our buffer, or // read stale data from the last frame. @@ -660,7 +716,7 @@ HRESULT WgcCaptureSession::ProcessFrame() { const int width_in_bytes = current_frame->size().width() * DesktopFrame::kBytesPerPixel; RTC_DCHECK_GE(current_frame->stride(), width_in_bytes); - RTC_DCHECK_GE(map_info.RowPitch, width_in_bytes); + RTC_CHECK_GE(map_info.RowPitch, width_in_bytes); const int middle_pixel_offset = (image_width / 2) * DesktopFrame::kBytesPerPixel; for (int i = 0; i < image_height; i++) { @@ -729,6 +785,61 @@ HRESULT WgcCaptureSession::ProcessFrame() { return hr; } +HRESULT WgcCaptureSession::ProcessTexture(ComPtr texture, + ComPtr capture_frame) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + + D3D11_TEXTURE2D_DESC desc; + texture->GetDesc(&desc); + DesktopSize size(desc.Width, desc.Height); + + std::unique_ptr texture_frame = DXGIDesktopFrame::Create( + size, std::move(texture), std::move(capture_frame)); + if (!texture_frame) { + return E_FAIL; + } + + if (queue_.current_frame() && queue_.current_frame()->IsShared()) { + RTC_LOG(LS_VERBOSE) << "Overwriting texture frame that is still shared."; + } + + // Update the monitor and scale factor information for the new frame. + if (is_window_source_) { + monitor_ = ::MonitorFromWindow(reinterpret_cast(source_id_), + MONITOR_DEFAULTTONEAREST); + } else if (!monitor_.has_value()) { + HMONITOR monitor; + if (GetHmonitorFromDeviceIndex(source_id_, &monitor)) { + monitor_ = monitor; + } + } + + if (monitor_.has_value()) { + DEVICE_SCALE_FACTOR device_scale_factor = DEVICE_SCALE_FACTOR_INVALID; + HRESULT hr = + GetScaleFactorForMonitor(monitor_.value(), &device_scale_factor); + if (SUCCEEDED(hr) && device_scale_factor != DEVICE_SCALE_FACTOR_INVALID) { + texture_frame->set_device_scale_factor( + static_cast(device_scale_factor) / 100.0f); + } + } + + queue_.ReplaceCurrentFrame( + SharedDesktopFrame::Wrap(std::move(texture_frame))); + + // We use ProcessTexture() only when WGC supports skipping static frames + // (Windows 11 24H2+), so we can directly mark the whole frame as damaged + // without doing any content comparison. + damage_region_.SetRect(DesktopRect::MakeSize(size)); + + // Sync the session size with the captured texture size. + size_.Width = size.width(); + size_.Height = size.height(); + + RecordGetFrameResult(GetFrameResult::kSuccess); + return S_OK; +} + HRESULT WgcCaptureSession::OnItemClosed(WGC::IGraphicsCaptureItem* sender, IInspectable* event_args) { RTC_DCHECK_RUN_ON(&sequence_checker_); @@ -748,7 +859,9 @@ HRESULT WgcCaptureSession::OnItemClosed(WGC::IGraphicsCaptureItem* sender, void WgcCaptureSession::RemoveEventHandlers() { RemoveItemClosedEventHandler(); - RemoveFrameArrivedEventHandler(); + if (frame_pool_) { + RemoveFrameArrivedEventHandler(); + } } void WgcCaptureSession::RemoveItemClosedEventHandler() { diff --git a/modules/desktop_capture/win/wgc_capture_session.h b/modules/desktop_capture/win/wgc_capture_session.h index 0e5fbd3f981..74ef4c07bac 100644 --- a/modules/desktop_capture/win/wgc_capture_session.h +++ b/modules/desktop_capture/win/wgc_capture_session.h @@ -63,6 +63,10 @@ class WgcCaptureSession final { return is_capture_started_; } + // Returns true if the captured frame may contain the cursor. Reads the + // actual IsCursorCaptureEnabled state from the WGC capture session. + bool MayContainCursor() const; + // We keep 2 buffers in the frame pool since it results in a good compromise // between latency/capture-rate and the rate at which // Direct3D11CaptureFramePool.TryGetNextFrame returns NULL and we have to fall @@ -70,6 +74,14 @@ class WgcCaptureSession final { // We make this public for tests. static constexpr int kNumBuffers = 2; + // When using the texture path (allow_using_texture_), each in-flight frame + // holds a reference to the WGC capture frame (via prevent_release) to prevent + // the frame pool from recycling the texture. We need more buffers so that the + // frame pool has free slots for new captures while downstream consumers still + // hold older frames. The queue holds 2 frames, and the downstream consumer + // (encoder) may hold 1-2 more, so we use 5 to provide sufficient headroom. + static constexpr int kNumBuffersForTexture = 5; + private: class RefCountedEvent : public RefCountedNonVirtual, public Event { @@ -135,6 +147,13 @@ class WgcCaptureSession final { // Process the captured frame and copy it to the `queue_`. HRESULT ProcessFrame(); + // Process texture and copy frame with texture to the `queue_`. + // `capture_frame` is stored in the DXGIDesktopFrame's FrameTexture to keep + // the WGC frame alive, preventing the frame pool from recycling the texture + // while downstream consumers still reference this frame. + HRESULT ProcessTexture(Microsoft::WRL::ComPtr texture, + Microsoft::WRL::ComPtr capture_frame); + void RemoveEventHandlers(); void RemoveItemClosedEventHandler(); void RemoveFrameArrivedEventHandler(); @@ -144,6 +163,10 @@ class WgcCaptureSession final { bool allow_zero_hertz() const { return allow_zero_hertz_; } + int num_buffers() const { + return allow_using_texture_ ? kNumBuffersForTexture : kNumBuffers; + } + std::unique_ptr item_closed_token_; std::unique_ptr frame_arrived_token_; @@ -197,6 +220,12 @@ class WgcCaptureSession final { // adds complexity since memcmp() is performed on two successive frames. bool allow_zero_hertz_ = false; + // Caches the value of DesktopCaptureOptions.allow_wgc_using_texture() in + // StartCapture(). Store texture handle of captured frame in DesktopFrame + // instead of mapping texture data which can reduce copy if clients process + // textures directly. + bool allow_using_texture_ = false; + // Tracks damage region updates that were reported since the last time a frame // was captured. Currently only supports either the complete rect being // captured or an empty region. Will always be empty if `allow_zero_hertz_` is diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc index f15eddc2a9b..cc9ea52c1fb 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.cc +++ b/modules/desktop_capture/win/wgc_capturer_win.cc @@ -49,7 +49,6 @@ constexpr wchar_t kCoreMessagingDll[] = L"CoreMessaging.dll"; constexpr wchar_t kWgcSessionType[] = L"Windows.Graphics.Capture.GraphicsCaptureSession"; constexpr wchar_t kApiContract[] = L"Windows.Foundation.UniversalApiContract"; -constexpr wchar_t kDirtyRegionMode[] = L"DirtyRegionMode"; constexpr UINT16 kRequiredApiContractVersion = 8; enum class WgcCapturerResult { @@ -70,44 +69,6 @@ void RecordWgcCapturerResult(WgcCapturerResult error) { static_cast(WgcCapturerResult::kMaxValue)); } -// Checks if the DirtyRegionMode property is present in GraphicsCaptureSession -// and logs a boolean histogram with the result. -// TODO(https://crbug.com/40259177): Detecting support for this property means -// that the WGC API supports dirty regions and it can be utilized to improve -// the capture performance and the existing zero-herz support. -void LogDirtyRegionSupport() { - ComPtr - api_info_statics; - HRESULT hr = GetActivationFactory< - ABI::Windows::Foundation::Metadata::IApiInformationStatics, - RuntimeClass_Windows_Foundation_Metadata_ApiInformation>( - &api_info_statics); - if (FAILED(hr)) { - return; - } - - HSTRING dirty_region_mode; - hr = CreateHstring(kDirtyRegionMode, wcslen(kDirtyRegionMode), - &dirty_region_mode); - if (FAILED(hr)) { - DeleteHstring(dirty_region_mode); - return; - } - - HSTRING wgc_session_type; - hr = CreateHstring(kWgcSessionType, wcslen(kWgcSessionType), - &wgc_session_type); - if (SUCCEEDED(hr)) { - boolean is_dirty_region_mode_supported = - api_info_statics->IsPropertyPresent(wgc_session_type, dirty_region_mode, - &is_dirty_region_mode_supported); - RTC_HISTOGRAM_BOOLEAN("WebRTC.DesktopCapture.Win.WgcDirtyRegionSupport", - !!is_dirty_region_mode_supported); - } - DeleteHstring(dirty_region_mode); - DeleteHstring(wgc_session_type); -} - } // namespace bool IsWgcSupported(CaptureType capture_type) { @@ -217,7 +178,6 @@ WgcCapturerWin::WgcCapturerWin( reinterpret_cast(GetProcAddress( core_messaging_library_, "CreateDispatcherQueueController")); } - LogDirtyRegionSupport(); } WgcCapturerWin::~WgcCapturerWin() { @@ -471,7 +431,7 @@ void WgcCapturerWin::CaptureFrame() { capture_time_ms); frame->set_capture_time_ms(capture_time_ms); frame->set_capturer_id(DesktopCapturerId::kWgcCapturerWin); - frame->set_may_contain_cursor(options_.prefer_cursor_embedded()); + frame->set_may_contain_cursor(capture_session->MayContainCursor()); frame->set_top_left(capture_source_->GetTopLeft()); RecordWgcCapturerResult(WgcCapturerResult::kSuccess); callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS, diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc index 46e8e1432b5..8119c704ae8 100644 --- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc +++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc @@ -21,6 +21,7 @@ #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_capturer.h" +#include "modules/desktop_capture/frame_texture.h" #include "modules/desktop_capture/full_screen_window_detector.h" #include "modules/desktop_capture/win/full_screen_win_application_handler.h" #include "modules/desktop_capture/win/screen_capture_utils.h" @@ -789,4 +790,273 @@ TEST_F(WgcCapturerFullScreenDetectorTest, 0); } +// Tests for the WGC texture mode (allow_wgc_using_texture). +class WgcCapturerTextureTest : public ::testing::TestWithParam, + public DesktopCapturer::Callback { + public: + void SetUp() override { + com_initializer_ = + std::make_unique(ScopedCOMInitializer::kMTA); + ASSERT_TRUE(com_initializer_->Succeeded()); + + if (!IsWgcSupported(GetParam())) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerTextureTests on unsupported platforms."; + GTEST_SKIP(); + } + } + + void SetUpForWindowCapture(int window_width = kMediumWindowWidth, + int window_height = kMediumWindowHeight) { + auto options = DesktopCaptureOptions::CreateDefault(); + options.set_allow_wgc_using_texture(true); + capturer_ = WgcCapturerWin::CreateRawWindowCapturer(options); + CreateWindowOnSeparateThread(window_width, window_height); + StartWindowThreadMessageLoop(); + source_id_ = GetTestWindowIdFromSourceList(); + } + + void SetUpForScreenCapture() { + auto options = DesktopCaptureOptions::CreateDefault(); + options.set_allow_wgc_using_texture(true); + capturer_ = WgcCapturerWin::CreateRawScreenCapturer(options); + source_id_ = GetScreenIdFromSourceList(); + } + + void TearDown() override { + if (window_open_) { + CloseTestWindow(); + } + } + + void CreateWindowOnSeparateThread(int window_width, int window_height) { + window_thread_ = Thread::Create(); + window_thread_->SetName(kWindowThreadName, nullptr); + window_thread_->Start(); + SendTask(window_thread_.get(), [this, window_width, window_height]() { + window_thread_id_ = GetCurrentThreadId(); + window_info_ = + CreateTestWindow(kWindowTitle, window_height, window_width); + window_open_ = true; + + while (!IsWindowResponding(window_info_.hwnd)) { + RTC_LOG(LS_INFO) << "Waiting for test window to become responsive in " + "WgcCapturerTextureTest."; + } + + while (!IsWindowValidAndVisible(window_info_.hwnd)) { + RTC_LOG(LS_INFO) << "Waiting for test window to be visible in " + "WgcCapturerTextureTest."; + } + }); + + ASSERT_TRUE(window_thread_->RunningForTest()); + ASSERT_FALSE(window_thread_->IsCurrent()); + } + + void StartWindowThreadMessageLoop() { + window_thread_->PostTask([this]() { + MSG msg; + BOOL gm; + while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) { + ::DispatchMessage(&msg); + if (msg.message == kDestroyWindow) { + DestroyTestWindow(window_info_); + } + if (msg.message == kQuitRunning) { + PostQuitMessage(0); + } + } + }); + } + + void CloseTestWindow() { + ::PostThreadMessage(window_thread_id_, kDestroyWindow, 0, 0); + ::PostThreadMessage(window_thread_id_, kQuitRunning, 0, 0); + window_thread_->Stop(); + window_open_ = false; + } + + DesktopCapturer::SourceId GetTestWindowIdFromSourceList() { + intptr_t src_id = 0; + do { + DesktopCapturer::SourceList sources; + EXPECT_TRUE(capturer_->GetSourceList(&sources)); + auto it = std::find_if( + sources.begin(), sources.end(), + [&](const DesktopCapturer::Source& src) { + return src.id == reinterpret_cast(window_info_.hwnd); + }); + if (it != sources.end()) + src_id = it->id; + } while (src_id != reinterpret_cast(window_info_.hwnd)); + return src_id; + } + + DesktopCapturer::SourceId GetScreenIdFromSourceList() { + DesktopCapturer::SourceList sources; + EXPECT_TRUE(capturer_->GetSourceList(&sources)); + EXPECT_GT(sources.size(), 0ULL); + return sources[0].id; + } + + void DoCapture(int num_captures = 1) { + const int max_tries = num_captures == 1 ? 1 : kMaxTries; + int success_count = 0; + for (int i = 0; success_count < num_captures && i < max_tries; i++) { + capturer_->CaptureFrame(); + if (result_ == DesktopCapturer::Result::ERROR_PERMANENT) + break; + if (result_ == DesktopCapturer::Result::SUCCESS) + success_count++; + } + + total_successful_captures_ += success_count; + EXPECT_EQ(success_count, num_captures); + EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS); + EXPECT_TRUE(frame_); + } + + // DesktopCapturer::Callback interface + void OnCaptureResult(DesktopCapturer::Result result, + std::unique_ptr frame) override { + result_ = result; + frame_ = std::move(frame); + } + + protected: + std::unique_ptr com_initializer_; + DWORD window_thread_id_; + std::unique_ptr window_thread_; + WindowInfo window_info_; + intptr_t source_id_; + bool window_open_ = false; + DesktopCapturer::Result result_; + int total_successful_captures_ = 0; + std::unique_ptr frame_; + std::unique_ptr capturer_; +}; + +// Verifies that the captured frame in texture mode has a non-null texture with +// a valid handle, and that the CPU data pointer is null (texture-only frame). +TEST_P(WgcCapturerTextureTest, CapturedFrameHasTexture) { + if (GetParam() == CaptureType::kWindow) { + SetUpForWindowCapture(); + } else { + SetUpForScreenCapture(); + } + + EXPECT_TRUE(capturer_->SelectSource(source_id_)); + capturer_->Start(this); + DoCapture(); + + ASSERT_NE(frame_, nullptr); + EXPECT_GT(frame_->size().width(), 0); + EXPECT_GT(frame_->size().height(), 0); + ASSERT_NE(frame_->texture(), nullptr); + EXPECT_NE(frame_->texture()->handle(), FrameTexture::kInvalidHandle); + // In texture mode, frame data should be null (no CPU copy). + EXPECT_EQ(frame_->data(), nullptr); +} + +// Verifies that consecutive captures in texture mode each produce frames with +// valid texture handles. +TEST_P(WgcCapturerTextureTest, MultipleCapturesProduceValidTextures) { + if (GetParam() == CaptureType::kWindow) { + SetUpForWindowCapture(); + } else { + SetUpForScreenCapture(); + } + + EXPECT_TRUE(capturer_->SelectSource(source_id_)); + capturer_->Start(this); + + for (int i = 0; i < 3; ++i) { + DoCapture(); + ASSERT_NE(frame_, nullptr); + ASSERT_NE(frame_->texture(), nullptr); + EXPECT_NE(frame_->texture()->handle(), FrameTexture::kInvalidHandle); + } +} + +// Verifies that the frame pool uses `kNumBuffersForTexture` buffers when +// texture mode is enabled (verified indirectly by successfully capturing more +// frames than kNumBuffers without starvation). +TEST_P(WgcCapturerTextureTest, CaptureDoesNotStarveWithTextureBuffers) { + if (GetParam() == CaptureType::kWindow) { + SetUpForWindowCapture(); + } else { + SetUpForScreenCapture(); + } + + EXPECT_TRUE(capturer_->SelectSource(source_id_)); + capturer_->Start(this); + + // Capture more frames than kNumBuffers to ensure the texture buffer count + // (kNumBuffersForTexture) is sufficient. + constexpr int kCaptures = WgcCaptureSession::kNumBuffersForTexture + 1; + for (int i = 0; i < kCaptures; ++i) { + DoCapture(); + ASSERT_NE(frame_, nullptr); + ASSERT_NE(frame_->texture(), nullptr); + } +} + +INSTANTIATE_TEST_SUITE_P(SourceAgnosticTexture, + WgcCapturerTextureTest, + ::testing::Values(CaptureType::kWindow, + CaptureType::kScreen)); + +// Window-specific texture mode tests. +class WgcCapturerTextureWindowTest : public WgcCapturerTextureTest { + public: + void SetUp() override { + com_initializer_ = + std::make_unique(ScopedCOMInitializer::kMTA); + ASSERT_TRUE(com_initializer_->Succeeded()); + + if (!IsWgcSupported(CaptureType::kWindow)) { + RTC_LOG(LS_INFO) << "Skipping WgcCapturerTextureWindowTest on " + "unsupported platforms."; + GTEST_SKIP(); + } + } +}; + +// Verifies that the cursor is embedded in the frame when texture mode is +// enabled (since we cannot do cursor composition on texture). +TEST_F(WgcCapturerTextureWindowTest, CursorEmbeddedInTextureMode) { + SetUpForWindowCapture(); + EXPECT_TRUE(capturer_->SelectSource(source_id_)); + capturer_->Start(this); + DoCapture(); + ASSERT_NE(frame_, nullptr); + // When texture mode is enabled, cursor embedding should be forced on, + // so the frame should indicate it may contain the cursor. + EXPECT_TRUE(frame_->may_contain_cursor()); +} + +// Verifies capture continues to work after a window resize in texture mode. +TEST_F(WgcCapturerTextureWindowTest, ResizeWindowMidTextureCapture) { + SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight); + EXPECT_TRUE(capturer_->SelectSource(source_id_)); + capturer_->Start(this); + DoCapture(); + ASSERT_NE(frame_, nullptr); + ASSERT_NE(frame_->texture(), nullptr); + + // Resize the window. + ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kLargeWindowHeight); + + // We need more captures to flush the buffers and pick up the new size. + constexpr int kNumCapturesToFlushTextureBuffers = + WgcCaptureSession::kNumBuffersForTexture * 2 + 1; + DoCapture(kNumCapturesToFlushTextureBuffers); + ASSERT_NE(frame_, nullptr); + ASSERT_NE(frame_->texture(), nullptr); + EXPECT_NE(frame_->texture()->handle(), FrameTexture::kInvalidHandle); + EXPECT_GT(frame_->size().width(), 0); + EXPECT_GT(frame_->size().height(), 0); +} + } // namespace webrtc diff --git a/modules/pacing/BUILD.gn b/modules/pacing/BUILD.gn index 11c8d02cc16..083b6ecf2ac 100644 --- a/modules/pacing/BUILD.gn +++ b/modules/pacing/BUILD.gn @@ -29,7 +29,6 @@ rtc_library("pacing") { ] deps = [ - "../../api:array_view", "../../api:field_trials_view", "../../api:rtp_headers", "../../api:rtp_packet_sender", @@ -87,7 +86,6 @@ if (rtc_include_tests) { deps = [ ":interval_budget", ":pacing", - "../../api:array_view", "../../api:field_trials", "../../api:rtp_headers", "../../api:sequence_checker", diff --git a/modules/pacing/DIR_METADATA b/modules/pacing/DIR_METADATA new file mode 100644 index 00000000000..a904cc9a09e --- /dev/null +++ b/modules/pacing/DIR_METADATA @@ -0,0 +1,3 @@ +buganizer_public: { + component_id: 1565318 # BWE +} \ No newline at end of file diff --git a/modules/pacing/OWNERS b/modules/pacing/OWNERS index f709476d438..fa476a42c64 100644 --- a/modules/pacing/OWNERS +++ b/modules/pacing/OWNERS @@ -1,5 +1,3 @@ -stefan@webrtc.org -mflodman@webrtc.org philipel@webrtc.org sprang@webrtc.org perkj@webrtc.org diff --git a/modules/pacing/pacing_controller.cc b/modules/pacing/pacing_controller.cc index 96fb3cbd0d5..8c2dda696e5 100644 --- a/modules/pacing/pacing_controller.cc +++ b/modules/pacing/pacing_controller.cc @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include "absl/cleanup/cleanup.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" @@ -102,7 +102,7 @@ PacingController::PacingController(Clock* clock, PacingController::~PacingController() = default; void PacingController::CreateProbeClusters( - ArrayView probe_cluster_configs) { + std::span probe_cluster_configs) { for (const ProbeClusterConfig probe_cluster_config : probe_cluster_configs) { prober_.CreateProbeCluster(probe_cluster_config); } diff --git a/modules/pacing/pacing_controller.h b/modules/pacing/pacing_controller.h index 0311423915d..6e244014cd5 100644 --- a/modules/pacing/pacing_controller.h +++ b/modules/pacing/pacing_controller.h @@ -17,10 +17,10 @@ #include #include #include +#include #include #include "absl/base/attributes.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/rtp_packet_sender.h" #include "api/transport/network_types.h" @@ -59,7 +59,7 @@ class PacingController { // have been updated. virtual void OnAbortedRetransmissions( uint32_t /* ssrc */, - ArrayView /* sequence_numbers */) {} + std::span /* sequence_numbers */) {} virtual std::optional GetRtxSsrcForMedia( uint32_t /* ssrc */) const { return std::nullopt; @@ -133,7 +133,7 @@ class PacingController { void EnqueuePacket(std::unique_ptr packet); void CreateProbeClusters( - ArrayView probe_cluster_configs); + std::span probe_cluster_configs); void Pause(); // Temporarily pause all sending. void Resume(); // Resume sending packets. diff --git a/modules/pacing/pacing_controller_unittest.cc b/modules/pacing/pacing_controller_unittest.cc index 84fa97c5c7f..0370248aca8 100644 --- a/modules/pacing/pacing_controller_unittest.cc +++ b/modules/pacing/pacing_controller_unittest.cc @@ -17,10 +17,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/field_trials.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" @@ -139,7 +139,7 @@ class MockPacingControllerCallback : public PacingController::PacketSender { MOCK_METHOD(size_t, SendPadding, (size_t target_size)); MOCK_METHOD(void, OnAbortedRetransmissions, - (uint32_t, ArrayView), + (uint32_t, std::span), (override)); MOCK_METHOD(std::optional, GetRtxSsrcForMedia, @@ -167,7 +167,7 @@ class MockPacketSender : public PacingController::PacketSender { (override)); MOCK_METHOD(void, OnAbortedRetransmissions, - (uint32_t, ArrayView), + (uint32_t, std::span), (override)); MOCK_METHOD(std::optional, GetRtxSsrcForMedia, @@ -205,7 +205,7 @@ class PacingControllerPadding : public PacingController::PacketSender { return packets; } - void OnAbortedRetransmissions(uint32_t, ArrayView) override {} + void OnAbortedRetransmissions(uint32_t, std::span) override {} std::optional GetRtxSsrcForMedia(uint32_t) const override { return std::nullopt; } @@ -265,7 +265,7 @@ class PacingControllerProbing : public PacingController::PacketSender { return packets; } - void OnAbortedRetransmissions(uint32_t, ArrayView) override {} + void OnAbortedRetransmissions(uint32_t, std::span) override {} std::optional GetRtxSsrcForMedia(uint32_t) const override { return std::nullopt; } diff --git a/modules/pacing/packet_router.cc b/modules/pacing/packet_router.cc index eb6b5240828..a8b4aea1868 100644 --- a/modules/pacing/packet_router.cc +++ b/modules/pacing/packet_router.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include "absl/functional/any_invocable.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/sequence_checker.h" #include "api/transport/network_types.h" @@ -291,7 +291,7 @@ std::vector> PacketRouter::GeneratePadding( void PacketRouter::OnAbortedRetransmissions( uint32_t ssrc, - ArrayView sequence_numbers) { + std::span sequence_numbers) { RTC_DCHECK_RUN_ON(&thread_checker_); auto it = send_modules_map_.find(ssrc); if (it != send_modules_map_.end()) { @@ -342,6 +342,18 @@ void PacketRouter::SendCombinedRtcpPacket( rtcp_sender->SendCombinedRtcpPacket(std::move(packets)); } +std::optional PacketRouter::SsrcOfFirstSender() { + RTC_DCHECK_RUN_ON(&thread_checker_); + // Prefer send modules. + for (RtpRtcpInterface* rtp_module : send_modules_list_) { + if (rtp_module->RTCP() == RtcpMode::kOff) { + continue; + } + return rtp_module->SSRC(); + } + return std::nullopt; +} + void PacketRouter::AddRembModuleCandidate( RtcpFeedbackSenderInterface* candidate_module, bool media_sender) { diff --git a/modules/pacing/packet_router.h b/modules/pacing/packet_router.h index 6321f2006fc..7b0ab394654 100644 --- a/modules/pacing/packet_router.h +++ b/modules/pacing/packet_router.h @@ -19,11 +19,11 @@ #include #include #include +#include #include #include #include "absl/functional/any_invocable.h" -#include "api/array_view.h" #include "api/sequence_checker.h" #include "api/transport/network_types.h" #include "api/units/data_size.h" @@ -79,7 +79,7 @@ class PacketRouter : public PacingController::PacketSender { DataSize size) override; void OnAbortedRetransmissions( uint32_t ssrc, - ArrayView sequence_numbers) override; + std::span sequence_numbers) override; std::optional GetRtxSsrcForMedia(uint32_t ssrc) const override; void OnBatchComplete() override; @@ -90,6 +90,10 @@ class PacketRouter : public PacingController::PacketSender { void SendCombinedRtcpPacket( std::vector> packets); + // Gives the SSRC of a sender associated with this packet router that + // can be used for sending RTCP messages. std::nullopt if no sender exists. + std::optional SsrcOfFirstSender(); + private: void AddRembModuleCandidate(RtcpFeedbackSenderInterface* candidate_module, bool media_sender); diff --git a/modules/pacing/packet_router_unittest.cc b/modules/pacing/packet_router_unittest.cc index ee853294ffe..d0143c23ace 100644 --- a/modules/pacing/packet_router_unittest.cc +++ b/modules/pacing/packet_router_unittest.cc @@ -20,7 +20,6 @@ #include "api/rtp_headers.h" #include "api/transport/network_types.h" #include "api/units/data_size.h" -#include "api/units/time_delta.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" @@ -29,7 +28,6 @@ #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "rtc_base/checks.h" -#include "rtc_base/fake_clock.h" #include "test/gmock.h" #include "test/gtest.h" @@ -676,7 +674,6 @@ TEST_F(PacketRouterTest, DuplicateRemovalOfSendModuleIgnored) { } TEST(PacketRouterRembTest, ChangeSendRtpModuleChangeRembSender) { - ScopedFakeClock clock; NiceMock rtp_send; NiceMock rtp_recv; PacketRouter packet_router; @@ -701,7 +698,6 @@ TEST(PacketRouterRembTest, ChangeSendRtpModuleChangeRembSender) { // Only register receiving modules and make sure we fallback to trigger a REMB // packet on this one. TEST(PacketRouterRembTest, NoSendingRtpModule) { - ScopedFakeClock clock; NiceMock rtp; PacketRouter packet_router; @@ -722,7 +718,6 @@ TEST(PacketRouterRembTest, NoSendingRtpModule) { } TEST(PacketRouterRembTest, NonCandidateSendRtpModuleNotUsedForRemb) { - ScopedFakeClock clock; PacketRouter packet_router; NiceMock module; @@ -740,7 +735,6 @@ TEST(PacketRouterRembTest, NonCandidateSendRtpModuleNotUsedForRemb) { } TEST(PacketRouterRembTest, CandidateSendRtpModuleUsedForRemb) { - ScopedFakeClock clock; PacketRouter packet_router; NiceMock module; @@ -758,7 +752,6 @@ TEST(PacketRouterRembTest, CandidateSendRtpModuleUsedForRemb) { } TEST(PacketRouterRembTest, NonCandidateReceiveRtpModuleNotUsedForRemb) { - ScopedFakeClock clock; PacketRouter packet_router; NiceMock module; @@ -776,7 +769,6 @@ TEST(PacketRouterRembTest, NonCandidateReceiveRtpModuleNotUsedForRemb) { } TEST(PacketRouterRembTest, CandidateReceiveRtpModuleUsedForRemb) { - ScopedFakeClock clock; PacketRouter packet_router; NiceMock module; @@ -795,7 +787,6 @@ TEST(PacketRouterRembTest, CandidateReceiveRtpModuleUsedForRemb) { TEST(PacketRouterRembTest, SendCandidatePreferredOverReceiveCandidate_SendModuleAddedFirst) { - ScopedFakeClock clock; PacketRouter packet_router; NiceMock send_module; NiceMock receive_module; @@ -822,7 +813,6 @@ TEST(PacketRouterRembTest, TEST(PacketRouterRembTest, SendCandidatePreferredOverReceiveCandidate_ReceiveModuleAddedFirst) { - ScopedFakeClock clock; PacketRouter packet_router; NiceMock send_module; NiceMock receive_module; @@ -840,7 +830,6 @@ TEST(PacketRouterRembTest, EXPECT_CALL(send_module, SetRemb(bitrate_estimate, ssrcs)); EXPECT_CALL(receive_module, SetRemb(_, _)).Times(0); - clock.AdvanceTime(TimeDelta::Millis(1000)); packet_router.SendRemb(bitrate_estimate, ssrcs); // Test tear-down @@ -849,7 +838,6 @@ TEST(PacketRouterRembTest, } TEST(PacketRouterRembTest, ReceiveModuleTakesOverWhenLastSendModuleRemoved) { - ScopedFakeClock clock; PacketRouter packet_router; NiceMock send_module; NiceMock receive_module; diff --git a/modules/pacing/task_queue_paced_sender_unittest.cc b/modules/pacing/task_queue_paced_sender_unittest.cc index ed70ee61104..9d96aa455a2 100644 --- a/modules/pacing/task_queue_paced_sender_unittest.cc +++ b/modules/pacing/task_queue_paced_sender_unittest.cc @@ -679,7 +679,7 @@ TEST(TaskQueuePacedSenderTest, PostedPacketsNotSendFromRemovePacketsForSsrc) { pacer.EnsureStarted(); auto encoder_queue = time_controller.GetTaskQueueFactory()->CreateTaskQueue( - "encoder_queue", TaskQueueFactory::Priority::HIGH); + "encoder_queue", TaskQueueFactory::Priority::kHigh); EXPECT_CALL(packet_router, SendPacket).Times(5); encoder_queue->PostTask([&pacer] { diff --git a/modules/portal/pipewire.sigs b/modules/portal/pipewire.sigs index cab90280ef4..f922048548a 100644 --- a/modules/portal/pipewire.sigs +++ b/modules/portal/pipewire.sigs @@ -54,5 +54,11 @@ pw_context *pw_context_new(pw_loop *main_loop, pw_properties *props, size_t user pw_core * pw_context_connect(pw_context *context, pw_properties *properties, size_t user_data_size); pw_core * pw_context_connect_fd(pw_context *context, int fd, pw_properties *properties, size_t user_data_size); +// node.h +struct pw_node_info * pw_node_info_update(struct pw_node_info *info, const struct pw_node_info *update); +void pw_node_info_free(struct pw_node_info *info); + // proxy.h void pw_proxy_destroy(struct pw_proxy *proxy); +void pw_proxy_add_listener(struct pw_proxy *proxy, struct spa_hook *listener, const struct pw_proxy_events *events, void *data); +int pw_proxy_sync(struct pw_proxy *proxy, int seq); diff --git a/modules/portal/pipewire_utils.cc b/modules/portal/pipewire_utils.cc index 91a2ae69513..850726cc589 100644 --- a/modules/portal/pipewire_utils.cc +++ b/modules/portal/pipewire_utils.cc @@ -12,7 +12,15 @@ #include +#include +#include +#include +#include +#include + #include "rtc_base/sanitizer.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/string_to_number.h" #if defined(WEBRTC_DLOPEN_PIPEWIRE) #include "modules/portal/pipewire_stubs.h" @@ -20,6 +28,45 @@ namespace webrtc { +constexpr PipeWireVersion kReentrantDeinitMinVersion = {.major = 0, + .minor = 3, + .micro = 49}; + +PipeWireVersion PipeWireVersion::Parse(const std::string_view& version) { + std::vector parsed_version = split(version, '.'); + + if (parsed_version.size() != 3) { + return {}; + } + + std::optional major = StringToNumber(parsed_version.at(0)); + std::optional minor = StringToNumber(parsed_version.at(1)); + std::optional micro = StringToNumber(parsed_version.at(2)); + + // Return invalid version if we failed to parse it + if (!major || !minor || !micro) { + return {}; + } + + return {.major = major.value(), + .minor = minor.value(), + .micro = micro.value(), + .full_version = std::string(version)}; +} + +bool PipeWireVersion::operator>=(const PipeWireVersion& other) { + if (!major && !minor && !micro) { + return false; + } + + return std::tie(major, minor, micro) >= + std::tie(other.major, other.minor, other.micro); +} + +std::string_view PipeWireVersion::ToStringView() const { + return full_version; +} + RTC_NO_SANITIZE("cfi-icall") bool InitializePipeWire() { #if defined(WEBRTC_DLOPEN_PIPEWIRE) @@ -57,7 +104,11 @@ PipeWireInitializer::PipeWireInitializer() { RTC_NO_SANITIZE("cfi-icall") PipeWireInitializer::~PipeWireInitializer() { - pw_deinit(); + PipeWireVersion pw_client_version = + PipeWireVersion::Parse(pw_get_library_version()); + if (pw_client_version >= kReentrantDeinitMinVersion) { + pw_deinit(); + } } } // namespace webrtc diff --git a/modules/portal/pipewire_utils.h b/modules/portal/pipewire_utils.h index c22f296f5cf..d0734c19be0 100644 --- a/modules/portal/pipewire_utils.h +++ b/modules/portal/pipewire_utils.h @@ -17,6 +17,8 @@ #include #include +#include +#include // static struct dma_buf_sync { @@ -34,6 +36,20 @@ namespace webrtc { constexpr int kInvalidPipeWireFd = -1; +struct PipeWireVersion { + static PipeWireVersion Parse(const std::string_view& version); + + // Returns whether current version is newer or same as required version + bool operator>=(const PipeWireVersion& other); + + std::string_view ToStringView() const; + + int major = 0; + int minor = 0; + int micro = 0; + std::string full_version; +}; + // Prepare PipeWire so that it is ready to be used. If it needs to be dlopen'd // this will do so. Note that this does not guarantee a PipeWire server is // running nor does it establish a connection to one. diff --git a/modules/remote_bitrate_estimator/DIR_METADATA b/modules/remote_bitrate_estimator/DIR_METADATA new file mode 100644 index 00000000000..a904cc9a09e --- /dev/null +++ b/modules/remote_bitrate_estimator/DIR_METADATA @@ -0,0 +1,3 @@ +buganizer_public: { + component_id: 1565318 # BWE +} \ No newline at end of file diff --git a/modules/remote_bitrate_estimator/OWNERS b/modules/remote_bitrate_estimator/OWNERS index 993e2fd77b1..9db73ffca9c 100644 --- a/modules/remote_bitrate_estimator/OWNERS +++ b/modules/remote_bitrate_estimator/OWNERS @@ -1,5 +1,3 @@ danilchap@webrtc.org -stefan@webrtc.org terelius@webrtc.org -mflodman@webrtc.org perkj@webrtc.org diff --git a/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc b/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc index 574e230dcc1..fd11ae2f2fa 100644 --- a/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc +++ b/modules/remote_bitrate_estimator/congestion_control_feedback_generator.cc @@ -112,7 +112,9 @@ void CongestionControlFeedbackGenerator::SendFeedback(Timestamp now) { void CongestionControlFeedbackGenerator::CalculateNextPossibleSendTime( DataSize feedback_size, Timestamp now) { - TimeDelta time_since_last_sent = now - last_feedback_sent_time_; + TimeDelta time_since_last_sent = last_feedback_sent_time_.IsFinite() + ? now - last_feedback_sent_time_ + : TimeDelta::Zero(); DataSize debt_payed = time_since_last_sent * kMaxFeedbackRate; send_rate_debt_ = debt_payed > send_rate_debt_ ? DataSize::Zero() : send_rate_debt_ - debt_payed; diff --git a/modules/remote_bitrate_estimator/congestion_control_feedback_generator.h b/modules/remote_bitrate_estimator/congestion_control_feedback_generator.h index a5225c0f499..99c8f2fdb8d 100644 --- a/modules/remote_bitrate_estimator/congestion_control_feedback_generator.h +++ b/modules/remote_bitrate_estimator/congestion_control_feedback_generator.h @@ -89,8 +89,7 @@ class CongestionControlFeedbackGenerator std::map feedback_trackers_; - // std::vector packets_; - Timestamp last_feedback_sent_time_ = Timestamp::Zero(); + Timestamp last_feedback_sent_time_ = Timestamp::MinusInfinity(); std::optional first_arrival_time_since_feedback_; bool marker_bit_seen_ = false; Timestamp next_possible_feedback_send_time_ = Timestamp::Zero(); diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index 2ff9183b099..3915a10786e 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -113,14 +113,11 @@ rtc_library("rtp_rtcp_format") { deps = [ ":leb128", "..:module_api_public", - "../../api:array_view", "../../api:function_view", "../../api:refcountedbase", "../../api:rtp_headers", - "../../api:rtp_packet_sender", # For compatibility with downstream projects "../../api:rtp_parameters", "../../api:scoped_refptr", - "../../api/audio_codecs:audio_codecs_api", "../../api/transport:ecn_marking", "../../api/transport:network_control", "../../api/transport/rtp:corruption_detection_message", @@ -143,7 +140,6 @@ rtc_library("rtp_rtcp_format") { "../../rtc_base:safe_conversions", "../../rtc_base:stringutils", "../../system_wrappers", - "../video_coding:codec_globals_headers", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/container:inlined_vector", @@ -215,7 +211,6 @@ rtc_library("rtp_rtcp") { "source/rtp_header_extension_size.h", "source/rtp_packet_history.cc", "source/rtp_packet_history.h", - "source/rtp_packet_send_info.cc", "source/rtp_packetizer_av1.cc", "source/rtp_packetizer_av1.h", "source/rtp_rtcp_config.h", @@ -280,7 +275,6 @@ rtc_library("rtp_rtcp") { ":rtp_video_header", "..:module_api_public", "..:module_fec_api", - "../../api:array_view", "../../api:field_trials_view", "../../api:frame_transformer_interface", "../../api:function_view", @@ -292,7 +286,6 @@ rtc_library("rtp_rtcp") { "../../api:scoped_refptr", "../../api:sequence_checker", "../../api:transport_api", - "../../api/audio_codecs:audio_codecs_api", "../../api/crypto:frame_encryptor_interface", "../../api/environment", "../../api/rtc_event_log", @@ -371,7 +364,6 @@ rtc_library("ntp_time_util") { ] deps = [ "../../api/units:time_delta", - "../../rtc_base:checks", "../../rtc_base:divide_round", "../../rtc_base:safe_conversions", "../../rtc_base:timeutils", @@ -393,7 +385,6 @@ rtc_library("rtcp_transceiver") { ":ntp_time_util", ":rtp_rtcp", ":rtp_rtcp_format", - "../../api:array_view", "../../api:rtp_headers", "../../api/task_queue", "../../api/units:data_rate", @@ -405,8 +396,6 @@ rtc_library("rtcp_transceiver") { "../../rtc_base:copy_on_write_buffer", "../../rtc_base:divide_round", "../../rtc_base:logging", - "../../rtc_base:rtc_event", - "../../rtc_base:timeutils", "../../rtc_base/containers:flat_map", "../../rtc_base/task_utils:repeating_task", "../../system_wrappers", @@ -431,7 +420,7 @@ rtc_library("rtp_video_header") { "../../api/video:video_frame_type", "../../api/video:video_rtp_headers", "../../api/video/corruption_detection:frame_instrumentation_data", - "../../modules/video_coding:codec_globals_headers", + "../video_coding:codec_globals_headers", "//third_party/abseil-cpp/absl/container:inlined_vector", ] } @@ -480,14 +469,12 @@ rtc_library("mock_rtp_rtcp") { ":rtp_rtcp", ":rtp_rtcp_format", "..:module_fec_api", - "../../api:array_view", "../../api:rtp_headers", "../../api/transport:network_control", "../../api/units:data_rate", "../../api/units:time_delta", "../../api/units:timestamp", "../../api/video:video_bitrate_allocation", - "../../rtc_base:checks", "../../test:test_support", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -503,7 +490,7 @@ rtc_library("rtp_packetizer_av1_test_helper") { if (rtc_include_tests) { if (!build_with_chromium) { - rtc_executable("test_packet_masks_metrics") { + rtc_test("test_packet_masks_metrics") { testonly = true sources = [ @@ -514,14 +501,12 @@ if (rtc_include_tests) { deps = [ ":rtp_rtcp", "..:module_fec_api", - "../../api:array_view", "../../rtc_base:checks", "../../test:fileutils", - "../../test:test_main", "../../test:test_support", "//testing/gtest", ] - } # test_packet_masks_metrics + } } rtc_library("rtp_rtcp_modules_tests") { @@ -602,7 +587,6 @@ if (rtc_include_tests) { "source/rtp_header_extension_map_unittest.cc", "source/rtp_header_extension_size_unittest.cc", "source/rtp_packet_history_unittest.cc", - "source/rtp_packet_send_info_unittest.cc", "source/rtp_packet_unittest.cc", "source/rtp_packetizer_av1_unittest.cc", "source/rtp_rtcp_impl2_unittest.cc", @@ -647,10 +631,7 @@ if (rtc_include_tests) { ":rtp_video_header", ":rtp_video_header_unittest", "..:module_fec_api", - "../../api:array_view", "../../api:create_time_controller", - "../../api:field_trials", - "../../api:field_trials_registry", "../../api:field_trials_view", "../../api:frame_transformer_factory", "../../api:frame_transformer_interface", @@ -668,7 +649,6 @@ if (rtc_include_tests) { "../../api:transport_api", "../../api/environment", "../../api/environment:environment_factory", - "../../api/rtc_event_log", "../../api/task_queue", "../../api/transport:ecn_marking", "../../api/transport:network_control", @@ -690,35 +670,27 @@ if (rtc_include_tests) { "../../api/video:video_layers_allocation", "../../api/video:video_rtp_headers", "../../api/video/corruption_detection:frame_instrumentation_data", - "../../api/video_codecs:video_codecs_api", "../../call:rtp_interfaces", "../../call:rtp_receiver", - "../../call:video_receive_stream_api", "../../common_video", - "../../common_video/generic_frame_descriptor", "../../common_video/test:utilities", - "../../logging:mocks", - "../../rtc_base:bit_buffer", "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:copy_on_write_buffer", "../../rtc_base:logging", "../../rtc_base:random", "../../rtc_base:rate_limiter", - "../../rtc_base:rtc_base_tests_utils", "../../rtc_base:rtc_event", "../../rtc_base:rtc_numerics", "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", "../../rtc_base:threading", - "../../rtc_base:timeutils", "../../system_wrappers", "../../system_wrappers:metrics", "../../test:create_test_field_trials", "../../test:mock_transport", "../../test:near_matcher", "../../test:rtp_test_utils", - "../../test:run_loop", "../../test:test_support", "../../test/time_controller", "../audio_coding:audio_coding_module_typedefs", @@ -735,18 +707,11 @@ if (rtc_include_tests) { testonly = true sources = [ "source/frame_transformer_factory_unittest.cc" ] deps = [ - "../../api:array_view", "../../api:frame_transformer_factory", - "../../api:mock_frame_transformer", "../../api:mock_transformable_audio_frame", "../../api:mock_transformable_video_frame", - "../../api:transport_api", "../../api/video:video_frame_metadata", - "../../call:video_receive_stream_api", - "../../modules/rtp_rtcp", - "../../rtc_base:rtc_event", "../../test:test_support", - "../../video", "//third_party/abseil-cpp/absl/memory", ] } diff --git a/modules/rtp_rtcp/DIR_METADATA b/modules/rtp_rtcp/DIR_METADATA new file mode 100644 index 00000000000..783c9b89583 --- /dev/null +++ b/modules/rtp_rtcp/DIR_METADATA @@ -0,0 +1,3 @@ +buganizer_public: { + component_id: 1565889 # Network +} \ No newline at end of file diff --git a/modules/rtp_rtcp/OWNERS b/modules/rtp_rtcp/OWNERS index 47d12c401fa..bc1dea8ba48 100644 --- a/modules/rtp_rtcp/OWNERS +++ b/modules/rtp_rtcp/OWNERS @@ -1,6 +1,4 @@ -stefan@webrtc.org henrik.lundin@webrtc.org -mflodman@webrtc.org asapersson@webrtc.org danilchap@webrtc.org sprang@webrtc.org diff --git a/modules/rtp_rtcp/g3doc/remote_timestamp_estimation.md b/modules/rtp_rtcp/g3doc/remote_timestamp_estimation.md new file mode 100644 index 00000000000..02d727d37c0 --- /dev/null +++ b/modules/rtp_rtcp/g3doc/remote_timestamp_estimation.md @@ -0,0 +1,39 @@ +# NTP Time Estimation based on RRTR/DLRR + +## Overview +The estimation of NTP time offset between a local system and a remote system using RTCP XR (Extended Reports) RRTR and DLRR blocks involves two main steps: Round-Trip Time (RTT) calculation and NTP Offset estimation. + +## 1. RTT Calculation using RRTR/DLRR +The flow is as follows (RFC 3611): +1. **Local Receiver** sends an **RRTR** (Receiver Reference Time Report) block containing its current NTP timestamp ($T_1$). +2. **Remote Sender** receives the RRTR at $T_2$. +3. **Remote Sender** prepares a **DLRR** (Delay since Last Receiver Report) block. It includes: + * `last_rr`: The middle 32 bits of the $T_1$ timestamp from the received RRTR. + * `delay_since_last_rr`: The time elapsed between receiving the RRTR and sending the DLRR ($D = T_3 - T_2$). +4. **Local Receiver** receives the DLRR at $T_4$. + +The **RTT** is calculated in `RTCPReceiver::HandleXrDlrrReportBlock` (in `modules/rtp_rtcp/source/rtcp_receiver.cc`) as: +$$RTT = (T_4 - T_1) - ext{delay\_since\_last\_rr}$$ +In code, this uses compact NTP representation (1/2^16 seconds resolution). + +## 2. NTP Offset Estimation +Once the RTT is known, it is used along with Sender Reports (SR) to estimate the NTP offset between the remote and local clocks. This logic is implemented in `RemoteNtpTimeEstimator::UpdateRtcpTimestamp` (in `modules/rtp_rtcp/source/remote_ntp_time_estimator.cc`). + +1. **SR Data**: The Sender Report provides the remote NTP time ($T_{ ext{remote\_send}}$) and the corresponding RTP timestamp. +2. **Delivery Time Estimation**: Assuming network symmetry, the one-way delivery time is estimated as: + $$DeliverTime = RTT / 2$$ +3. **Receiver Arrival Time**: The local system records the NTP time when the SR was received ($T_{ ext{local\_arrival}}$). +4. **Clock Offset Calculation**: + $$ ext{RemoteToLocalOffset} = (T_{ ext{local\_arrival}} - T_{ ext{remote\_send}}) - DeliverTime$$ + +This offset is inserted into a smoothing filter (`ntp_clocks_offset_estimator_`). + +## 3. Usage +The estimated offset is used by: +* `RemoteNtpTimeEstimator::EstimateNtp`: To convert RTP timestamps to receiver-local NTP time. +* `CaptureClockOffsetUpdater`: To adjust `Absolute Capture Time` extensions in RTP packets, allowing the receiver to know the exact capture time in its own NTP clock. + +## Relevant Code Locations +* `modules/rtp_rtcp/source/rtcp_receiver.cc`: `HandleXrDlrrReportBlock` - RTT calculation logic. +* `modules/rtp_rtcp/source/remote_ntp_time_estimator.cc`: `UpdateRtcpTimestamp` - NTP offset estimation. +* `modules/rtp_rtcp/source/ntp_time_util.h`: NTP conversion helpers like `CompactNtp`, `ToNtpUnits`, and `CompactNtpRttToTimeDelta`. diff --git a/modules/rtp_rtcp/include/flexfec_sender.h b/modules/rtp_rtcp/include/flexfec_sender.h index 75006612f68..ff1855d6348 100644 --- a/modules/rtp_rtcp/include/flexfec_sender.h +++ b/modules/rtp_rtcp/include/flexfec_sender.h @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/rtp_parameters.h" #include "api/units/data_rate.h" @@ -50,7 +50,7 @@ class FlexfecSender : public VideoFecGenerator { uint32_t protected_media_ssrc, absl::string_view mid, const std::vector& rtp_header_extensions, - ArrayView extension_sizes, + std::span extension_sizes, const RtpState* rtp_state); ~FlexfecSender() override; diff --git a/modules/rtp_rtcp/include/rtp_header_extension_map.h b/modules/rtp_rtcp/include/rtp_header_extension_map.h index 2975c4aebb8..9ebfcf7ec6d 100644 --- a/modules/rtp_rtcp/include/rtp_header_extension_map.h +++ b/modules/rtp_rtcp/include/rtp_header_extension_map.h @@ -11,11 +11,11 @@ #ifndef MODULES_RTP_RTCP_INCLUDE_RTP_HEADER_EXTENSION_MAP_H_ #define MODULES_RTP_RTCP_INCLUDE_RTP_HEADER_EXTENSION_MAP_H_ -#include - +#include +#include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/checks.h" @@ -29,9 +29,9 @@ class RtpHeaderExtensionMap { RtpHeaderExtensionMap(); explicit RtpHeaderExtensionMap(bool extmap_allow_mixed); - explicit RtpHeaderExtensionMap(ArrayView extensions); + explicit RtpHeaderExtensionMap(std::span extensions); - void Reset(ArrayView extensions); + void Reset(std::span extensions); template bool Register(int id) { @@ -65,7 +65,7 @@ class RtpHeaderExtensionMap { private: bool Register(int id, RTPExtensionType type, absl::string_view uri); - uint8_t ids_[kRtpExtensionNumberOfExtensions]; + std::array ids_; bool extmap_allow_mixed_; }; diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h index 6a8b521be06..a8b235401c8 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h +++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h @@ -18,11 +18,11 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" @@ -128,6 +128,11 @@ enum RtxMode { const size_t kRtxHeaderSize = 2; +// Special values for SSRC, used when sending receiver reports from +// receive-only endpoints. +inline constexpr uint32_t kFallbackRtcpSsrcForVideo = 1; +inline constexpr uint32_t kFallbackRtcpSsrcForAudio = 0xFA17FA17; + struct RtpState { uint16_t sequence_number = 0; uint32_t start_timestamp = 0; @@ -178,7 +183,7 @@ class NetworkLinkRtcpObserver { // Called on an RTCP packet with sender or receiver reports with non zero // report blocks. Report blocks are combined from all reports into one array. virtual void OnReport(Timestamp /* receive_time */, - ArrayView /* report_blocks */) {} + std::span /* report_blocks */) {} virtual void OnRttUpdate(Timestamp /* receive_time */, TimeDelta /* rtt */) {} }; @@ -193,32 +198,12 @@ enum class RtpPacketMediaType : size_t { // Again, don't forget to update `kNumMediaTypes` if you add another value! }; -struct RtpPacketSendInfo { - static RtpPacketSendInfo From(const RtpPacketToSend& rtp_packet_to_send, - const PacedPacketInfo& pacing_info); - - uint16_t transport_sequence_number = 0; - std::optional media_ssrc; - uint16_t rtp_sequence_number = 0; // Only valid if `media_ssrc` is set. - uint32_t rtp_timestamp = 0; - size_t length = 0; - std::optional packet_type; - PacedPacketInfo pacing_info; -}; - class NetworkStateEstimateObserver { public: virtual void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) = 0; virtual ~NetworkStateEstimateObserver() = default; }; -class TransportFeedbackObserver { - public: - virtual ~TransportFeedbackObserver() = default; - - virtual void OnAddPacket(const RtpPacketSendInfo& packet_info) = 0; -}; - // Interface for PacketRouter to send rtcp feedback on behalf of // congestion controller. // TODO(bugs.webrtc.org/8239): Remove and use RtcpTransceiver directly diff --git a/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h b/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h index f2775b79d08..80dc5e971ce 100644 --- a/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h +++ b/modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h @@ -11,7 +11,8 @@ #ifndef MODULES_RTP_RTCP_MOCKS_MOCK_NETWORK_LINK_RTCP_OBSERVER_H_ #define MODULES_RTP_RTCP_MOCKS_MOCK_NETWORK_LINK_RTCP_OBSERVER_H_ -#include "api/array_view.h" +#include + #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -45,7 +46,7 @@ class MockNetworkLinkRtcpObserver : public NetworkLinkRtcpObserver { MOCK_METHOD(void, OnReport, (Timestamp receive_time, - ArrayView report_blocks), + std::span report_blocks), (override)); }; diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 46163f7f35d..dbfc386ebfa 100644 --- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/transport/network_types.h" #include "api/units/time_delta.h" @@ -38,7 +38,7 @@ class MockRtpRtcpInterface : public RtpRtcpInterface { public: MOCK_METHOD(void, IncomingRtcpPacket, - (ArrayView packet), + (std::span packet), (override)); MOCK_METHOD(void, SetRemoteSSRC, (uint32_t ssrc), (override)); MOCK_METHOD(void, SetLocalSsrc, (uint32_t ssrc), (override)); @@ -120,11 +120,11 @@ class MockRtpRtcpInterface : public RtpRtcpInterface { (override)); MOCK_METHOD(void, OnAbortedRetransmissions, - (ArrayView), + (std::span), (override)); MOCK_METHOD(void, OnPacketsAcknowledged, - (ArrayView), + (std::span), (override)); MOCK_METHOD(std::vector>, GeneratePadding, @@ -132,7 +132,7 @@ class MockRtpRtcpInterface : public RtpRtcpInterface { (override)); MOCK_METHOD(std::vector, GetSentRtpPacketInfos, - (ArrayView sequence_numbers), + (std::span sequence_numbers), (const, override)); MOCK_METHOD(size_t, ExpectedPerPacketOverhead, (), (const, override)); MOCK_METHOD(void, OnPacketSendingThreadSwitched, (), (override)); diff --git a/modules/rtp_rtcp/source/absolute_capture_time_interpolator.cc b/modules/rtp_rtcp/source/absolute_capture_time_interpolator.cc index ef7aa4ed6cd..5745a316663 100644 --- a/modules/rtp_rtcp/source/absolute_capture_time_interpolator.cc +++ b/modules/rtp_rtcp/source/absolute_capture_time_interpolator.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -31,7 +31,7 @@ AbsoluteCaptureTimeInterpolator::AbsoluteCaptureTimeInterpolator(Clock* clock) uint32_t AbsoluteCaptureTimeInterpolator::GetSource( uint32_t ssrc, - ArrayView csrcs) { + std::span csrcs) { if (csrcs.empty()) { return ssrc; } diff --git a/modules/rtp_rtcp/source/absolute_capture_time_interpolator.h b/modules/rtp_rtcp/source/absolute_capture_time_interpolator.h index c6212a86468..b9a5c49761c 100644 --- a/modules/rtp_rtcp/source/absolute_capture_time_interpolator.h +++ b/modules/rtp_rtcp/source/absolute_capture_time_interpolator.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -43,7 +43,7 @@ class AbsoluteCaptureTimeInterpolator { explicit AbsoluteCaptureTimeInterpolator(Clock* clock); // Returns the source (i.e. SSRC or CSRC) of the capture system. - static uint32_t GetSource(uint32_t ssrc, ArrayView csrcs); + static uint32_t GetSource(uint32_t ssrc, std::span csrcs); // Returns a received header extension, an interpolated header extension, or // `std::nullopt` if it's not possible to interpolate a header extension. diff --git a/modules/rtp_rtcp/source/absolute_capture_time_interpolator_unittest.cc b/modules/rtp_rtcp/source/absolute_capture_time_interpolator_unittest.cc index a99d06a7a11..a8531c6e52f 100644 --- a/modules/rtp_rtcp/source/absolute_capture_time_interpolator_unittest.cc +++ b/modules/rtp_rtcp/source/absolute_capture_time_interpolator_unittest.cc @@ -31,7 +31,7 @@ using testing::Le; TEST(AbsoluteCaptureTimeInterpolatorTest, GetSourceWithoutCsrcs) { constexpr uint32_t kSsrc = 12; - EXPECT_EQ(AbsoluteCaptureTimeInterpolator::GetSource(kSsrc, nullptr), kSsrc); + EXPECT_EQ(AbsoluteCaptureTimeInterpolator::GetSource(kSsrc, {}), kSsrc); } TEST(AbsoluteCaptureTimeInterpolatorTest, GetSourceWithCsrcs) { diff --git a/modules/rtp_rtcp/source/absolute_capture_time_sender.cc b/modules/rtp_rtcp/source/absolute_capture_time_sender.cc index 61e321f6bb6..bf621fe4547 100644 --- a/modules/rtp_rtcp/source/absolute_capture_time_sender.cc +++ b/modules/rtp_rtcp/source/absolute_capture_time_sender.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/units/timestamp.h" #include "modules/rtp_rtcp/source/absolute_capture_time_interpolator.h" @@ -32,7 +32,7 @@ AbsoluteCaptureTimeSender::AbsoluteCaptureTimeSender(Clock* clock) : clock_(clock) {} uint32_t AbsoluteCaptureTimeSender::GetSource(uint32_t ssrc, - ArrayView csrcs) { + std::span csrcs) { return AbsoluteCaptureTimeInterpolator::GetSource(ssrc, csrcs); } diff --git a/modules/rtp_rtcp/source/absolute_capture_time_sender.h b/modules/rtp_rtcp/source/absolute_capture_time_sender.h index 352754c7b71..b7f885f430f 100644 --- a/modules/rtp_rtcp/source/absolute_capture_time_sender.h +++ b/modules/rtp_rtcp/source/absolute_capture_time_sender.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -48,7 +48,7 @@ class AbsoluteCaptureTimeSender { explicit AbsoluteCaptureTimeSender(Clock* clock); // Returns the source (i.e. SSRC or CSRC) of the capture system. - static uint32_t GetSource(uint32_t ssrc, ArrayView csrcs); + static uint32_t GetSource(uint32_t ssrc, std::span csrcs); // Returns value to write into AbsoluteCaptureTime RTP header extension to be // sent, or `std::nullopt` if the header extension shouldn't be attached to diff --git a/modules/rtp_rtcp/source/active_decode_targets_helper.cc b/modules/rtp_rtcp/source/active_decode_targets_helper.cc index c1150a571b2..901ed6ec085 100644 --- a/modules/rtp_rtcp/source/active_decode_targets_helper.cc +++ b/modules/rtp_rtcp/source/active_decode_targets_helper.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -26,7 +26,7 @@ namespace { // missing. That assumptions allows a simple detection when previous frame is // part of a chain. std::bitset<32> LastSendOnChain(int frame_diff, - ArrayView chain_diffs) { + std::span chain_diffs) { std::bitset<32> bitmask = 0; for (size_t i = 0; i < chain_diffs.size(); ++i) { if (frame_diff == chain_diffs[i]) { @@ -44,7 +44,7 @@ std::bitset<32> AllActive(size_t num) { // Returns bitmask of chains that protect at least one active decode target. std::bitset<32> ActiveChains( - ArrayView decode_target_protected_by_chain, + std::span decode_target_protected_by_chain, int num_chains, std::bitset<32> active_decode_targets) { std::bitset<32> active_chains = 0; @@ -62,11 +62,11 @@ std::bitset<32> ActiveChains( } // namespace void ActiveDecodeTargetsHelper::OnFrame( - ArrayView decode_target_protected_by_chain, + std::span decode_target_protected_by_chain, std::bitset<32> active_decode_targets, bool is_keyframe, int64_t frame_id, - ArrayView chain_diffs) { + std::span chain_diffs) { const int num_chains = chain_diffs.size(); if (num_chains == 0) { // Avoid printing the warning diff --git a/modules/rtp_rtcp/source/active_decode_targets_helper.h b/modules/rtp_rtcp/source/active_decode_targets_helper.h index 293abafb73d..8e432ee8590 100644 --- a/modules/rtp_rtcp/source/active_decode_targets_helper.h +++ b/modules/rtp_rtcp/source/active_decode_targets_helper.h @@ -15,8 +15,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -34,11 +33,11 @@ class ActiveDecodeTargetsHelper { // Decides if active decode target bitmask should be attached to the frame // that is about to be sent. - void OnFrame(ArrayView decode_target_protected_by_chain, + void OnFrame(std::span decode_target_protected_by_chain, std::bitset<32> active_decode_targets, bool is_keyframe, int64_t frame_id, - ArrayView chain_diffs); + std::span chain_diffs); // Returns active decode target to attach to the dependency descriptor. std::optional ActiveDecodeTargetsBitmask() const { diff --git a/modules/rtp_rtcp/source/active_decode_targets_helper_unittest.cc b/modules/rtp_rtcp/source/active_decode_targets_helper_unittest.cc index e87ae1719de..5062a075d1b 100644 --- a/modules/rtp_rtcp/source/active_decode_targets_helper_unittest.cc +++ b/modules/rtp_rtcp/source/active_decode_targets_helper_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -228,8 +228,8 @@ TEST(ActiveDecodeTargetsHelperTest, ReturnsBitmaskWhenChanged) { } TEST(ActiveDecodeTargetsHelperTest, ReturnsNulloptWhenChainsAreNotUsed) { - const ArrayView kDecodeTargetProtectedByChain; - const ArrayView kNoChainDiffs; + const std::span kDecodeTargetProtectedByChain; + const std::span kNoChainDiffs; ActiveDecodeTargetsHelper helper; helper.OnFrame(kDecodeTargetProtectedByChain, /*active_decode_targets=*/kAll, diff --git a/modules/rtp_rtcp/source/corruption_detection_extension.cc b/modules/rtp_rtcp/source/corruption_detection_extension.cc index 934fb2c4f1b..4913dd1c9f8 100644 --- a/modules/rtp_rtcp/source/corruption_detection_extension.cc +++ b/modules/rtp_rtcp/source/corruption_detection_extension.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/transport/rtp/corruption_detection_message.h" namespace webrtc { @@ -30,7 +30,7 @@ constexpr double kMaxValueForStdDev = 40.0; // A description of the extension can be found at // http://www.webrtc.org/experiments/rtp-hdrext/corruption-detection -bool CorruptionDetectionExtension::Parse(ArrayView data, +bool CorruptionDetectionExtension::Parse(std::span data, CorruptionDetectionMessage* message) { if (message == nullptr) { return false; @@ -49,13 +49,13 @@ bool CorruptionDetectionExtension::Parse(ArrayView data, uint8_t channel_error_thresholds = data[2]; message->luma_error_threshold_ = channel_error_thresholds >> 4; message->chroma_error_threshold_ = channel_error_thresholds & 0xF; - message->sample_values_.assign(data.cbegin() + kConfigurationBytes, - data.cend()); + message->sample_values_.assign(data.begin() + kConfigurationBytes, + data.end()); return true; } bool CorruptionDetectionExtension::Write( - ArrayView data, + std::span data, const CorruptionDetectionMessage& message) { if (data.size() != ValueSize(message) || data.size() > kMaxValueSizeBytes) { return false; @@ -72,7 +72,7 @@ bool CorruptionDetectionExtension::Write( std::round(message.std_dev() / kMaxValueForStdDev * 255.0)); data[2] = (message.luma_error_threshold() << 4) | (message.chroma_error_threshold() & 0xF); - ArrayView sample_values = data.subview(kConfigurationBytes); + std::span sample_values = data.subspan(kConfigurationBytes); for (size_t i = 0; i < message.sample_values().size(); ++i) { sample_values[i] = std::floor(message.sample_values()[i]); } diff --git a/modules/rtp_rtcp/source/corruption_detection_extension.h b/modules/rtp_rtcp/source/corruption_detection_extension.h index 4dc6c55b39c..ee3e14b8221 100644 --- a/modules/rtp_rtcp/source/corruption_detection_extension.h +++ b/modules/rtp_rtcp/source/corruption_detection_extension.h @@ -13,9 +13,9 @@ #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "api/transport/rtp/corruption_detection_message.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -38,9 +38,9 @@ class CorruptionDetectionExtension { static constexpr absl::string_view Uri() { return RtpExtension::kCorruptionDetectionUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, CorruptionDetectionMessage* message); - static bool Write(ArrayView data, + static bool Write(std::span data, const CorruptionDetectionMessage& message); // Size of the header extension in bytes. static size_t ValueSize(const CorruptionDetectionMessage& message); diff --git a/modules/rtp_rtcp/source/dtmf_queue.cc b/modules/rtp_rtcp/source/dtmf_queue.cc index 3f054c7177b..53f429b5dd9 100644 --- a/modules/rtp_rtcp/source/dtmf_queue.cc +++ b/modules/rtp_rtcp/source/dtmf_queue.cc @@ -15,11 +15,12 @@ #include "rtc_base/checks.h" #include "rtc_base/synchronization/mutex.h" +namespace webrtc { + namespace { constexpr size_t kDtmfOutbandMax = 20; } // namespace -namespace webrtc { DtmfQueue::DtmfQueue() {} DtmfQueue::~DtmfQueue() {} diff --git a/modules/rtp_rtcp/source/fec_private_tables_bursty.cc b/modules/rtp_rtcp/source/fec_private_tables_bursty.cc index d0b9da4a3f0..84a4857e7be 100644 --- a/modules/rtp_rtcp/source/fec_private_tables_bursty.cc +++ b/modules/rtp_rtcp/source/fec_private_tables_bursty.cc @@ -12,6 +12,8 @@ #include +namespace webrtc { + namespace { // clang-format off #define kMaskBursty1_1 \ @@ -639,7 +641,6 @@ namespace { // clang-format on } // namespace -namespace webrtc { namespace fec_private_tables { const uint8_t kPacketMaskBurstyTbl[] = { diff --git a/modules/rtp_rtcp/source/fec_private_tables_bursty_unittest.cc b/modules/rtp_rtcp/source/fec_private_tables_bursty_unittest.cc index d85b450bdc7..b14e2fb0ffb 100644 --- a/modules/rtp_rtcp/source/fec_private_tables_bursty_unittest.cc +++ b/modules/rtp_rtcp/source/fec_private_tables_bursty_unittest.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/include/module_fec_types.h" #include "modules/rtp_rtcp/source/fec_private_tables_random.h" #include "modules/rtp_rtcp/source/forward_error_correction_internal.h" @@ -30,7 +30,7 @@ namespace fec_private_tables { using internal::LookUpInFecTable; TEST(FecTable, TestBurstyLookup) { - ArrayView result; + std::span result; result = LookUpInFecTable(&kPacketMaskBurstyTbl[0], 0, 0); // Should match kMaskBursty1_1. EXPECT_EQ(2u, result.size()); @@ -56,7 +56,7 @@ TEST(FecTable, TestBurstyLookup) { } TEST(FecTable, TestRandomLookup) { - ArrayView result; + std::span result; result = LookUpInFecTable(&kPacketMaskRandomTbl[0], 0, 0); EXPECT_EQ(2u, result.size()); EXPECT_EQ(0x80u, result[0]); @@ -75,7 +75,7 @@ TEST(FecTable, TestRandomGenerated) { int num_fec_packets = 6; size_t mask_size = sizeof(kMaskRandom15_6) / sizeof(uint8_t); internal::PacketMaskTable mask_table(fec_mask_type, num_media_packets); - ArrayView mask = + std::span mask = mask_table.LookUp(num_media_packets, num_fec_packets); EXPECT_EQ(mask.size(), mask_size); for (size_t i = 0; i < mask_size; ++i) { diff --git a/modules/rtp_rtcp/source/fec_private_tables_random.cc b/modules/rtp_rtcp/source/fec_private_tables_random.cc index 885aeb5ef2d..b5cd8bccbdd 100644 --- a/modules/rtp_rtcp/source/fec_private_tables_random.cc +++ b/modules/rtp_rtcp/source/fec_private_tables_random.cc @@ -12,6 +12,8 @@ #include +namespace webrtc { + namespace { // clang-format off #define kMaskRandom1_1 \ @@ -639,7 +641,6 @@ namespace { // clang-format on } // namespace -namespace webrtc { namespace fec_private_tables { const uint8_t kPacketMaskRandomTbl[] = { diff --git a/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.cc b/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.cc index f05f9268474..5f4c32ab3ec 100644 --- a/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.cc +++ b/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/forward_error_correction.h" @@ -249,7 +249,7 @@ size_t Flexfec03HeaderWriter::FecHeaderSize(size_t packet_mask_size) const { // FecHeaderSize(), so in this function we can be sure that we are // writing in space that is intended for the header. void Flexfec03HeaderWriter::FinalizeFecHeader( - ArrayView protected_streams, + std::span protected_streams, ForwardErrorCorrection::Packet& fec_packet) const { RTC_CHECK_EQ(protected_streams.size(), 1); uint32_t media_ssrc = protected_streams[0].ssrc; diff --git a/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.h b/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.h index 04dcf31ee10..30be1754ceb 100644 --- a/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.h +++ b/modules/rtp_rtcp/source/flexfec_03_header_reader_writer.h @@ -14,7 +14,8 @@ #include #include -#include "api/array_view.h" +#include + #include "modules/rtp_rtcp/source/forward_error_correction.h" namespace webrtc { @@ -77,7 +78,7 @@ class Flexfec03HeaderWriter : public FecHeaderWriter { size_t FecHeaderSize(size_t packet_mask_row_size) const override; void FinalizeFecHeader( - ArrayView protected_streams, + std::span protected_streams, ForwardErrorCorrection::Packet& fec_packet) const override; }; diff --git a/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc b/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc index d467d53203a..d79b4449f16 100644 --- a/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc +++ b/modules/rtp_rtcp/source/flexfec_header_reader_writer.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/forward_error_correction.h" @@ -250,7 +250,7 @@ size_t FlexfecHeaderWriter::FecHeaderSize(size_t packet_mask_size) const { // TODO(brandtr): Update this function when we support offset-based masks // and retransmissions. void FlexfecHeaderWriter::FinalizeFecHeader( - ArrayView protected_streams, + std::span protected_streams, ForwardErrorCorrection::Packet& fec_packet) const { uint8_t* data = fec_packet.data.MutableData(); *data &= 0x7f; // Clear R bit. diff --git a/modules/rtp_rtcp/source/flexfec_header_reader_writer.h b/modules/rtp_rtcp/source/flexfec_header_reader_writer.h index 1f5202cd185..7018c856e5f 100644 --- a/modules/rtp_rtcp/source/flexfec_header_reader_writer.h +++ b/modules/rtp_rtcp/source/flexfec_header_reader_writer.h @@ -14,7 +14,8 @@ #include #include -#include "api/array_view.h" +#include + #include "modules/rtp_rtcp/source/forward_error_correction.h" namespace webrtc { @@ -59,7 +60,7 @@ class FlexfecHeaderWriter : public FecHeaderWriter { size_t FecHeaderSize(size_t packet_mask_row_size) const override; void FinalizeFecHeader( - ArrayView protected_streams, + std::span protected_streams, ForwardErrorCorrection::Packet& fec_packet) const override; }; diff --git a/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc b/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc index baeea9ddf31..1bc056fe8c1 100644 --- a/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc +++ b/modules/rtp_rtcp/source/flexfec_header_reader_writer_unittest.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/make_ref_counted.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/forward_error_correction.h" @@ -54,13 +54,13 @@ constexpr uint8_t kPayloadBits = 0x00; struct FecPacketStreamReadProperties { ProtectedStream stream; - ArrayView mask; + std::span mask; }; struct FecPacketStreamWriteProperties { size_t byte_index; uint16_t seq_num_base; - ArrayView mask; + std::span mask; }; Packet WritePacket( @@ -93,9 +93,9 @@ void VerifyReadHeaders(size_t expected_fec_header_size, EXPECT_EQ(protected_stream.packet_mask_size, expected[i].stream.packet_mask_size); // Ensure that the K-bits are removed and the packet mask has been packed. - EXPECT_THAT(MakeArrayView(read_packet.pkt->data.cdata() + - protected_stream.packet_mask_offset, - protected_stream.packet_mask_size), + EXPECT_THAT(std::span(read_packet.pkt->data.cdata() + + protected_stream.packet_mask_offset, + protected_stream.packet_mask_size), ElementsAreArray(expected[i].mask)); } EXPECT_EQ(read_packet.pkt->data.size() - expected_fec_header_size, @@ -115,9 +115,9 @@ void VerifyFinalizedHeaders( ByteReader::ReadBigEndian(packet + expected[i].byte_index), expected[i].seq_num_base); // Verify mask. - EXPECT_THAT(MakeArrayView(packet + expected[i].byte_index + 2, - expected[i].mask.size()), - ElementsAreArray(expected[i].mask)); + EXPECT_THAT( + std::span(packet + expected[i].byte_index + 2, expected[i].mask.size()), + ElementsAreArray(expected[i].mask)); } } @@ -162,19 +162,19 @@ void VerifyWrittenAndReadHeaders( read_packet.pkt->data.cdata() + read_packet.protected_streams[i].packet_mask_offset; // Verify actual mask bits. - EXPECT_THAT(MakeArrayView(read_mask_ptr, mask_write_size), + EXPECT_THAT(std::span(read_mask_ptr, mask_write_size), ElementsAreArray(write_protected_streams[i].packet_mask)); // If read mask size is larger than written mask size, verify all other bits // are 0. - EXPECT_THAT(MakeArrayView(read_mask_ptr + mask_write_size, - expected_mask_read_size - mask_write_size), + EXPECT_THAT(std::span(read_mask_ptr + mask_write_size, + expected_mask_read_size - mask_write_size), Each(0)); } // Verify that the call to ReadFecHeader did not tamper with the payload. EXPECT_THAT( - MakeArrayView(read_packet.pkt->data.cdata() + read_packet.fec_header_size, - read_packet.pkt->data.size() - read_packet.fec_header_size), + std::span(read_packet.pkt->data.cdata() + read_packet.fec_header_size, + read_packet.pkt->data.size() - read_packet.fec_header_size), ElementsAreArray(written_packet.data.cdata() + expected_header_size, written_packet.data.size() - expected_header_size)); } diff --git a/modules/rtp_rtcp/source/flexfec_sender.cc b/modules/rtp_rtcp/source/flexfec_sender.cc index 32328fb5111..5a87f3fbcef 100644 --- a/modules/rtp_rtcp/source/flexfec_sender.cc +++ b/modules/rtp_rtcp/source/flexfec_sender.cc @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/rtp_parameters.h" #include "api/units/data_rate.h" @@ -89,7 +89,7 @@ FlexfecSender::FlexfecSender( uint32_t protected_media_ssrc, absl::string_view mid, const std::vector& rtp_header_extensions, - ArrayView extension_sizes, + std::span extension_sizes, const RtpState* rtp_state) : env_(env), random_(env_.clock().TimeInMicroseconds()), diff --git a/modules/rtp_rtcp/source/forward_error_correction.h b/modules/rtp_rtcp/source/forward_error_correction.h index b4e1a50f6eb..fcfa6412044 100644 --- a/modules/rtp_rtcp/source/forward_error_correction.h +++ b/modules/rtp_rtcp/source/forward_error_correction.h @@ -16,10 +16,10 @@ #include #include +#include #include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "modules/include/module_fec_types.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" @@ -398,7 +398,7 @@ class FecHeaderWriter { struct ProtectedStream { uint32_t ssrc = 0; uint16_t seq_num_base = 0; - ArrayView packet_mask; + std::span packet_mask; }; virtual ~FecHeaderWriter(); @@ -424,7 +424,7 @@ class FecHeaderWriter { // Writes FEC header. virtual void FinalizeFecHeader( - ArrayView protected_streams, + std::span protected_streams, ForwardErrorCorrection::Packet& fec_packet) const = 0; protected: diff --git a/modules/rtp_rtcp/source/forward_error_correction_internal.cc b/modules/rtp_rtcp/source/forward_error_correction_internal.cc index d2f3b59ec45..46457794e89 100644 --- a/modules/rtp_rtcp/source/forward_error_correction_internal.cc +++ b/modules/rtp_rtcp/source/forward_error_correction_internal.cc @@ -13,13 +13,15 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/include/module_fec_types.h" #include "modules/rtp_rtcp/source/fec_private_tables_bursty.h" #include "modules/rtp_rtcp/source/fec_private_tables_random.h" #include "rtc_base/checks.h" +namespace webrtc { + namespace { // Allow for different modes of protection for packets in UEP case. enum ProtectionMode { @@ -141,7 +143,6 @@ void ShiftFitSubMask(int num_mask_bytes, } // namespace -namespace webrtc { namespace internal { PacketMaskTable::PacketMaskTable(FecMaskType fec_mask_type, @@ -150,7 +151,7 @@ PacketMaskTable::PacketMaskTable(FecMaskType fec_mask_type, PacketMaskTable::~PacketMaskTable() = default; -ArrayView PacketMaskTable::LookUp(int num_media_packets, +std::span PacketMaskTable::LookUp(int num_media_packets, int num_fec_packets) { RTC_DCHECK_GT(num_media_packets, 0); RTC_DCHECK_GT(num_fec_packets, 0); @@ -246,7 +247,7 @@ void RemainingPacketProtection(int num_media_packets, PacketMaskSize(num_media_packets - num_fec_for_imp_packets); auto end_row = (num_fec_for_imp_packets + num_fec_remaining); - ArrayView packet_mask_sub_21 = mask_table->LookUp( + std::span packet_mask_sub_21 = mask_table->LookUp( num_media_packets - num_fec_for_imp_packets, num_fec_remaining); ShiftFitSubMask(num_mask_bytes, res_mask_bytes, num_fec_for_imp_packets, @@ -254,7 +255,7 @@ void RemainingPacketProtection(int num_media_packets, } else if (mode == kModeOverlap || mode == kModeBiasFirstPacket) { // sub_mask22 - ArrayView packet_mask_sub_22 = + std::span packet_mask_sub_22 = mask_table->LookUp(num_media_packets, num_fec_remaining); FitSubMask(num_mask_bytes, num_mask_bytes, num_fec_remaining, @@ -281,7 +282,7 @@ void ImportantPacketProtection(int num_fec_for_imp_packets, const int num_imp_mask_bytes = PacketMaskSize(num_imp_packets); // Get sub_mask1 from table - ArrayView packet_mask_sub_1 = + std::span packet_mask_sub_1 = mask_table->LookUp(num_imp_packets, num_fec_for_imp_packets); FitSubMask(num_mask_bytes, num_imp_mask_bytes, num_fec_for_imp_packets, @@ -410,7 +411,7 @@ void UnequalProtectionMask(int num_media_packets, // * For all entries: 2 * fec index (1 based) // * Size for kPacketMaskBurstyTbl: 2 bytes. // * For all entries: 2 * fec index (1 based) -ArrayView LookUpInFecTable(const uint8_t* table, +std::span LookUpInFecTable(const uint8_t* table, int media_packet_index, int fec_index) { RTC_DCHECK_LT(media_packet_index, table[0]); @@ -466,7 +467,7 @@ void GeneratePacketMasks(int num_media_packets, // Retrieve corresponding mask table directly:for equal-protection case. // Mask = (k,n-k), with protection factor = (n-k)/k, // where k = num_media_packets, n=total#packets, (n-k)=num_fec_packets. - ArrayView mask = + std::span mask = mask_table->LookUp(num_media_packets, num_fec_packets); memcpy(packet_mask, &mask[0], mask.size()); } else { // UEP case diff --git a/modules/rtp_rtcp/source/forward_error_correction_internal.h b/modules/rtp_rtcp/source/forward_error_correction_internal.h index e02ba499d86..1a9674c9f57 100644 --- a/modules/rtp_rtcp/source/forward_error_correction_internal.h +++ b/modules/rtp_rtcp/source/forward_error_correction_internal.h @@ -14,7 +14,8 @@ #include #include -#include "api/array_view.h" +#include + #include "modules/include/module_fec_types.h" namespace webrtc { @@ -43,7 +44,7 @@ class PacketMaskTable { PacketMaskTable(FecMaskType fec_mask_type, int num_media_packets); ~PacketMaskTable(); - ArrayView LookUp(int num_media_packets, int num_fec_packets); + std::span LookUp(int num_media_packets, int num_fec_packets); private: static const uint8_t* PickTable(FecMaskType fec_mask_type, @@ -52,7 +53,7 @@ class PacketMaskTable { uint8_t fec_packet_mask_[kFECPacketMaskMaxSize]; }; -ArrayView LookUpInFecTable(const uint8_t* table, +std::span LookUpInFecTable(const uint8_t* table, int media_packet_index, int fec_index); diff --git a/modules/rtp_rtcp/source/frame_object.cc b/modules/rtp_rtcp/source/frame_object.cc index 3a52b7541c9..9f6442eefb7 100644 --- a/modules/rtp_rtcp/source/frame_object.cc +++ b/modules/rtp_rtcp/source/frame_object.cc @@ -64,7 +64,7 @@ RtpFrameObject::RtpFrameObject( _payloadType = payload_type; SetRtpTimestamp(rtp_timestamp); ntp_time_ms_ = ntp_time_ms; - _frameType = rtp_video_header_.frame_type; + set_frame_type(rtp_video_header_.frame_type); // Setting frame's playout delays to the same values // as of the first packet's. diff --git a/modules/rtp_rtcp/source/frame_transformer_factory_unittest.cc b/modules/rtp_rtcp/source/frame_transformer_factory_unittest.cc index f6a923f022c..0e909c7bc9b 100644 --- a/modules/rtp_rtcp/source/frame_transformer_factory_unittest.cc +++ b/modules/rtp_rtcp/source/frame_transformer_factory_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/test/mock_transformable_audio_frame.h" #include "api/test/mock_transformable_video_frame.h" #include "api/video/video_frame_metadata.h" @@ -34,7 +34,7 @@ TEST(FrameTransformerFactory, CloneAudioFrame) { NiceMock original_frame; uint8_t data[10]; std::fill_n(data, 10, 5); - ArrayView data_view(data); + std::span data_view(data); ON_CALL(original_frame, GetData()).WillByDefault(Return(data_view)); auto cloned_frame = CloneAudioFrame(&original_frame); @@ -45,7 +45,7 @@ TEST(FrameTransformerFactory, CloneVideoFrame) { NiceMock original_frame; uint8_t data[10]; std::fill_n(data, 10, 5); - ArrayView data_view(data); + std::span data_view(data); EXPECT_CALL(original_frame, GetData()).WillRepeatedly(Return(data_view)); VideoFrameMetadata metadata; std::vector csrcs{123, 321}; diff --git a/modules/rtp_rtcp/source/leb128_unittest.cc b/modules/rtp_rtcp/source/leb128_unittest.cc index c58b8bc776a..3512dbd1c22 100644 --- a/modules/rtp_rtcp/source/leb128_unittest.cc +++ b/modules/rtp_rtcp/source/leb128_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "test/gmock.h" #include "test/gtest.h" @@ -121,17 +121,15 @@ TEST(Leb128Test, WriteTwoByteValue) { TEST(Leb128Test, WriteNearlyMaxValue) { uint8_t buffer[16]; EXPECT_EQ(WriteLeb128(0x7fff'ffff'ffff'ffff, buffer), 9); - EXPECT_THAT( - MakeArrayView(buffer, 9), - ElementsAre(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f)); + EXPECT_THAT(std::span(buffer, 9), ElementsAre(0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x7f)); } TEST(Leb128Test, WriteMaxValue) { uint8_t buffer[16]; EXPECT_EQ(WriteLeb128(0xffff'ffff'ffff'ffff, buffer), 10); - EXPECT_THAT( - MakeArrayView(buffer, 10), - ElementsAre(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01)); + EXPECT_THAT(std::span(buffer, 10), ElementsAre(0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01)); } } // namespace diff --git a/modules/rtp_rtcp/source/nack_rtx_unittest.cc b/modules/rtp_rtcp/source/nack_rtx_unittest.cc index 11a0a99f742..a94dd69b38d 100644 --- a/modules/rtp_rtcp/source/nack_rtx_unittest.cc +++ b/modules/rtp_rtcp/source/nack_rtx_unittest.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -90,7 +90,7 @@ class RtxLoopBackTransport : public Transport { packet_loss_ = 0; } - bool SendRtp(ArrayView data, + bool SendRtp(std::span data, const PacketOptions& /* options */) override { count_++; RtpPacketReceived packet; @@ -115,7 +115,7 @@ class RtxLoopBackTransport : public Transport { return true; } - bool SendRtcp(ArrayView data, + bool SendRtcp(std::span data, const PacketOptions& /* options */) override { module_->IncomingRtcpPacket(data); return true; @@ -137,7 +137,10 @@ class RtpRtcpRtxNackTest : public ::testing::Test { : fake_clock_(123456), env_(CreateEnvironment(&fake_clock_)), transport_(kTestRtxSsrc), - rtx_stream_(&media_stream_, rtx_associated_payload_types_, kTestSsrc), + rtx_stream_(env_, + &media_stream_, + rtx_associated_payload_types_, + kTestSsrc), retransmission_rate_limiter_(&fake_clock_, kMaxRttMs) {} ~RtpRtcpRtxNackTest() override {} @@ -151,7 +154,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test { configuration.local_media_ssrc = kTestSsrc; configuration.rtx_send_ssrc = kTestRtxSsrc; rtp_rtcp_module_ = - std::make_unique(env_, configuration); + ModuleRtpRtcpImpl2::CreateSendModule(env_, configuration); RTPSenderVideo::Config video_config; video_config.clock = &fake_clock_; video_config.rtp_sender = rtp_rtcp_module_->RtpSender(); diff --git a/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc b/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc index 5bde3445a02..806cc244620 100644 --- a/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc +++ b/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc @@ -18,11 +18,15 @@ #include "modules/rtp_rtcp/source/ntp_time_util.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/ntp_time.h" +#include "test/gmock.h" #include "test/gtest.h" +#include "test/near_matcher.h" namespace webrtc { namespace { +using ::testing::Optional; + constexpr TimeDelta kTestRtt = TimeDelta::Millis(10); constexpr Timestamp kLocalClockInitialTime = Timestamp::Millis(123); constexpr Timestamp kRemoteClockInitialTime = Timestamp::Millis(373); @@ -62,6 +66,14 @@ class RemoteNtpTimeEstimatorTest : public ::testing::Test { EXPECT_TRUE(estimator_.UpdateRtcpTimestamp(kTestRtt, ntp, rtcp_timestamp)); } + void ReceiveRemoteSr(TimeDelta delivery_delay, TimeDelta rtt) { + const uint32_t rtp_sr = GetRemoteTimestamp(); + const NtpTime ntp_sr = remote_clock_.CurrentNtpTime(); + + AdvanceTime(delivery_delay); + EXPECT_TRUE(estimator_.UpdateRtcpTimestamp(rtt, ntp_sr, rtp_sr)); + } + SimulatedClock local_clock_{kLocalClockInitialTime}; SimulatedClock remote_clock_{kRemoteClockInitialTime}; RemoteNtpTimeEstimator estimator_{&local_clock_}; @@ -134,5 +146,60 @@ TEST_F(RemoteNtpTimeEstimatorTest, AveragesErrorsOut) { *estimator_.EstimateRemoteToLocalClockOffset(), kEpsilon); } +TEST_F(RemoteNtpTimeEstimatorTest, EstimateUsingRrtrLogic) { + // This test emulates estimation using the logic embedded in the + // handler code for RRTR and DLRR (receiver side RTT estimate). + // It is subtly different from the sender-side RTT estimate simulated + // in the "Estimate" test. + + // 1. Simulate receiver sending RRTR. + const NtpTime t1 = local_clock_.CurrentNtpTime(); + + // 2. Simulate sender receiving RRTR and sending DLRR. + // Assume a one-way delay of 10ms and a remote processing delay of 5ms. + const TimeDelta kOneWayDelay = TimeDelta::Millis(10); + const TimeDelta kRemoteProcessingDelay = TimeDelta::Millis(5); + + AdvanceTime(kOneWayDelay); // Remote receives RRTR at t2. + AdvanceTime(kRemoteProcessingDelay); // Remote sends DLRR at t3. + + // 3. Receiver receives DLRR. + AdvanceTime(kOneWayDelay); // Local receives DLRR at t4. + const NtpTime t4 = local_clock_.CurrentNtpTime(); + + // RTT calculation (as done in RTCPReceiver::HandleXrDlrrReportBlock): + // RTT = (t4 - t1) - (t3 - t2) + const uint32_t last_rr = CompactNtp(t1); + const uint32_t delay_since_last_rr = + SaturatedToCompactNtp(kRemoteProcessingDelay); + const uint32_t now_ntp = CompactNtp(t4); + const uint32_t rtt_compact = now_ntp - delay_since_last_rr - last_rr; + const TimeDelta rtt = CompactNtpRttToTimeDelta(rtt_compact); + + // Expect RTT to be approximately 20ms (2 * kOneWayDelay). + EXPECT_THAT(rtt, Near(2 * kOneWayDelay, TimeDelta::Millis(1))); + + AdvanceTime(TimeDelta::Millis(100)); + // Remote sends Sender Report. + ReceiveRemoteSr(kOneWayDelay, rtt); + // Local peer needs at least 2 RTCP SR to calculate the capture time. + EXPECT_EQ(estimator_.EstimateRemoteToLocalClockOffset(), std::nullopt); + + // Second SR update. + AdvanceTime(TimeDelta::Millis(800)); + ReceiveRemoteSr(kOneWayDelay, rtt); + + // Third SR update. + AdvanceTime(TimeDelta::Millis(800)); + ReceiveRemoteSr(kOneWayDelay, rtt); + + // Verify that the estimated offset is correct. + // The epsilon is in NTP ticks (approx 0.2 ns), so this number + // corresponds to an epsilon of 5 microseconds. + constexpr int64_t kDlrrEpsilon = 10000; + EXPECT_THAT(estimator_.EstimateRemoteToLocalClockOffset(), + Optional(Near(kRemoteToLocalClockOffsetNtp, kDlrrEpsilon))); +} + } // namespace } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_packet.cc b/modules/rtp_rtcp/source/rtcp_packet.cc index 242ec075f36..66cde51a0e2 100644 --- a/modules/rtp_rtcp/source/rtcp_packet.cc +++ b/modules/rtp_rtcp/source/rtcp_packet.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -24,7 +24,7 @@ namespace rtcp { Buffer RtcpPacket::Build() const { Buffer packet = Buffer::CreateWithCapacity(BlockLength()); - packet.AppendData(BlockLength(), [&](ArrayView data) { + packet.AppendData(BlockLength(), [&](std::span data) { size_t length = 0; bool created = Create(data.data(), &length, data.size(), nullptr); RTC_DCHECK(created) << "Invalid packet is not supported."; @@ -50,7 +50,7 @@ bool RtcpPacket::OnBufferFull(uint8_t* packet, if (*index == 0) return false; RTC_DCHECK(callback) << "Fragmentation not supported."; - callback(ArrayView(packet, *index)); + callback(std::span(packet, *index)); *index = 0; return true; } diff --git a/modules/rtp_rtcp/source/rtcp_packet.h b/modules/rtp_rtcp/source/rtcp_packet.h index 079414694c5..988d49f7518 100644 --- a/modules/rtp_rtcp/source/rtcp_packet.h +++ b/modules/rtp_rtcp/source/rtcp_packet.h @@ -14,7 +14,8 @@ #include #include -#include "api/array_view.h" +#include + #include "api/function_view.h" #include "rtc_base/buffer.h" @@ -54,7 +55,7 @@ class RtcpPacket { // max_length bytes, it will be fragmented and multiple calls to this // callback will be made. using PacketReadyCallback = - FunctionView packet)>; + FunctionView packet)>; virtual ~RtcpPacket() = default; diff --git a/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc index e31c330231d..5c4bfe3f0bb 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtcp_packet/bye.h" #include "modules/rtp_rtcp/source/rtcp_packet/fir.h" @@ -108,8 +108,8 @@ TEST(RtcpCompoundPacketTest, BuildWithInputBuffer) { const size_t kFirLength = 20; const size_t kBufferSize = kRrLength + kReportBlockLength + kFirLength; - MockFunction)> callback; - EXPECT_CALL(callback, Call(_)).WillOnce([&](ArrayView packet) { + MockFunction)> callback; + EXPECT_CALL(callback, Call(_)).WillOnce([&](std::span packet) { RtcpPacketParser parser; parser.Parse(packet); EXPECT_EQ(1, parser.receiver_report()->num_packets()); @@ -135,16 +135,16 @@ TEST(RtcpCompoundPacketTest, BuildWithTooSmallBuffer_FragmentedSend) { const size_t kReportBlockLength = 24; const size_t kBufferSize = kRrLength + kReportBlockLength; - MockFunction)> callback; + MockFunction)> callback; EXPECT_CALL(callback, Call(_)) - .WillOnce([&](ArrayView packet) { + .WillOnce([&](std::span packet) { RtcpPacketParser parser; parser.Parse(packet); EXPECT_EQ(1, parser.receiver_report()->num_packets()); EXPECT_EQ(1U, parser.receiver_report()->report_blocks().size()); EXPECT_EQ(0, parser.fir()->num_packets()); }) - .WillOnce([&](ArrayView packet) { + .WillOnce([&](std::span packet) { RtcpPacketParser parser; parser.Parse(packet); EXPECT_EQ(0, parser.receiver_report()->num_packets()); diff --git a/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.cc b/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.cc index b4e227c1cf7..abe858f7bd4 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/transport/ecn_marking.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -166,7 +166,7 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, // |R|ECN| Arrival time offset | ... . // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // . . - auto write_report_for_ssrc = [&](ArrayView packets) { + auto write_report_for_ssrc = [&](std::span packets) { // SSRC of nth RTP stream. ByteWriter::WriteBigEndian(&buffer[*position], packets[0].ssrc); *position += 4; @@ -211,7 +211,7 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, } }; - ArrayView remaining(packets_); + std::span remaining(packets_); while (!remaining.empty()) { int number_of_packets_for_ssrc = 0; uint32_t ssrc = remaining[0].ssrc; @@ -221,8 +221,8 @@ bool CongestionControlFeedback::Create(uint8_t* buffer, } ++number_of_packets_for_ssrc; } - write_report_for_ssrc(remaining.subview(0, number_of_packets_for_ssrc)); - remaining = remaining.subview(number_of_packets_for_ssrc); + write_report_for_ssrc(remaining.subspan(0, number_of_packets_for_ssrc)); + remaining = remaining.subspan(number_of_packets_for_ssrc); } // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ diff --git a/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h b/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h index da9beb9e003..2d2b0771b53 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h +++ b/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/transport/ecn_marking.h" #include "api/units/time_delta.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" @@ -50,7 +50,7 @@ class CongestionControlFeedback : public Rtpfb { bool Parse(const CommonHeader& packet); - ArrayView packets() const { return packets_; } + std::span packets() const { return packets_; } uint32_t report_timestamp_compact_ntp() const { return report_timestamp_compact_ntp_; diff --git a/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback_unittest.cc index 945747b4fa3..ea085cd7479 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback_unittest.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/function_view.h" #include "api/transport/ecn_marking.h" #include "api/units/time_delta.h" @@ -129,10 +129,14 @@ TEST(CongestionControlFeedbackTest, CreateReturnsTrueForBasicPacket) { .arrival_time_offset = TimeDelta::Millis(2)}}; CongestionControlFeedback fb(std::move(packets), /*compact_ntp_timestamp=*/1); - Buffer buf = Buffer::CreateUninitializedWithSize(fb.BlockLength()); - size_t position = 0; - FunctionView packet)> callback; - EXPECT_TRUE(fb.Create(buf.data(), &position, buf.capacity(), callback)); + Buffer buf = Buffer::CreateWithCapacity(fb.BlockLength()); + buf.AppendData(fb.BlockLength(), [&](std::span buf_view) { + size_t position = 0; + FunctionView packet)> callback; + EXPECT_TRUE( + fb.Create(buf_view.data(), &position, buf_view.size(), callback)); + return position; + }); } TEST(CongestionControlFeedbackTest, CanCreateAndParseWithoutPackets) { diff --git a/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc b/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc index 87ac475ac76..e244599d938 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc @@ -52,7 +52,6 @@ bool Dlrr::Parse(const uint8_t* buffer, uint16_t block_length_32bits) { RTC_LOG(LS_WARNING) << "Invalid size for dlrr block."; return false; } - size_t blocks_count = block_length_32bits / 3; const uint8_t* read_at = buffer + kBlockHeaderLength; sub_blocks_.resize(blocks_count); diff --git a/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc index 3f00d5d16d1..3530bbc9c6b 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "test/gmock.h" #include "test/gtest.h" @@ -133,16 +133,16 @@ TEST(RtcpPacketNackTest, CreateFragmented) { const size_t kBufferSize = 12 + (3 * 4); // Fits common header + 3 nack items - MockFunction)> callback; + MockFunction)> callback; EXPECT_CALL(callback, Call(_)) - .WillOnce([&](ArrayView packet) { + .WillOnce([&](std::span packet) { Nack nack; EXPECT_TRUE(test::ParseSinglePacket(packet, &nack)); EXPECT_EQ(kSenderSsrc, nack.sender_ssrc()); EXPECT_EQ(kRemoteSsrc, nack.media_ssrc()); EXPECT_THAT(nack.packet_ids(), ElementsAre(1, 100, 200)); }) - .WillOnce([&](ArrayView packet) { + .WillOnce([&](std::span packet) { Nack nack; EXPECT_TRUE(test::ParseSinglePacket(packet, &nack)); EXPECT_EQ(kSenderSsrc, nack.sender_ssrc()); @@ -161,7 +161,7 @@ TEST(RtcpPacketNackTest, CreateFailsWithTooSmallBuffer) { nack.SetMediaSsrc(kRemoteSsrc); nack.SetPacketIds(kSmallList, std::size(kSmallList)); - MockFunction)> callback; + MockFunction)> callback; EXPECT_CALL(callback, Call(_)).Times(0); EXPECT_FALSE(nack.Build(kMinNackBlockSize - 1, callback.AsStdFunction())); } diff --git a/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc index 8855aa5dc94..dd58f5b96a9 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc @@ -12,10 +12,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" #include "modules/rtp_rtcp/source/byte_io.h" @@ -88,20 +88,20 @@ class RemoteEstimateSerializerImpl : public RemoteEstimateSerializer { size_t max_size = fields_.size() * kFieldSize; Buffer buf = Buffer::CreateWithCapacity(max_size); for (const auto& field : fields_) { - buf.AppendData(kFieldSize, [&](ArrayView dst) { + buf.AppendData(kFieldSize, [&](std::span dst) { return field.Write(src, dst.data()) ? kFieldSize : 0; }); } return buf; } - bool Parse(ArrayView src, + bool Parse(std::span src, NetworkStateEstimate* target) const override { if (src.size() % kFieldSize != 0) return false; RTC_DCHECK_EQ(src.size() % kFieldSize, 0); - for (const uint8_t* data_ptr = src.data(); data_ptr < src.end(); - data_ptr += kFieldSize) { + for (const uint8_t* data_ptr = src.data(); + data_ptr < src.data() + src.size(); data_ptr += kFieldSize) { uint8_t field_id = ByteReader::ReadBigEndian(data_ptr); for (const auto& field : fields_) { if (field.id() == field_id) { diff --git a/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h index 4c8c29405f2..ed596f4d30a 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h +++ b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h @@ -11,8 +11,8 @@ #define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMOTE_ESTIMATE_H_ #include +#include -#include "api/array_view.h" #include "api/transport/network_types.h" #include "api/units/time_delta.h" #include "modules/rtp_rtcp/source/rtcp_packet/app.h" @@ -24,7 +24,7 @@ namespace rtcp { class CommonHeader; class RemoteEstimateSerializer { public: - virtual bool Parse(ArrayView src, + virtual bool Parse(std::span src, NetworkStateEstimate* target) const = 0; virtual Buffer Serialize(const NetworkStateEstimate& src) const = 0; virtual ~RemoteEstimateSerializer() = default; diff --git a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc index deda519ade0..2894aab98ec 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/rtp_rtcp/source/byte_io.h" @@ -62,7 +62,7 @@ MATCHER(IsValidFeedback, "") { feedback.Parse(rtcp_header); } -TransportFeedback Parse(ArrayView buffer) { +TransportFeedback Parse(std::span buffer) { rtcp::CommonHeader header; EXPECT_TRUE(header.Parse(buffer.data(), buffer.size())); EXPECT_EQ(header.type(), TransportFeedback::kPacketType); @@ -86,8 +86,8 @@ class FeedbackTester { void WithDefaultDelta(TimeDelta delta) { default_delta_ = delta; } - void WithInput(ArrayView received_seq, - ArrayView received_ts = {}) { + void WithInput(std::span received_seq, + std::span received_ts = {}) { std::vector temp_timestamps; if (received_ts.empty()) { temp_timestamps = GenerateReceiveTimestamps(received_seq); @@ -153,7 +153,7 @@ class FeedbackTester { } std::vector GenerateReceiveTimestamps( - ArrayView seq_nums) { + std::span seq_nums) { RTC_CHECK(!seq_nums.empty()); uint16_t last_seq = seq_nums[0]; Timestamp time = Timestamp::Zero(); diff --git a/modules/rtp_rtcp/source/rtcp_packet_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet_unittest.cc index 793535f0e42..101d61b8b32 100644 --- a/modules/rtp_rtcp/source/rtcp_packet_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet_unittest.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" #include "test/gmock.h" @@ -38,7 +38,7 @@ TEST(RtcpPacketTest, BuildWithTooSmallBuffer) { const size_t kReportBlockLength = 24; // No packet. - MockFunction)> callback; + MockFunction)> callback; EXPECT_CALL(callback, Call(_)).Times(0); const size_t kBufferSize = kRrLength + kReportBlockLength - 1; EXPECT_FALSE(rr.Build(kBufferSize, callback.AsStdFunction())); diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc index 2bc9014a330..72b5c35ffa8 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -19,12 +19,12 @@ #include #include #include +#include #include #include #include "absl/algorithm/container.h" #include "absl/base/attributes.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" #include "api/sequence_checker.h" @@ -184,7 +184,7 @@ RTCPReceiver::RTCPReceiver(const Environment& env, RTCPReceiver::~RTCPReceiver() {} -void RTCPReceiver::IncomingPacket(ArrayView packet) { +void RTCPReceiver::IncomingPacket(std::span packet) { if (packet.empty()) { RTC_LOG(LS_WARNING) << "Incoming empty RTCP packet"; return; @@ -347,7 +347,7 @@ std::vector RTCPReceiver::GetLatestReportBlockData() const { return result; } -bool RTCPReceiver::ParseCompoundPacket(ArrayView packet, +bool RTCPReceiver::ParseCompoundPacket(std::span packet, PacketInformation* packet_information) { MutexLock lock(&rtcp_receiver_lock_); @@ -363,12 +363,13 @@ bool RTCPReceiver::ParseCompoundPacket(ArrayView packet, // block. flat_map received_blocks; bool valid = true; - for (const uint8_t* next_block = packet.begin(); - valid && next_block != packet.end(); - next_block = rtcp_block.NextPacket()) { + for (auto next_block = packet.begin(); valid && next_block != packet.end(); + next_block = + packet.begin() + (rtcp_block.NextPacket() - packet.data())) { ptrdiff_t remaining_blocks_size = packet.end() - next_block; RTC_DCHECK_GT(remaining_blocks_size, 0); - if (!rtcp_block.Parse(next_block, remaining_blocks_size)) { + if (!rtcp_block.Parse(std::to_address(next_block), + static_cast(remaining_blocks_size))) { valid = false; break; } diff --git a/modules/rtp_rtcp/source/rtcp_receiver.h b/modules/rtp_rtcp/source/rtcp_receiver.h index 555269ae24d..09b6f587d1f 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver.h +++ b/modules/rtp_rtcp/source/rtcp_receiver.h @@ -16,10 +16,10 @@ #include #include #include +#include #include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/sequence_checker.h" #include "api/units/time_delta.h" @@ -58,7 +58,7 @@ class RTCPReceiver final { virtual void OnReceivedNack( const std::vector& nack_sequence_numbers) = 0; virtual void OnReceivedRtcpReportBlocks( - ArrayView report_blocks) = 0; + std::span report_blocks) = 0; protected: virtual ~ModuleRtpRtcp() = default; @@ -99,7 +99,7 @@ class RTCPReceiver final { ~RTCPReceiver(); - void IncomingPacket(ArrayView packet); + void IncomingPacket(std::span packet); int64_t LastReceivedReportBlockMs() const; @@ -231,7 +231,7 @@ class RTCPReceiver final { size_t num_rtts_ = 0; }; - bool ParseCompoundPacket(ArrayView packet, + bool ParseCompoundPacket(std::span packet, PacketInformation* packet_information); void TriggerCallbacksFromRtcpPacket( diff --git a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc index 4c91dc46b9f..cd724e6b837 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc @@ -15,12 +15,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment_factory.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" @@ -126,7 +126,7 @@ class MockModuleRtpRtcp : public RTCPReceiver::ModuleRtpRtcp { MOCK_METHOD(void, OnReceivedNack, (const std::vector&), (override)); MOCK_METHOD(void, OnReceivedRtcpReportBlocks, - (ArrayView), + (std::span), (override)); }; diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc index 589b387d111..9b9bd222621 100644 --- a/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/modules/rtp_rtcp/source/rtcp_sender.cc @@ -15,12 +15,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/rtc_event_log/rtc_event_log.h" @@ -87,7 +87,7 @@ class RTCPSender::PacketSender { // Sends pending rtcp packet. void Send() { if (index_ > 0) { - callback_(ArrayView(buffer_, index_)); + callback_(std::span(buffer_, index_)); index_ = 0; } } @@ -115,19 +115,21 @@ RTCPSender::FeedbackState::~FeedbackState() = default; class RTCPSender::RtcpContext { public: RtcpContext(const FeedbackState& feedback_state, - ArrayView nacks, + std::span nacks, Timestamp now) : feedback_state_(feedback_state), nacks_(nacks), now_(now) {} const FeedbackState& feedback_state_; - const ArrayView nacks_; + const std::span nacks_; const Timestamp now_; }; RTCPSender::RTCPSender(const Environment& env, Configuration config) : env_(env), + is_rtp_sender_(config.recv_ssrc_callback == nullptr), audio_(config.audio), - ssrc_(config.local_media_ssrc), + send_ssrc_(config.local_media_ssrc), + recv_ssrc_callback_(std::move(config.recv_ssrc_callback)), random_(env_.clock().TimeInMicroseconds()), method_(RtcpMode::kOff), transport_(config.outgoing_transport), @@ -152,6 +154,9 @@ RTCPSender::RTCPSender(const Environment& env, Configuration config) RTC_CHECK(schedule_next_rtcp_send_evaluation_); RTC_CHECK_GT(report_interval_, TimeDelta::Zero()); RTC_DCHECK(transport_ != nullptr); + // We don't want to see local_media_ssrc if we're not an RTP sender. + // It's likely a configuration error. + RTC_DCHECK(is_rtp_sender_ || send_ssrc_ == 0); builders_[kRtcpSr] = &RTCPSender::BuildSR; builders_[kRtcpRr] = &RTCPSender::BuildRR; @@ -194,6 +199,8 @@ bool RTCPSender::Sending() const { void RTCPSender::SetSendingStatus(const FeedbackState& /* feedback_state */, bool sending) { MutexLock lock(&mutex_rtcp_sender_); + RTC_DCHECK(is_rtp_sender_ || !sending) + << "Only senders can have sending turned on"; sending_ = sending; } @@ -208,7 +215,7 @@ int32_t RTCPSender::SendLossNotification(const FeedbackState& feedback_state, bool decodability_flag, bool buffering_allowed) { int32_t error_code = -1; - auto callback = [&](ArrayView packet) { + auto callback = [&](std::span packet) { transport_->SendRtcp(packet, /*packet_options=*/{}); error_code = 0; env_.event_log().Log(std::make_unique(packet)); @@ -304,12 +311,12 @@ void RTCPSender::SetRtpClockRate(int8_t payload_type, int rtp_clock_rate_hz) { uint32_t RTCPSender::SSRC() const { MutexLock lock(&mutex_rtcp_sender_); - return ssrc_; + return ComputeSsrc(); } void RTCPSender::SetSsrc(uint32_t ssrc) { MutexLock lock(&mutex_rtcp_sender_); - ssrc_ = ssrc; + send_ssrc_ = ssrc; } void RTCPSender::SetRemoteSSRC(uint32_t ssrc) { @@ -364,7 +371,7 @@ void RTCPSender::BuildSR(const RtcpContext& ctx, PacketSender& sender) { rtp_rate; rtcp::SenderReport report; - report.SetSenderSsrc(ssrc_); + report.SetSenderSsrc(ComputeSsrc()); report.SetNtp(env_.clock().ConvertTimestampToNtpTime(ctx.now_)); report.SetRtpTimestamp(rtp_timestamp); report.SetPacketCount(ctx.feedback_state_.packets_sent); @@ -378,13 +385,13 @@ void RTCPSender::BuildSDES(const RtcpContext& /* ctx */, PacketSender& sender) { RTC_CHECK_LT(length_cname, RTCP_CNAME_SIZE); rtcp::Sdes sdes; - sdes.AddCName(ssrc_, cname_); + sdes.AddCName(ComputeSsrc(), cname_); sender.AppendPacket(sdes); } void RTCPSender::BuildRR(const RtcpContext& ctx, PacketSender& sender) { rtcp::ReceiverReport report; - report.SetSenderSsrc(ssrc_); + report.SetSenderSsrc(ComputeSsrc()); report.SetReportBlocks(CreateReportBlocks(ctx.feedback_state_)); if (method_ == RtcpMode::kCompound || !report.report_blocks().empty()) { sender.AppendPacket(report); @@ -393,7 +400,7 @@ void RTCPSender::BuildRR(const RtcpContext& ctx, PacketSender& sender) { void RTCPSender::BuildPLI(const RtcpContext& /* ctx */, PacketSender& sender) { rtcp::Pli pli; - pli.SetSenderSsrc(ssrc_); + pli.SetSenderSsrc(ComputeSsrc()); pli.SetMediaSsrc(remote_ssrc_); ++packet_type_counter_.pli_packets; @@ -404,7 +411,7 @@ void RTCPSender::BuildFIR(const RtcpContext& /* ctx */, PacketSender& sender) { ++sequence_number_fir_; rtcp::Fir fir; - fir.SetSenderSsrc(ssrc_); + fir.SetSenderSsrc(ComputeSsrc()); fir.AddRequestTo(remote_ssrc_, sequence_number_fir_); ++packet_type_counter_.fir_packets; @@ -413,7 +420,7 @@ void RTCPSender::BuildFIR(const RtcpContext& /* ctx */, PacketSender& sender) { void RTCPSender::BuildREMB(const RtcpContext& /* ctx */, PacketSender& sender) { rtcp::Remb remb; - remb.SetSenderSsrc(ssrc_); + remb.SetSenderSsrc(ComputeSsrc()); remb.SetBitrateBps(remb_bitrate_); remb.SetSsrcs(remb_ssrcs_); sender.AppendPacket(remb); @@ -452,12 +459,12 @@ void RTCPSender::BuildTMMBR(const RtcpContext& ctx, PacketSender& sender) { if (!tmmbr_owner) { // Use received bounding set as candidate set. // Add current tuple. - candidates.emplace_back(ssrc_, tmmbr_send_bps_, packet_oh_send_); + candidates.emplace_back(ComputeSsrc(), tmmbr_send_bps_, packet_oh_send_); // Find bounding set. std::vector bounding = TMMBRHelp::FindBoundingSet(std::move(candidates)); - tmmbr_owner = TMMBRHelp::IsOwner(bounding, ssrc_); + tmmbr_owner = TMMBRHelp::IsOwner(bounding, ComputeSsrc()); if (!tmmbr_owner) { // Did not enter bounding set, no meaning to send this request. return; @@ -469,7 +476,7 @@ void RTCPSender::BuildTMMBR(const RtcpContext& ctx, PacketSender& sender) { return; rtcp::Tmmbr tmmbr; - tmmbr.SetSenderSsrc(ssrc_); + tmmbr.SetSenderSsrc(ComputeSsrc()); rtcp::TmmbItem request; request.set_ssrc(remote_ssrc_); request.set_bitrate_bps(tmmbr_send_bps_); @@ -481,7 +488,7 @@ void RTCPSender::BuildTMMBR(const RtcpContext& ctx, PacketSender& sender) { void RTCPSender::BuildTMMBN(const RtcpContext& /* ctx */, PacketSender& sender) { rtcp::Tmmbn tmmbn; - tmmbn.SetSenderSsrc(ssrc_); + tmmbn.SetSenderSsrc(ComputeSsrc()); for (const rtcp::TmmbItem& tmmbr : tmmbn_to_send_) { if (tmmbr.bitrate_bps() > 0) { tmmbn.AddTmmbr(tmmbr); @@ -492,20 +499,20 @@ void RTCPSender::BuildTMMBN(const RtcpContext& /* ctx */, void RTCPSender::BuildAPP(const RtcpContext& /* ctx */, PacketSender& sender) { rtcp::App app; - app.SetSenderSsrc(ssrc_); + app.SetSenderSsrc(ComputeSsrc()); sender.AppendPacket(app); } void RTCPSender::BuildLossNotification(const RtcpContext& /* ctx */, PacketSender& sender) { - loss_notification_.SetSenderSsrc(ssrc_); + loss_notification_.SetSenderSsrc(ComputeSsrc()); loss_notification_.SetMediaSsrc(remote_ssrc_); sender.AppendPacket(loss_notification_); } void RTCPSender::BuildNACK(const RtcpContext& ctx, PacketSender& sender) { rtcp::Nack nack; - nack.SetSenderSsrc(ssrc_); + nack.SetSenderSsrc(ComputeSsrc()); nack.SetMediaSsrc(remote_ssrc_); nack.SetPacketIds(ctx.nacks_.data(), ctx.nacks_.size()); @@ -522,7 +529,7 @@ void RTCPSender::BuildNACK(const RtcpContext& ctx, PacketSender& sender) { void RTCPSender::BuildBYE(const RtcpContext& /* ctx */, PacketSender& sender) { rtcp::Bye bye; - bye.SetSenderSsrc(ssrc_); + bye.SetSenderSsrc(ComputeSsrc()); bye.SetCsrcs(csrcs_); sender.AppendPacket(bye); } @@ -530,7 +537,7 @@ void RTCPSender::BuildBYE(const RtcpContext& /* ctx */, PacketSender& sender) { void RTCPSender::BuildExtendedReports(const RtcpContext& ctx, PacketSender& sender) { rtcp::ExtendedReports xr; - xr.SetSenderSsrc(ssrc_); + xr.SetSenderSsrc(ComputeSsrc()); if (!sending_ && xr_send_receiver_reference_time_enabled_) { rtcp::Rrtr rrtr; @@ -545,8 +552,8 @@ void RTCPSender::BuildExtendedReports(const RtcpContext& ctx, if (send_video_bitrate_allocation_) { rtcp::TargetBitrate target_bitrate; - for (int sl = 0; sl < kMaxSpatialLayers; ++sl) { - for (int tl = 0; tl < kMaxTemporalStreams; ++tl) { + for (size_t sl = 0; sl < kMaxSpatialLayers; ++sl) { + for (size_t tl = 0; tl < kMaxTemporalStreams; ++tl) { if (video_bitrate_allocation_.HasBitrate(sl, tl)) { target_bitrate.AddTargetBitrate( sl, tl, video_bitrate_allocation_.GetBitrate(sl, tl) / 1000); @@ -562,9 +569,9 @@ void RTCPSender::BuildExtendedReports(const RtcpContext& ctx, int32_t RTCPSender::SendRTCP(const FeedbackState& feedback_state, RTCPPacketType packet_type, - ArrayView nacks) { + std::span nacks) { int32_t error_code = -1; - auto callback = [&](ArrayView packet) { + auto callback = [&](std::span packet) { if (transport_->SendRtcp(packet, /*packet_options=*/{})) { error_code = 0; env_.event_log().Log( @@ -586,10 +593,19 @@ int32_t RTCPSender::SendRTCP(const FeedbackState& feedback_state, return error_code; } +uint32_t RTCPSender::ComputeSsrc() const { + if (is_rtp_sender_) { + return send_ssrc_; + } else { + RTC_DCHECK(recv_ssrc_callback_); + return recv_ssrc_callback_(); + } +} + std::optional RTCPSender::ComputeCompoundRTCPPacket( const FeedbackState& feedback_state, RTCPPacketType packet_type, - ArrayView nacks, + std::span nacks, PacketSender& sender) { if (method_ == RtcpMode::kOff) { RTC_LOG(LS_WARNING) << "Can't send RTCP if it is disabled."; @@ -810,7 +826,7 @@ void RTCPSender::SetVideoBitrateAllocation( CheckAndUpdateLayerStructure(bitrate); if (new_bitrate) { video_bitrate_allocation_ = *new_bitrate; - RTC_LOG(LS_INFO) << "Emitting TargetBitrate XR for SSRC " << ssrc_ + RTC_LOG(LS_INFO) << "Emitting TargetBitrate XR for SSRC " << ComputeSsrc() << " with new layers enabled/disabled: " << video_bitrate_allocation_.ToString(); SetNextRtcpSendEvaluationDuration(TimeDelta::Zero()); @@ -857,10 +873,10 @@ void RTCPSender::SendCombinedRtcpPacket( } max_packet_size = max_packet_size_; - ssrc = ssrc_; + ssrc = ComputeSsrc(); } RTC_DCHECK_LE(max_packet_size, IP_PACKET_SIZE); - auto callback = [&](ArrayView packet) { + auto callback = [&](std::span packet) { if (transport_->SendRtcp(packet, /*packet_options=*/{})) { env_.event_log().Log( std::make_unique(packet)); diff --git a/modules/rtp_rtcp/source/rtcp_sender.h b/modules/rtp_rtcp/source/rtcp_sender.h index b2ad982f280..7fa3ce9a337 100644 --- a/modules/rtp_rtcp/source/rtcp_sender.h +++ b/modules/rtp_rtcp/source/rtcp_sender.h @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/rtp_headers.h" @@ -53,9 +53,12 @@ class RTCPSender final { // True for a audio version of the RTP/RTCP module object false will create // a video version. bool audio = false; - // SSRCs for media and retransmission, respectively. - // FlexFec SSRC is fetched from `flexfec_sender`. + // SSRC for sending media. uint32_t local_media_ssrc = 0; + // Function for fetching SSRC for sending RTCP reports when this object + // belongs to a ModuleRtpRtcp2 that is not used for sending RTP. + absl::AnyInvocable recv_ssrc_callback; + // FlexFec SSRC is fetched from `flexfec_sender`. // Transport object that will be called when packets are ready to be sent // out on the network. @@ -137,7 +140,7 @@ class RTCPSender final { int32_t SendRTCP(const FeedbackState& feedback_state, RTCPPacketType packetType, - ArrayView nacks = {}) + std::span nacks = {}) RTC_LOCKS_EXCLUDED(mutex_rtcp_sender_); int32_t SendLossNotification(const FeedbackState& feedback_state, @@ -175,10 +178,12 @@ class RTCPSender final { class RtcpContext; class PacketSender; + uint32_t ComputeSsrc() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_); + std::optional ComputeCompoundRTCPPacket( const FeedbackState& feedback_state, RTCPPacketType packet_type, - ArrayView nacks, + std::span nacks, PacketSender& sender) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_); TimeDelta ComputeTimeUntilNextReport(DataRate send_bitrate) @@ -224,12 +229,19 @@ class RTCPSender final { RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_rtcp_sender_); const Environment env_; + const bool is_rtp_sender_; const bool audio_; // TODO(bugs.webrtc.org/11581): `mutex_rtcp_sender_` shouldn't be required if // we consistently run network related operations on the network thread. // This is currently not possible due to callbacks from the process thread in // ModuleRtpRtcpImpl2. - uint32_t ssrc_ RTC_GUARDED_BY(mutex_rtcp_sender_); + // The SSRC used for sending when is_rtp_sender_ is true + // and sending_ is true. + uint32_t send_ssrc_ RTC_GUARDED_BY(mutex_rtcp_sender_); + // The function used for getting the right SSRC to send from + // when the RTCPSender is used with a ModuleRtpRtcp that is not + // configured for sending RTP (is_rtp_sender_ is false). + absl::AnyInvocable recv_ssrc_callback_; Random random_ RTC_GUARDED_BY(mutex_rtcp_sender_); RtcpMode method_ RTC_GUARDED_BY(mutex_rtcp_sender_); diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc index 5d0b47e262f..abe4761bd0a 100644 --- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -75,11 +75,11 @@ class TestTransport : public Transport { public: TestTransport() {} - bool SendRtp(ArrayView /*data*/, + bool SendRtp(std::span /*data*/, const PacketOptions& /* options */) override { return false; } - bool SendRtcp(ArrayView data, + bool SendRtcp(std::span data, const PacketOptions& options) override { EXPECT_FALSE(options.is_media); parser_.Parse(data); @@ -99,7 +99,9 @@ class RtcpSenderTest : public ::testing::Test { : clock_(1335900000), env_(CreateEnvironment(&clock_)), receive_statistics_(ReceiveStatistics::Create(&clock_)), - rtp_rtcp_impl_(env_, GetDefaultRtpRtcpConfig()) {} + rtp_rtcp_impl_( + ModuleRtpRtcpImpl2::CreateSendModule(env_, + GetDefaultRtpRtcpConfig())) {} RTCPSender::Configuration GetDefaultConfig() { RTCPSender::Configuration configuration; @@ -147,7 +149,7 @@ class RtcpSenderTest : public ::testing::Test { test::RtcpPacketParser* parser() { return &test_transport_.parser_; } RTCPSender::FeedbackState feedback_state() { - return rtp_rtcp_impl_.GetFeedbackState(); + return rtp_rtcp_impl_->GetFeedbackState(); } AutoThread main_thread_; @@ -155,7 +157,7 @@ class RtcpSenderTest : public ::testing::Test { const Environment env_; TestTransport test_transport_; std::unique_ptr receive_statistics_; - ModuleRtpRtcpImpl2 rtp_rtcp_impl_; + const std::unique_ptr rtp_rtcp_impl_; }; TEST_F(RtcpSenderTest, SetRtcpStatus) { @@ -183,7 +185,7 @@ TEST_F(RtcpSenderTest, SendSr) { const uint32_t kOctetCount = 0x23456; auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); - RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); rtcp_sender->SetSendingStatus(feedback_state, true); feedback_state.packets_sent = kPacketCount; feedback_state.media_bytes_sent = kOctetCount; @@ -208,7 +210,7 @@ TEST_F(RtcpSenderTest, SendConsecutiveSrWithExactSlope) { // Make sure clock is not exactly at some milliseconds point. clock_.AdvanceTimeMicroseconds(kTimeBetweenSRsUs); rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); - RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); rtcp_sender->SetSendingStatus(feedback_state, true); feedback_state.packets_sent = kPacketCount; feedback_state.media_bytes_sent = kOctetCount; @@ -487,7 +489,7 @@ TEST_F(RtcpSenderTest, RembIncludedInEachCompoundPacketAfterSet) { TEST_F(RtcpSenderTest, SendXrWithDlrr) { auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); - RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); rtcp::ReceiveTimeInfo last_xr_rr; last_xr_rr.ssrc = 0x11111111; last_xr_rr.last_rr = 0x22222222; @@ -507,7 +509,7 @@ TEST_F(RtcpSenderTest, SendXrWithMultipleDlrrSubBlocks) { const size_t kNumReceivers = 2; auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); - RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_.GetFeedbackState(); + RTCPSender::FeedbackState feedback_state = rtp_rtcp_impl_->GetFeedbackState(); for (size_t i = 0; i < kNumReceivers; ++i) { rtcp::ReceiveTimeInfo last_xr_rr; last_xr_rr.ssrc = i; @@ -667,7 +669,7 @@ TEST_F(RtcpSenderTest, SendsTmmbnIfSetAndEmpty) { TEST_F(RtcpSenderTest, ByeMustBeLast) { MockTransport mock_transport; EXPECT_CALL(mock_transport, SendRtcp(_, _)) - .WillOnce([](ArrayView data, ::testing::Unused) { + .WillOnce([](std::span data, ::testing::Unused) { const uint8_t* next_packet = data.data(); const uint8_t* const packet_end = data.data() + data.size(); rtcp::CommonHeader packet; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.h b/modules/rtp_rtcp/source/rtcp_transceiver_config.h index 37500fb13fe..8fc7d6374a0 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/task_queue/task_queue_base.h" #include "api/units/time_delta.h" @@ -81,7 +81,7 @@ class RtpStreamRtcpHandler { virtual RtpStats SentStats() = 0; virtual void OnNack(uint32_t /* sender_ssrc */, - ArrayView /* sequence_numbers */) {} + std::span /* sequence_numbers */) {} virtual void OnFir(uint32_t /* sender_ssrc */) {} virtual void OnPli(uint32_t /* sender_ssrc */) {} @@ -117,7 +117,7 @@ struct RtcpTransceiverConfig { Clock* clock = nullptr; // Transport to send RTCP packets to. - std::function)> rtcp_transport; + std::function)> rtcp_transport; // Queue for scheduling delayed tasks, e.g. sending periodic compound packets. TaskQueueBase* task_queue = nullptr; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc index 4f077fffc80..41144156a68 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/task_queue/task_queue_base.h" #include "api/units/data_rate.h" @@ -70,7 +70,7 @@ struct SenderReportTimes { NtpTime remote_sent_time; }; -std::function)> GetRtcpTransport( +std::function)> GetRtcpTransport( const RtcpTransceiverConfig& config) { if (config.rtcp_transport != nullptr) { return config.rtcp_transport; @@ -78,7 +78,7 @@ std::function)> GetRtcpTransport( bool first = true; std::string log_prefix = config.debug_id; - return [first, log_prefix](ArrayView /* packet */) mutable { + return [first, log_prefix](std::span /* packet */) mutable { if (first) { RTC_LOG(LS_ERROR) << log_prefix << "Sending RTCP packets is disabled."; first = false; @@ -126,7 +126,7 @@ class RtcpTransceiverImpl::PacketSender { // Sends pending rtcp compound packet. void Send() { if (index_ > 0) { - callback_(ArrayView(buffer_, index_)); + callback_(std::span(buffer_, index_)); index_ = 0; } } @@ -215,7 +215,7 @@ void RtcpTransceiverImpl::SetReadyToSend(bool ready) { ready_to_send_ = ready; } -void RtcpTransceiverImpl::ReceivePacket(ArrayView packet, +void RtcpTransceiverImpl::ReceivePacket(std::span packet, Timestamp now) { // Report blocks may be spread across multiple sender and receiver reports. std::vector report_blocks; @@ -227,7 +227,7 @@ void RtcpTransceiverImpl::ReceivePacket(ArrayView packet, HandleReceivedPacket(rtcp_block, now, report_blocks); - packet = packet.subview(rtcp_block.packet_size()); + packet = packet.subspan(rtcp_block.packet_size()); } if (!report_blocks.empty()) { @@ -288,7 +288,7 @@ void RtcpTransceiverImpl::SendPictureLossIndication(uint32_t ssrc) { SendImmediateFeedback(pli); } -void RtcpTransceiverImpl::SendFullIntraRequest(ArrayView ssrcs, +void RtcpTransceiverImpl::SendFullIntraRequest(std::span ssrcs, bool new_request) { RTC_DCHECK(!ssrcs.empty()); if (!ready_to_send_) @@ -377,7 +377,7 @@ void RtcpTransceiverImpl::HandleReceiverReport( void RtcpTransceiverImpl::HandleReportBlocks( uint32_t sender_ssrc, Timestamp now, - ArrayView rtcp_report_blocks, + std::span rtcp_report_blocks, std::vector& report_blocks) { if (rtcp_report_blocks.empty()) { return; @@ -575,7 +575,7 @@ void RtcpTransceiverImpl::HandleDlrr(const rtcp::Dlrr& dlrr, Timestamp now) { void RtcpTransceiverImpl::ProcessReportBlocks( Timestamp now, - ArrayView report_blocks) { + std::span report_blocks) { RTC_DCHECK(!report_blocks.empty()); if (config_.network_link_observer == nullptr) { return; @@ -837,7 +837,7 @@ void RtcpTransceiverImpl::CreateCompoundPacket(Timestamp now, sender.AppendPacket(xr_with_rrtr); } if (xr_with_dlrr.has_value()) { - ArrayView ssrcs(&sender_ssrc, 1); + std::span ssrcs(&sender_ssrc, 1); if (config_.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs && !sender_ssrcs.empty()) { ssrcs = sender_ssrcs; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h index adc9cc5283a..c7249d30c5c 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/rtp_rtcp/include/report_block_data.h" @@ -59,7 +59,7 @@ class RtcpTransceiverImpl { void SetReadyToSend(bool ready); - void ReceivePacket(ArrayView packet, Timestamp now); + void ReceivePacket(std::span packet, Timestamp now); void SendCompoundPacket(); @@ -71,7 +71,7 @@ class RtcpTransceiverImpl { void SendPictureLossIndication(uint32_t ssrc); // If new_request is true then requested sequence no. will increase for each // requested ssrc. - void SendFullIntraRequest(ArrayView ssrcs, bool new_request); + void SendFullIntraRequest(std::span ssrcs, bool new_request); // SendCombinedRtcpPacket ignores rtcp mode and does not send a compound // message. https://tools.ietf.org/html/rfc4585#section-3.1 @@ -103,7 +103,7 @@ class RtcpTransceiverImpl { std::vector& report_blocks); void HandleReportBlocks(uint32_t sender_ssrc, Timestamp now, - ArrayView rtcp_report_blocks, + std::span rtcp_report_blocks, std::vector& report_blocks); void HandlePayloadSpecificFeedback( const rtcp::CommonHeader& rtcp_packet_header, @@ -126,7 +126,7 @@ class RtcpTransceiverImpl { void HandleTargetBitrate(const rtcp::TargetBitrate& target_bitrate, uint32_t remote_ssrc); void ProcessReportBlocks(Timestamp now, - ArrayView report_blocks); + std::span report_blocks); void ReschedulePeriodicCompoundPackets(); void SchedulePeriodicCompoundPackets(TimeDelta delay); @@ -156,7 +156,7 @@ class RtcpTransceiverImpl { size_t num_max_blocks); const RtcpTransceiverConfig config_; - std::function)> rtcp_transport_; + std::function)> rtcp_transport_; bool ready_to_send_; std::optional remb_; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc index be485b87dac..4f108781f87 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/task_queue/task_queue_base.h" #include "api/task_queue/task_queue_factory.h" @@ -106,10 +106,7 @@ class MockRtpStreamRtcpHandler : public RtpStreamRtcpHandler { } MOCK_METHOD(RtpStats, SentStats, (), (override)); - MOCK_METHOD(void, - OnNack, - (uint32_t, ArrayView), - (override)); + MOCK_METHOD(void, OnNack, (uint32_t, std::span), (override)); MOCK_METHOD(void, OnFir, (uint32_t), (override)); MOCK_METHOD(void, OnPli, (uint32_t), (override)); MOCK_METHOD(void, OnReport, (const ReportBlockData&), (override)); @@ -131,8 +128,8 @@ class FakeRtcpTransport { public: explicit FakeRtcpTransport(TimeController& time) : time_(time) {} - std::function)> AsStdFunction() { - return [this](ArrayView) { sent_rtcp_ = true; }; + std::function)> AsStdFunction() { + return [this](std::span) { sent_rtcp_ = true; }; } // Returns true when packet was received by the transport. @@ -148,9 +145,9 @@ class FakeRtcpTransport { bool sent_rtcp_ = false; }; -std::function)> RtcpParserTransport( +std::function)> RtcpParserTransport( RtcpPacketParser& parser) { - return [&parser](ArrayView packet) { + return [&parser](std::span packet) { return parser.Parse(packet); }; } @@ -174,7 +171,7 @@ class RtcpTransceiverImplTest : public ::testing::Test { void AdvanceTime(TimeDelta time) { time_->AdvanceTime(time); } std::unique_ptr CreateTaskQueue() { return time_->GetTaskQueueFactory()->CreateTaskQueue( - "rtcp", TaskQueueFactory::Priority::NORMAL); + "rtcp", TaskQueueFactory::Priority::kNormal); } private: @@ -343,7 +340,7 @@ TEST_F(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { } TEST_F(RtcpTransceiverImplTest, SendsNoRtcpWhenNetworkStateIsDown) { - MockFunction)> mock_transport; + MockFunction)> mock_transport; RtcpTransceiverConfig config = DefaultTestConfig(); config.initial_ready_to_send = false; config.rtcp_transport = mock_transport.AsStdFunction(); @@ -360,7 +357,7 @@ TEST_F(RtcpTransceiverImplTest, SendsNoRtcpWhenNetworkStateIsDown) { } TEST_F(RtcpTransceiverImplTest, SendsRtcpWhenNetworkStateIsUp) { - MockFunction)> mock_transport; + MockFunction)> mock_transport; RtcpTransceiverConfig config = DefaultTestConfig(); config.initial_ready_to_send = false; config.rtcp_transport = mock_transport.AsStdFunction(); @@ -426,7 +423,7 @@ TEST_F(RtcpTransceiverImplTest, SendsMinimalCompoundPacket) { } TEST_F(RtcpTransceiverImplTest, AvoidsEmptyPacketsInReducedMode) { - MockFunction)> transport; + MockFunction)> transport; EXPECT_CALL(transport, Call).Times(0); NiceMock receive_statistics; @@ -1632,10 +1629,10 @@ TEST_F(RtcpTransceiverImplTest, RotatesSendersWhenAllSenderReportDoNotFit) { rtcp_receiver.AddMediaReceiverRtcpObserver(kSenderSsrc[i], &receiver[i]); } - MockFunction)> transport; + MockFunction)> transport; EXPECT_CALL(transport, Call) .Times(kNumSenders) - .WillRepeatedly([&](ArrayView packet) { + .WillRepeatedly([&](std::span packet) { rtcp_receiver.ReceivePacket(packet, CurrentTime()); return true; }); diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc index b5de513c68e..51cc3684191 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/rtp_rtcp/source/rtcp_packet.h" @@ -69,7 +69,7 @@ void WaitPostedTasks(TaskQueueForTest* queue) { TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOffTaskQueue) { SimulatedClock clock(0); - MockFunction)> outgoing_transport; + MockFunction)> outgoing_transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -87,7 +87,7 @@ TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOffTaskQueue) { TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOnTaskQueue) { SimulatedClock clock(0); - MockFunction)> outgoing_transport; + MockFunction)> outgoing_transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -108,7 +108,7 @@ TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOnTaskQueue) { TEST(RtcpTransceiverTest, CanBeDestroyedOnTaskQueue) { SimulatedClock clock(0); - MockFunction)> outgoing_transport; + MockFunction)> outgoing_transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -148,7 +148,7 @@ TEST(RtcpTransceiverTest, CanBeDestroyedWithoutBlocking) { TEST(RtcpTransceiverTest, MaySendPacketsAfterDestructor) { // i.e. Be careful! SimulatedClock clock(0); // Must outlive queue below. - NiceMock)>> transport; + NiceMock)>> transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -232,7 +232,7 @@ TEST(RtcpTransceiverTest, RemoveMediaReceiverRtcpObserverIsNonBlocking) { TEST(RtcpTransceiverTest, CanCallSendCompoundPacketFromAnyThread) { SimulatedClock clock(0); - MockFunction)> outgoing_transport; + MockFunction)> outgoing_transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -263,8 +263,7 @@ TEST(RtcpTransceiverTest, CanCallSendCompoundPacketFromAnyThread) { TEST(RtcpTransceiverTest, DoesntSendPacketsAfterStopCallback) { SimulatedClock clock(0); - NiceMock)>> - outgoing_transport; + NiceMock)>> outgoing_transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -287,7 +286,7 @@ TEST(RtcpTransceiverTest, SendsCombinedRtcpPacketOnTaskQueue) { static constexpr uint32_t kSenderSsrc = 12345; SimulatedClock clock(0); - MockFunction)> outgoing_transport; + MockFunction)> outgoing_transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -298,7 +297,7 @@ TEST(RtcpTransceiverTest, SendsCombinedRtcpPacketOnTaskQueue) { RtcpTransceiver rtcp_transceiver(config); EXPECT_CALL(outgoing_transport, Call) - .WillOnce([&](webrtc::ArrayView buffer) { + .WillOnce([&](std::span buffer) { EXPECT_TRUE(queue.IsCurrent()); RtcpPacketParser rtcp_parser; rtcp_parser.Parse(buffer); @@ -326,7 +325,7 @@ TEST(RtcpTransceiverTest, SendFrameIntraRequestDefaultsToNewRequest) { static constexpr uint32_t kSenderSsrc = 12345; SimulatedClock clock(0); - MockFunction)> outgoing_transport; + MockFunction)> outgoing_transport; TaskQueueForTest queue("rtcp"); RtcpTransceiverConfig config; config.clock = &clock; @@ -338,7 +337,7 @@ TEST(RtcpTransceiverTest, SendFrameIntraRequestDefaultsToNewRequest) { uint8_t first_seq_nr; EXPECT_CALL(outgoing_transport, Call) - .WillOnce([&](webrtc::ArrayView buffer) { + .WillOnce([&](std::span buffer) { EXPECT_TRUE(queue.IsCurrent()); RtcpPacketParser rtcp_parser; rtcp_parser.Parse(buffer); @@ -346,7 +345,7 @@ TEST(RtcpTransceiverTest, SendFrameIntraRequestDefaultsToNewRequest) { first_seq_nr = rtcp_parser.fir()->requests()[0].seq_nr; return true; }) - .WillOnce([&](webrtc::ArrayView buffer) { + .WillOnce([&](std::span buffer) { EXPECT_TRUE(queue.IsCurrent()); RtcpPacketParser rtcp_parser; rtcp_parser.Parse(buffer); diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc index 7a8b25d8813..5b92c6466ea 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.h" #include "modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.h" @@ -23,7 +23,7 @@ namespace webrtc { bool RtpDependencyDescriptorExtension::Parse( - ArrayView data, + std::span data, const FrameDependencyStructure* structure, DependencyDescriptor* descriptor) { RtpDependencyDescriptorReader reader(data, structure, descriptor); @@ -40,7 +40,7 @@ size_t RtpDependencyDescriptorExtension::ValueSize( } bool RtpDependencyDescriptorExtension::Write( - ArrayView data, + std::span data, const FrameDependencyStructure& structure, std::bitset<32> active_chains, const DependencyDescriptor& descriptor) { @@ -50,7 +50,7 @@ bool RtpDependencyDescriptorExtension::Write( } bool RtpDependencyDescriptorExtension::Parse( - ArrayView data, + std::span data, DependencyDescriptorMandatory* descriptor) { if (data.size() < 3) { return false; diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h index 97d421eb4d7..c757e40d64b 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h @@ -13,9 +13,9 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "api/transport/rtp/dependency_descriptor.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -30,13 +30,13 @@ class RtpDependencyDescriptorExtension { return RtpExtension::kDependencyDescriptorUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, const FrameDependencyStructure* structure, DependencyDescriptor* descriptor); // Reads the mandatory part of the descriptor. // Such read is stateless, i.e., doesn't require `FrameDependencyStructure`. - static bool Parse(ArrayView data, + static bool Parse(std::span data, DependencyDescriptorMandatory* descriptor); static size_t ValueSize(const FrameDependencyStructure& structure, @@ -46,12 +46,12 @@ class RtpDependencyDescriptorExtension { static size_t ValueSize(const FrameDependencyStructure& structure, std::bitset<32> active_chains, const DependencyDescriptor& descriptor); - static bool Write(ArrayView data, + static bool Write(std::span data, const FrameDependencyStructure& structure, const DependencyDescriptor& descriptor) { return Write(data, structure, kAllChainsAreActive, descriptor); } - static bool Write(ArrayView data, + static bool Write(std::span data, const FrameDependencyStructure& structure, std::bitset<32> active_chains, const DependencyDescriptor& descriptor); diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension_unittest.cc b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension_unittest.cc index ec64e819afd..9b054e7dc61 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_extension_unittest.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "test/gmock.h" #include "test/gtest.h" @@ -64,7 +64,7 @@ TEST(RtpDependencyDescriptorExtensionTest, WriteZeroInUnusedBits) { const uint8_t* unused_bytes = buffer + value_size; size_t num_unused_bytes = buffer + sizeof(buffer) - unused_bytes; // Check remaining bytes are zeroed. - EXPECT_THAT(MakeArrayView(unused_bytes, num_unused_bytes), Each(0)); + EXPECT_THAT(std::span(unused_bytes, num_unused_bytes), Each(0)); } // In practice chain diff for inactive chain will grow uboundly because no diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.cc b/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.cc index 2d1f06df152..ad20ce321ec 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.cc +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/checks.h" @@ -24,7 +24,7 @@ namespace webrtc { RtpDependencyDescriptorReader::RtpDependencyDescriptorReader( - ArrayView raw_data, + std::span raw_data, const FrameDependencyStructure* structure, DependencyDescriptor* descriptor) : descriptor_(descriptor), buffer_(raw_data) { diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.h b/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.h index a350428db0f..b493fb66602 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.h +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_reader.h @@ -11,8 +11,8 @@ #define MODULES_RTP_RTCP_SOURCE_RTP_DEPENDENCY_DESCRIPTOR_READER_H_ #include +#include -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "rtc_base/bitstream_reader.h" @@ -21,7 +21,7 @@ namespace webrtc { class RtpDependencyDescriptorReader { public: // Parses the dependency descriptor. - RtpDependencyDescriptorReader(ArrayView raw_data, + RtpDependencyDescriptorReader(std::span raw_data, const FrameDependencyStructure* structure, DependencyDescriptor* descriptor); RtpDependencyDescriptorReader(const RtpDependencyDescriptorReader&) = delete; diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.cc b/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.cc index d7b309d5c6c..3d126050044 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.cc +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "api/video/render_resolution.h" #include "rtc_base/bit_buffer.h" @@ -57,7 +57,7 @@ NextLayerIdc GetNextLayerIdc(const FrameDependencyTemplate& previous, } // namespace RtpDependencyDescriptorWriter::RtpDependencyDescriptorWriter( - ArrayView data, + std::span data, const FrameDependencyStructure& structure, std::bitset<32> active_chains, const DependencyDescriptor& descriptor) diff --git a/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.h b/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.h index 4a6b362adea..1feb21ec614 100644 --- a/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.h +++ b/modules/rtp_rtcp/source/rtp_dependency_descriptor_writer.h @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "rtc_base/bit_buffer.h" @@ -24,7 +24,7 @@ class RtpDependencyDescriptorWriter { public: // Assumes `structure` and `descriptor` are valid and // `descriptor` matches the `structure`. - RtpDependencyDescriptorWriter(ArrayView data, + RtpDependencyDescriptorWriter(std::span data, const FrameDependencyStructure& structure, std::bitset<32> active_chains, const DependencyDescriptor& descriptor); diff --git a/modules/rtp_rtcp/source/rtp_format.cc b/modules/rtp_rtcp/source/rtp_format.cc index fda60ffac9e..5d3c880aff5 100644 --- a/modules/rtp_rtcp/source/rtp_format.cc +++ b/modules/rtp_rtcp/source/rtp_format.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format_h264.h" #include "modules/rtp_rtcp/source/rtp_format_video_generic.h" #include "modules/rtp_rtcp/source/rtp_format_vp8.h" @@ -32,7 +32,7 @@ namespace webrtc { std::unique_ptr RtpPacketizer::Create( PacketizationFormat format, - ArrayView payload, + std::span payload, PayloadSizeLimits limits, // Codec-specific details. const RTPVideoHeader& rtp_video_header) { diff --git a/modules/rtp_rtcp/source/rtp_format.h b/modules/rtp_rtcp/source/rtp_format.h index 6b850cda43e..e329109e82c 100644 --- a/modules/rtp_rtcp/source/rtp_format.h +++ b/modules/rtp_rtcp/source/rtp_format.h @@ -15,9 +15,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" namespace webrtc { @@ -45,7 +45,7 @@ class RtpPacketizer { }; static std::unique_ptr Create( PacketizationFormat format, - ArrayView payload, + std::span payload, PayloadSizeLimits limits, // Codec-specific details. const RTPVideoHeader& rtp_video_header); diff --git a/modules/rtp_rtcp/source/rtp_format_h264.cc b/modules/rtp_rtcp/source/rtp_format_h264.cc index b7798592035..ba4f4feb17c 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264.cc +++ b/modules/rtp_rtcp/source/rtp_format_h264.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" @@ -33,7 +33,7 @@ constexpr size_t kLengthFieldSize = 2; } // namespace -RtpPacketizerH264::RtpPacketizerH264(ArrayView payload, +RtpPacketizerH264::RtpPacketizerH264(std::span payload, PayloadSizeLimits limits, H264PacketizationMode packetization_mode) : limits_(limits), num_packets_left_(0) { @@ -43,11 +43,11 @@ RtpPacketizerH264::RtpPacketizerH264(ArrayView payload, for (const auto& nalu : H264::FindNaluIndices(payload)) { input_fragments_.push_back( - payload.subview(nalu.payload_start_offset, nalu.payload_size)); + payload.subspan(nalu.payload_start_offset, nalu.payload_size)); } bool has_empty_fragments = absl::c_any_of( input_fragments_, - [](const ArrayView fragment) { return fragment.empty(); }); + [](const std::span fragment) { return fragment.empty(); }); if (has_empty_fragments || !GeneratePackets(packetization_mode)) { // If empty fragments were found or we failed to generate all the packets, // discard already generated packets in case the caller would ignore the @@ -100,7 +100,7 @@ bool RtpPacketizerH264::GeneratePackets( bool RtpPacketizerH264::PacketizeFuA(size_t fragment_index) { // Fragment payload into packets (FU-A). - ArrayView fragment = input_fragments_[fragment_index]; + std::span fragment = input_fragments_[fragment_index]; PayloadSizeLimits limits = limits_; // Leave room for the FU-A header. @@ -134,7 +134,7 @@ bool RtpPacketizerH264::PacketizeFuA(size_t fragment_index) { for (size_t i = 0; i < payload_sizes.size(); ++i) { int packet_length = payload_sizes[i]; RTC_CHECK_GT(packet_length, 0); - packets_.push(PacketUnit(fragment.subview(offset, packet_length), + packets_.push(PacketUnit(fragment.subspan(offset, packet_length), /*first_fragment=*/i == 0, /*last_fragment=*/i == payload_sizes.size() - 1, false, fragment[0])); @@ -151,7 +151,7 @@ size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) { size_t payload_size_left = limits_.max_payload_len; int aggregated_fragments = 0; size_t fragment_headers_length = 0; - ArrayView fragment = input_fragments_[fragment_index]; + std::span fragment = input_fragments_[fragment_index]; RTC_CHECK_GE(payload_size_left, fragment.size()); ++num_packets_left_; @@ -205,7 +205,7 @@ bool RtpPacketizerH264::PacketizeSingleNalu(size_t fragment_index) { payload_size_left -= limits_.first_packet_reduction_len; else if (fragment_index + 1 == input_fragments_.size()) payload_size_left -= limits_.last_packet_reduction_len; - ArrayView fragment = input_fragments_[fragment_index]; + std::span fragment = input_fragments_[fragment_index]; if (payload_size_left < fragment.size()) { RTC_LOG(LS_ERROR) << "Failed to fit a fragment to packet in SingleNalu " "packetization mode. Payload size left " @@ -257,7 +257,7 @@ void RtpPacketizerH264::NextAggregatePacket(RtpPacketToSend* rtp_packet) { size_t index = kNalHeaderSize; bool is_last_fragment = packet->last_fragment; while (packet->aggregated) { - ArrayView fragment = packet->source_fragment; + std::span fragment = packet->source_fragment; RTC_CHECK_LE(index + kLengthFieldSize + fragment.size(), payload_capacity); // Add NAL unit length field. ByteWriter::WriteBigEndian(&buffer[index], fragment.size()); @@ -290,7 +290,7 @@ void RtpPacketizerH264::NextFragmentPacket(RtpPacketToSend* rtp_packet) { fu_header |= (packet->last_fragment ? kH264EBit : 0); uint8_t type = packet->header & kH264TypeMask; fu_header |= type; - ArrayView fragment = packet->source_fragment; + std::span fragment = packet->source_fragment; uint8_t* buffer = rtp_packet->AllocatePayload(kFuAHeaderSize + fragment.size()); buffer[0] = fu_indicator; diff --git a/modules/rtp_rtcp/source/rtp_format_h264.h b/modules/rtp_rtcp/source/rtp_format_h264.h index def175aaf97..0af5ba51cb3 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264.h +++ b/modules/rtp_rtcp/source/rtp_format_h264.h @@ -16,8 +16,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/h264/include/h264_globals.h" @@ -38,7 +38,7 @@ class RtpPacketizerH264 : public RtpPacketizer { public: // Initialize with payload from encoder. // The payload_data must be exactly one encoded H264 frame. - RtpPacketizerH264(ArrayView payload, + RtpPacketizerH264(std::span payload, PayloadSizeLimits limits, H264PacketizationMode packetization_mode); @@ -62,7 +62,7 @@ class RtpPacketizerH264 : public RtpPacketizer { // packet unit may represent a single NAL unit or a STAP-A packet, of which // there may be multiple in a single RTP packet (if so, aggregated = true). struct PacketUnit { - PacketUnit(ArrayView source_fragment, + PacketUnit(std::span source_fragment, bool first_fragment, bool last_fragment, bool aggregated, @@ -73,7 +73,7 @@ class RtpPacketizerH264 : public RtpPacketizer { aggregated(aggregated), header(header) {} - ArrayView source_fragment; + std::span source_fragment; bool first_fragment; bool last_fragment; bool aggregated; @@ -90,7 +90,7 @@ class RtpPacketizerH264 : public RtpPacketizer { const PayloadSizeLimits limits_; size_t num_packets_left_; - std::deque> input_fragments_; + std::deque> input_fragments_; std::queue packets_; }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc index ff4a20ea2e6..15e6817155b 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/h264/include/h264_globals.h" @@ -57,60 +57,70 @@ constexpr size_t kFuAHeaderSize = 2; // Creates Buffer that looks like nal unit of given size. Buffer GenerateNalUnit(size_t size) { RTC_CHECK_GT(size, 0); - Buffer buffer = Buffer::CreateUninitializedWithSize(size); - // Set some valid header. - buffer[0] = kSlice; - for (size_t i = 1; i < size; ++i) { - buffer[i] = static_cast(i); - } - // Last byte shouldn't be 0, or it may be counted as part of next 4-byte start - // sequence. - buffer[size - 1] |= 0x10; + Buffer buffer = Buffer::CreateWithCapacity(size); + buffer.AppendData(size, [](std::span buffer_view) { + // Set some valid header. + buffer_view[0] = kSlice; + for (size_t i = 1; i < buffer_view.size(); ++i) { + buffer_view[i] = static_cast(i); + } + // Last byte shouldn't be 0, or it may be counted as part of next 4-byte + // start sequence. + buffer_view[buffer_view.size() - 1] |= 0x10; + return buffer_view.size(); + }); return buffer; } // Create frame consisting of nalus of given size. Buffer CreateFrame(std::initializer_list nalu_sizes) { static constexpr int kStartCodeSize = 3; - Buffer frame = Buffer::CreateUninitializedWithSize( - absl::c_accumulate(nalu_sizes, 0) + kStartCodeSize * nalu_sizes.size()); - size_t offset = 0; - for (size_t nalu_size : nalu_sizes) { - EXPECT_GE(nalu_size, 1u); - // Insert nalu start code - frame[offset] = 0; - frame[offset + 1] = 0; - frame[offset + 2] = 1; - // Set some valid header. - frame[offset + 3] = 1; - // Fill payload avoiding accidental start codes - if (nalu_size > 1) { - memset(frame.data() + offset + 4, 0x3f, nalu_size - 1); + size_t size = + absl::c_accumulate(nalu_sizes, 0) + kStartCodeSize * nalu_sizes.size(); + Buffer frame = Buffer::CreateWithCapacity(size); + frame.AppendData(size, [&](std::span frame_view) { + size_t offset = 0; + for (size_t nalu_size : nalu_sizes) { + EXPECT_GE(nalu_size, 1u); + // Insert nalu start code + frame_view[offset] = 0; + frame_view[offset + 1] = 0; + frame_view[offset + 2] = 1; + // Set some valid header. + frame_view[offset + 3] = 1; + // Fill payload avoiding accidental start codes + if (nalu_size > 1) { + memset(frame_view.data() + offset + 4, 0x3f, nalu_size - 1); + } + offset += (kStartCodeSize + nalu_size); } - offset += (kStartCodeSize + nalu_size); - } - EXPECT_EQ(offset, frame.size()); // verify size calculation up front + EXPECT_EQ(offset, frame_view.size()); // verify size calculation up front + return offset; + }); return frame; } // Create frame consisting of given nalus. -Buffer CreateFrame(ArrayView nalus) { +Buffer CreateFrame(std::span nalus) { static constexpr int kStartCodeSize = 3; int frame_size = 0; for (const Buffer& nalu : nalus) { frame_size += (kStartCodeSize + nalu.size()); } - Buffer frame = Buffer::CreateUninitializedWithSize(frame_size); - size_t offset = 0; - for (const Buffer& nalu : nalus) { - // Insert nalu start code - frame[offset] = 0; - frame[offset + 1] = 0; - frame[offset + 2] = 1; - // Copy the nalu unit. - memcpy(frame.data() + offset + 3, nalu.data(), nalu.size()); - offset += (kStartCodeSize + nalu.size()); - } + Buffer frame = Buffer::CreateWithCapacity(frame_size); + frame.AppendData(frame_size, [&](std::span frame_view) { + size_t offset = 0; + for (const Buffer& nalu : nalus) { + // Insert nalu start code + frame_view[offset] = 0; + frame_view[offset + 1] = 0; + frame_view[offset + 2] = 1; + // Copy the nalu unit. + memcpy(frame_view.data() + offset + 3, nalu.data(), nalu.size()); + offset += (kStartCodeSize + nalu.size()); + } + return offset; + }); return frame; } @@ -230,23 +240,23 @@ TEST(RtpPacketizerH264Test, StapA) { kNalHeaderSize + 3 * kLengthFieldLength + 2 + 2 + 0x123); EXPECT_EQ(payload[0], kStapA); - payload = payload.subview(kNalHeaderSize); + payload = payload.subspan(kNalHeaderSize); // 1st fragment. - EXPECT_THAT(payload.subview(0, kLengthFieldLength), + EXPECT_THAT(payload.subspan(0, kLengthFieldLength), ElementsAre(0, 2)); // Size. - EXPECT_THAT(payload.subview(kLengthFieldLength, 2), + EXPECT_THAT(payload.subspan(kLengthFieldLength, 2), ElementsAreArray(nalus[0])); - payload = payload.subview(kLengthFieldLength + 2); + payload = payload.subspan(kLengthFieldLength + 2); // 2nd fragment. - EXPECT_THAT(payload.subview(0, kLengthFieldLength), + EXPECT_THAT(payload.subspan(0, kLengthFieldLength), ElementsAre(0, 2)); // Size. - EXPECT_THAT(payload.subview(kLengthFieldLength, 2), + EXPECT_THAT(payload.subspan(kLengthFieldLength, 2), ElementsAreArray(nalus[1])); - payload = payload.subview(kLengthFieldLength + 2); + payload = payload.subspan(kLengthFieldLength + 2); // 3rd fragment. - EXPECT_THAT(payload.subview(0, kLengthFieldLength), + EXPECT_THAT(payload.subspan(0, kLengthFieldLength), ElementsAre(0x1, 0x23)); // Size. - EXPECT_THAT(payload.subview(kLengthFieldLength), ElementsAreArray(nalus[2])); + EXPECT_THAT(payload.subspan(kLengthFieldLength), ElementsAreArray(nalus[2])); } TEST(RtpPacketizerH264Test, SingleNalUnitModeHasNoStapA) { @@ -389,30 +399,30 @@ TEST(RtpPacketizerH264Test, MixedStapAFUA) { ASSERT_THAT(packets, SizeIs(3)); // First expect two FU-A packets. - EXPECT_THAT(packets[0].payload().subview(0, kFuAHeaderSize), + EXPECT_THAT(packets[0].payload().subspan(0, kFuAHeaderSize), ElementsAre(kFuA, kH264SBit | nalus[0][0])); EXPECT_THAT( - packets[0].payload().subview(kFuAHeaderSize), + packets[0].payload().subspan(kFuAHeaderSize), ElementsAreArray(nalus[0].data() + kNalHeaderSize, kFuaPayloadSize)); - EXPECT_THAT(packets[1].payload().subview(0, kFuAHeaderSize), + EXPECT_THAT(packets[1].payload().subspan(0, kFuAHeaderSize), ElementsAre(kFuA, kH264EBit | nalus[0][0])); EXPECT_THAT( - packets[1].payload().subview(kFuAHeaderSize), + packets[1].payload().subspan(kFuAHeaderSize), ElementsAreArray(nalus[0].data() + kNalHeaderSize + kFuaPayloadSize, kFuaPayloadSize)); // Then expect one STAP-A packet with two nal units. EXPECT_THAT(packets[2].payload()[0], kStapA); - auto payload = packets[2].payload().subview(kNalHeaderSize); - EXPECT_THAT(payload.subview(0, kLengthFieldLength), + auto payload = packets[2].payload().subspan(kNalHeaderSize); + EXPECT_THAT(payload.subspan(0, kLengthFieldLength), ElementsAre(0, kStapANaluSize)); - EXPECT_THAT(payload.subview(kLengthFieldLength, kStapANaluSize), + EXPECT_THAT(payload.subspan(kLengthFieldLength, kStapANaluSize), ElementsAreArray(nalus[1])); - payload = payload.subview(kLengthFieldLength + kStapANaluSize); - EXPECT_THAT(payload.subview(0, kLengthFieldLength), + payload = payload.subspan(kLengthFieldLength + kStapANaluSize); + EXPECT_THAT(payload.subspan(0, kLengthFieldLength), ElementsAre(0, kStapANaluSize)); - EXPECT_THAT(payload.subview(kLengthFieldLength), ElementsAreArray(nalus[2])); + EXPECT_THAT(payload.subspan(kLengthFieldLength), ElementsAreArray(nalus[2])); } TEST(RtpPacketizerH264Test, LastFragmentFitsInSingleButNotLastPacket) { diff --git a/modules/rtp_rtcp/source/rtp_format_video_generic.cc b/modules/rtp_rtcp/source/rtp_format_video_generic.cc index 53c549efd1b..c44d7ca636c 100644 --- a/modules/rtp_rtcp/source/rtp_format_video_generic.cc +++ b/modules/rtp_rtcp/source/rtp_format_video_generic.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" @@ -29,7 +29,7 @@ constexpr size_t kExtendedHeaderLength = 2; } // namespace RtpPacketizerGeneric::RtpPacketizerGeneric( - ArrayView payload, + std::span payload, PayloadSizeLimits limits, const RTPVideoHeader& rtp_video_header) : remaining_payload_(payload) { @@ -40,7 +40,7 @@ RtpPacketizerGeneric::RtpPacketizerGeneric( current_packet_ = payload_sizes_.begin(); } -RtpPacketizerGeneric::RtpPacketizerGeneric(ArrayView payload, +RtpPacketizerGeneric::RtpPacketizerGeneric(std::span payload, PayloadSizeLimits limits) : header_size_(0), remaining_payload_(payload) { payload_sizes_ = SplitAboutEqually(payload.size(), limits); @@ -73,7 +73,7 @@ bool RtpPacketizerGeneric::NextPacket(RtpPacketToSend* packet) { memcpy(out_ptr + header_size_, remaining_payload_.data(), next_packet_payload_len); - remaining_payload_ = remaining_payload_.subview(next_packet_payload_len); + remaining_payload_ = remaining_payload_.subspan(next_packet_payload_len); ++current_packet_; diff --git a/modules/rtp_rtcp/source/rtp_format_video_generic.h b/modules/rtp_rtcp/source/rtp_format_video_generic.h index 3e0c1e3f053..cc9a6fb058b 100644 --- a/modules/rtp_rtcp/source/rtp_format_video_generic.h +++ b/modules/rtp_rtcp/source/rtp_format_video_generic.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format.h" namespace webrtc { @@ -36,14 +36,14 @@ class RtpPacketizerGeneric : public RtpPacketizer { // Initialize with payload from encoder. // The payload_data must be exactly one encoded generic frame. // Packets returned by `NextPacket` will contain the generic payload header. - RtpPacketizerGeneric(ArrayView payload, + RtpPacketizerGeneric(std::span payload, PayloadSizeLimits limits, const RTPVideoHeader& rtp_video_header); // Initialize with payload from encoder. // The payload_data must be exactly one encoded generic frame. // Packets returned by `NextPacket` will contain raw payload without the // generic payload header. - RtpPacketizerGeneric(ArrayView payload, + RtpPacketizerGeneric(std::span payload, PayloadSizeLimits limits); ~RtpPacketizerGeneric() override; @@ -64,7 +64,7 @@ class RtpPacketizerGeneric : public RtpPacketizer { uint8_t header_[3]; size_t header_size_; - ArrayView remaining_payload_; + std::span remaining_payload_; std::vector payload_sizes_; std::vector::const_iterator current_packet_; }; diff --git a/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc b/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc index e927038f040..2a200dd434b 100644 --- a/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_format_video_generic_unittest.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" @@ -82,7 +82,7 @@ TEST(RtpPacketizerVideoGeneric, WritesExtendedHeaderWhenPictureIdIsSet) { RtpPacketToSend packet(nullptr); ASSERT_TRUE(packetizer.NextPacket(&packet)); - ArrayView payload = packet.payload(); + std::span payload = packet.payload(); EXPECT_EQ(payload.size(), 3 + kPayloadSize); EXPECT_TRUE(payload[0] & 0x04); // Extended header bit is set. // Frame id is 37. @@ -136,7 +136,7 @@ TEST(RtpPacketizerVideoGeneric, FrameIdOver15bitsWrapsAround) { RtpPacketToSend packet(nullptr); ASSERT_TRUE(packetizer.NextPacket(&packet)); - ArrayView payload = packet.payload(); + std::span payload = packet.payload(); EXPECT_TRUE(payload[0] & 0x04); // Extended header bit is set. // Frame id is 0x137. EXPECT_EQ(0x01u, payload[1]); @@ -152,7 +152,7 @@ TEST(RtpPacketizerVideoGeneric, NoFrameIdDoesNotWriteExtendedHeader) { RtpPacketToSend packet(nullptr); ASSERT_TRUE(packetizer.NextPacket(&packet)); - ArrayView payload = packet.payload(); + std::span payload = packet.payload(); EXPECT_FALSE(payload[0] & 0x04); } @@ -164,7 +164,7 @@ TEST(RtpPacketizerVideoGeneric, DoesNotWriteHeaderForRawPayload) { RtpPacketToSend packet(nullptr); ASSERT_TRUE(packetizer.NextPacket(&packet)); - ArrayView payload = packet.payload(); + std::span payload = packet.payload(); EXPECT_THAT(payload, ElementsAreArray(kPayload)); } diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.cc b/modules/rtp_rtcp/source/rtp_format_vp8.cc index 717d7463c41..7727438b0dd 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8.cc +++ b/modules/rtp_rtcp/source/rtp_format_vp8.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/interface/common_constants.h" #include "modules/video_coding/codecs/vp8/include/vp8_globals.h" @@ -57,7 +57,7 @@ bool ValidateHeader(const RTPVideoHeaderVP8& hdr_info) { } // namespace -RtpPacketizerVp8::RtpPacketizerVp8(ArrayView payload, +RtpPacketizerVp8::RtpPacketizerVp8(std::span payload, PayloadSizeLimits limits, const RTPVideoHeaderVP8& hdr_info) : hdr_(BuildHeader(hdr_info)), remaining_payload_(payload) { @@ -89,7 +89,7 @@ bool RtpPacketizerVp8::NextPacket(RtpPacketToSend* packet) { memcpy(buffer, hdr_.data(), hdr_.size()); memcpy(buffer + hdr_.size(), remaining_payload_.data(), packet_payload_len); - remaining_payload_ = remaining_payload_.subview(packet_payload_len); + remaining_payload_ = remaining_payload_.subspan(packet_payload_len); hdr_[0] &= (~kSBit); // Clear 'Start of partition' bit. packet->SetMarker(current_packet_ == payload_sizes_.end()); return true; diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.h b/modules/rtp_rtcp/source/rtp_format_vp8.h index 36f4b8b3b39..034ad449a50 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8.h +++ b/modules/rtp_rtcp/source/rtp_format_vp8.h @@ -28,10 +28,10 @@ #include #include +#include #include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/vp8/include/vp8_globals.h" @@ -43,7 +43,7 @@ class RtpPacketizerVp8 : public RtpPacketizer { public: // Initialize with payload from encoder. // The payload_data must be exactly one encoded VP8 frame. - RtpPacketizerVp8(ArrayView payload, + RtpPacketizerVp8(std::span payload, PayloadSizeLimits limits, const RTPVideoHeaderVP8& hdr_info); @@ -65,7 +65,7 @@ class RtpPacketizerVp8 : public RtpPacketizer { static RawHeader BuildHeader(const RTPVideoHeaderVP8& header); RawHeader hdr_; - ArrayView remaining_payload_; + std::span remaining_payload_; std::vector payload_sizes_; std::vector::const_iterator current_packet_; }; diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc index 13341c316a2..f55bbfb2dd0 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc +++ b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.cc @@ -12,8 +12,9 @@ #include #include +#include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format_vp8.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/interface/common_constants.h" @@ -55,20 +56,22 @@ int Bit(uint8_t byte, int position) { RtpFormatVp8TestHelper::RtpFormatVp8TestHelper(const RTPVideoHeaderVP8* hdr, size_t payload_len) - : hdr_info_(hdr), - payload_(Buffer::CreateUninitializedWithSize(payload_len)) { - for (size_t i = 0; i < payload_.size(); ++i) { - payload_[i] = i; - } + : hdr_info_(hdr), payload_(Buffer::CreateWithCapacity(payload_len)) { + payload_.AppendData(payload_len, [](std::span payload_view) { + for (size_t i = 0; i < payload_view.size(); ++i) { + payload_view[i] = i; + } + return payload_view.size(); + }); } RtpFormatVp8TestHelper::~RtpFormatVp8TestHelper() = default; void RtpFormatVp8TestHelper::GetAllPacketsAndCheck( RtpPacketizerVp8* packetizer, - ArrayView expected_sizes) { + std::span expected_sizes) { EXPECT_EQ(packetizer->NumPackets(), expected_sizes.size()); - const uint8_t* data_ptr = payload_.begin(); + Buffer::const_iterator data_it = payload_.begin(); RtpPacketToSend packet(kNoExtensions); for (size_t i = 0; i < expected_sizes.size(); ++i) { EXPECT_TRUE(packetizer->NextPacket(&packet)); @@ -77,16 +80,18 @@ void RtpFormatVp8TestHelper::GetAllPacketsAndCheck( int payload_offset = CheckHeader(rtp_payload, /*first=*/i == 0); // Verify that the payload (i.e., after the headers) of the packet is - // identical to the expected (as found in data_ptr). - auto vp8_payload = rtp_payload.subview(payload_offset); - ASSERT_GE(payload_.end() - data_ptr, static_cast(vp8_payload.size())); - EXPECT_THAT(vp8_payload, ElementsAreArray(data_ptr, vp8_payload.size())); - data_ptr += vp8_payload.size(); + // identical to the expected (as found in data_it). + auto vp8_payload = rtp_payload.subspan(payload_offset); + ASSERT_GE(std::distance(data_it, payload_.cend()), + static_cast(vp8_payload.size())); + EXPECT_THAT(vp8_payload, + ElementsAreArray(data_it, data_it + vp8_payload.size())); + data_it += vp8_payload.size(); } - EXPECT_EQ(payload_.end() - data_ptr, 0); + EXPECT_EQ(payload_.end(), data_it); } -int RtpFormatVp8TestHelper::CheckHeader(ArrayView buffer, +int RtpFormatVp8TestHelper::CheckHeader(std::span buffer, bool first) { int x_bit = Bit(buffer[0], 7); EXPECT_EQ(Bit(buffer[0], 6), 0); // Reserved. @@ -113,7 +118,7 @@ int RtpFormatVp8TestHelper::CheckHeader(ArrayView buffer, // Verify that the I bit and the PictureID field are both set in accordance // with the information in hdr_info_->pictureId. -void RtpFormatVp8TestHelper::CheckPictureID(ArrayView buffer, +void RtpFormatVp8TestHelper::CheckPictureID(std::span buffer, int* offset) { int i_bit = Bit(buffer[1], 7); if (hdr_info_->pictureId != kNoPictureId) { @@ -130,7 +135,7 @@ void RtpFormatVp8TestHelper::CheckPictureID(ArrayView buffer, // Verify that the L bit and the TL0PICIDX field are both set in accordance // with the information in hdr_info_->tl0PicIdx. -void RtpFormatVp8TestHelper::CheckTl0PicIdx(ArrayView buffer, +void RtpFormatVp8TestHelper::CheckTl0PicIdx(std::span buffer, int* offset) { int l_bit = Bit(buffer[1], 6); if (hdr_info_->tl0PicIdx != kNoTl0PicIdx) { @@ -145,7 +150,7 @@ void RtpFormatVp8TestHelper::CheckTl0PicIdx(ArrayView buffer, // Verify that the T bit and the TL0PICIDX field, and the K bit and KEYIDX // field are all set in accordance with the information in // hdr_info_->temporalIdx and hdr_info_->keyIdx, respectively. -void RtpFormatVp8TestHelper::CheckTIDAndKeyIdx(ArrayView buffer, +void RtpFormatVp8TestHelper::CheckTIDAndKeyIdx(std::span buffer, int* offset) { int t_bit = Bit(buffer[1], 5); int k_bit = Bit(buffer[1], 4); diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h index a364df0294c..3e744d9b205 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h +++ b/modules/rtp_rtcp/source/rtp_format_vp8_test_helper.h @@ -19,8 +19,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format_vp8.h" #include "modules/video_coding/codecs/vp8/include/vp8_globals.h" #include "rtc_base/buffer.h" @@ -36,17 +36,17 @@ class RtpFormatVp8TestHelper { RtpFormatVp8TestHelper& operator=(const RtpFormatVp8TestHelper&) = delete; void GetAllPacketsAndCheck(RtpPacketizerVp8* packetizer, - ArrayView expected_sizes); + std::span expected_sizes); - ArrayView payload() const { return payload_; } + std::span payload() const { return payload_; } size_t payload_size() const { return payload_.size(); } private: // Returns header size, i.e. payload offset. - int CheckHeader(ArrayView rtp_payload, bool first); - void CheckPictureID(ArrayView rtp_payload, int* offset); - void CheckTl0PicIdx(ArrayView rtp_payload, int* offset); - void CheckTIDAndKeyIdx(ArrayView rtp_payload, int* offset); + int CheckHeader(std::span rtp_payload, bool first); + void CheckPictureID(std::span rtp_payload, int* offset); + void CheckTl0PicIdx(std::span rtp_payload, int* offset); + void CheckTIDAndKeyIdx(std::span rtp_payload, int* offset); void CheckPayload(const uint8_t* data_ptr); const RTPVideoHeaderVP8* const hdr_info_; diff --git a/modules/rtp_rtcp/source/rtp_format_vp9.cc b/modules/rtp_rtcp/source/rtp_format_vp9.cc index 661065cde1f..77935f9f05e 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp9.cc +++ b/modules/rtp_rtcp/source/rtp_format_vp9.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/interface/common_constants.h" #include "modules/video_coding/codecs/vp9/include/vp9_globals.h" @@ -307,7 +307,7 @@ RTPVideoHeaderVP9 RemoveInactiveSpatialLayers( } } // namespace -RtpPacketizerVp9::RtpPacketizerVp9(ArrayView payload, +RtpPacketizerVp9::RtpPacketizerVp9(std::span payload, PayloadSizeLimits limits, const RTPVideoHeaderVP9& hdr) : hdr_(RemoveInactiveSpatialLayers(hdr)), @@ -349,11 +349,11 @@ bool RtpPacketizerVp9::NextPacket(RtpPacketToSend* packet) { uint8_t* buffer = packet->AllocatePayload(header_size + packet_payload_len); RTC_CHECK(buffer); - if (!WriteHeader(layer_begin, layer_end, MakeArrayView(buffer, header_size))) + if (!WriteHeader(layer_begin, layer_end, std::span(buffer, header_size))) return false; memcpy(buffer + header_size, remaining_payload_.data(), packet_payload_len); - remaining_payload_ = remaining_payload_.subview(packet_payload_len); + remaining_payload_ = remaining_payload_.subspan(packet_payload_len); // Ensure end_of_picture is always set on top spatial layer when it is not // dropped. @@ -401,7 +401,7 @@ bool RtpPacketizerVp9::NextPacket(RtpPacketToSend* packet) { // +-+-+-+-+-+-+-+-+ bool RtpPacketizerVp9::WriteHeader(bool layer_begin, bool layer_end, - ArrayView buffer) const { + std::span buffer) const { // Required payload descriptor byte. bool i_bit = PictureIdPresent(hdr_); bool p_bit = hdr_.inter_pic_predicted; diff --git a/modules/rtp_rtcp/source/rtp_format_vp9.h b/modules/rtp_rtcp/source/rtp_format_vp9.h index 80c43f35b04..59af6383c40 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp9.h +++ b/modules/rtp_rtcp/source/rtp_format_vp9.h @@ -24,9 +24,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/vp9/include/vp9_globals.h" @@ -36,7 +36,7 @@ namespace webrtc { class RtpPacketizerVp9 : public RtpPacketizer { public: // The `payload` must be one encoded VP9 layer frame. - RtpPacketizerVp9(ArrayView payload, + RtpPacketizerVp9(std::span payload, PayloadSizeLimits limits, const RTPVideoHeaderVP9& hdr); @@ -58,12 +58,12 @@ class RtpPacketizerVp9 : public RtpPacketizer { // the layer frame. Returns false on failure. bool WriteHeader(bool layer_begin, bool layer_end, - ArrayView rtp_payload) const; + std::span rtp_payload) const; const RTPVideoHeaderVP9 hdr_; const int header_size_; const int first_packet_extra_header_size_; - ArrayView remaining_payload_; + std::span remaining_payload_; std::vector payload_sizes_; std::vector::const_iterator current_packet_; }; diff --git a/modules/rtp_rtcp/source/rtp_format_vp9_unittest.cc b/modules/rtp_rtcp/source/rtp_format_vp9_unittest.cc index 58789da14f1..9c59ef788f1 100644 --- a/modules/rtp_rtcp/source/rtp_format_vp9_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_format_vp9_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" @@ -78,7 +78,7 @@ void ParseAndCheckPacket(const uint8_t* packet, size_t expected_length) { RTPVideoHeader video_header; EXPECT_EQ(VideoRtpDepacketizerVp9::ParseRtpPayload( - MakeArrayView(packet, expected_length), &video_header), + std::span(packet, expected_length), &video_header), expected_hdr_length); EXPECT_EQ(kVideoCodecVP9, video_header.codec); auto& vp9_header = @@ -154,8 +154,8 @@ class RtpPacketizerVp9Test : public ::testing::Test { EXPECT_EQ(last, payload_pos_ == payload_.size()); } - void CreateParseAndCheckPackets(ArrayView expected_hdr_sizes, - ArrayView expected_sizes) { + void CreateParseAndCheckPackets(std::span expected_hdr_sizes, + std::span expected_sizes) { ASSERT_EQ(expected_hdr_sizes.size(), expected_sizes.size()); ASSERT_TRUE(packetizer_ != nullptr); EXPECT_EQ(expected_sizes.size(), num_packets_); diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc index 9bb88987b77..44124f3b469 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc @@ -11,8 +11,8 @@ #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -75,10 +75,10 @@ void RtpGenericFrameDescriptor::SetFrameId(uint16_t frame_id) { frame_id_ = frame_id; } -ArrayView RtpGenericFrameDescriptor::FrameDependenciesDiffs() +std::span RtpGenericFrameDescriptor::FrameDependenciesDiffs() const { RTC_DCHECK(FirstPacketInSubFrame()); - return MakeArrayView(frame_deps_id_diffs_, num_frame_deps_); + return std::span(frame_deps_id_diffs_, num_frame_deps_); } bool RtpGenericFrameDescriptor::AddFrameDependencyDiff(uint16_t fdiff) { diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h index 7ecdb2a69e2..4075bfef0e2 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h @@ -13,8 +13,7 @@ #include #include - -#include "api/array_view.h" +#include namespace webrtc { @@ -54,7 +53,7 @@ class RtpGenericFrameDescriptor { uint16_t FrameId() const; void SetFrameId(uint16_t frame_id); - ArrayView FrameDependenciesDiffs() const; + std::span FrameDependenciesDiffs() const; void ClearFrameDependencies() { num_frame_deps_ = 0; } // Returns false on failure, i.e. number of dependencies is too large. bool AddFrameDependencyDiff(uint16_t fdiff); diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc index e8ca3dd3b63..511b93e591b 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" #include "rtc_base/checks.h" @@ -63,7 +63,7 @@ constexpr uint8_t kFlageXtendedOffset = 0x02; // +-+-+-+-+-+-+-+-+ bool RtpGenericFrameDescriptorExtension00::Parse( - ArrayView data, + std::span data, RtpGenericFrameDescriptor* descriptor) { if (data.empty()) { return false; @@ -131,7 +131,7 @@ size_t RtpGenericFrameDescriptorExtension00::ValueSize( } bool RtpGenericFrameDescriptorExtension00::Write( - ArrayView data, + std::span data, const RtpGenericFrameDescriptor& descriptor) { RTC_CHECK_EQ(data.size(), ValueSize(descriptor)); uint8_t base_header = @@ -152,7 +152,7 @@ bool RtpGenericFrameDescriptorExtension00::Write( uint16_t frame_id = descriptor.FrameId(); data[2] = frame_id & 0xff; data[3] = frame_id >> 8; - ArrayView fdiffs = descriptor.FrameDependenciesDiffs(); + std::span fdiffs = descriptor.FrameDependenciesDiffs(); size_t offset = 4; if (descriptor.FirstPacketInSubFrame() && fdiffs.empty() && descriptor.Width() > 0 && descriptor.Height() > 0) { diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h index 8c6555564cc..c213f52310a 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h @@ -13,8 +13,9 @@ #include #include +#include + #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" @@ -33,10 +34,10 @@ class RtpGenericFrameDescriptorExtension00 { } static constexpr int kMaxSizeBytes = 16; - static bool Parse(ArrayView data, + static bool Parse(std::span data, RtpGenericFrameDescriptor* descriptor); static size_t ValueSize(const RtpGenericFrameDescriptor& descriptor); - static bool Write(ArrayView data, + static bool Write(std::span data, const RtpGenericFrameDescriptor& descriptor); }; diff --git a/modules/rtp_rtcp/source/rtp_header_extension_map.cc b/modules/rtp_rtcp/source/rtp_header_extension_map.cc index f170ab9a7a7..1a468a405ae 100644 --- a/modules/rtp_rtcp/source/rtp_header_extension_map.cc +++ b/modules/rtp_rtcp/source/rtp_header_extension_map.cc @@ -11,9 +11,10 @@ #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include +#include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/corruption_detection_extension.h" @@ -78,13 +79,13 @@ RtpHeaderExtensionMap::RtpHeaderExtensionMap(bool extmap_allow_mixed) } RtpHeaderExtensionMap::RtpHeaderExtensionMap( - ArrayView extensions) + std::span extensions) : RtpHeaderExtensionMap(false) { for (const RtpExtension& extension : extensions) RegisterByUri(extension.id, extension.uri); } -void RtpHeaderExtensionMap::Reset(ArrayView extensions) { +void RtpHeaderExtensionMap::Reset(std::span extensions) { for (auto& id : ids_) id = kInvalidId; for (const RtpExtension& extension : extensions) diff --git a/modules/rtp_rtcp/source/rtp_header_extension_size.cc b/modules/rtp_rtcp/source/rtp_header_extension_size.cc index b9016de16ee..dd7ff9be2aa 100644 --- a/modules/rtp_rtcp/source/rtp_header_extension_size.cc +++ b/modules/rtp_rtcp/source/rtp_header_extension_size.cc @@ -10,13 +10,14 @@ #include "modules/rtp_rtcp/source/rtp_header_extension_size.h" -#include "api/array_view.h" +#include + #include "api/rtp_parameters.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" namespace webrtc { -int RtpHeaderExtensionSize(ArrayView extensions, +int RtpHeaderExtensionSize(std::span extensions, const RtpHeaderExtensionMap& registered_extensions) { // RFC3550 Section 5.3.1 static constexpr int kExtensionBlockHeaderSize = 4; diff --git a/modules/rtp_rtcp/source/rtp_header_extension_size.h b/modules/rtp_rtcp/source/rtp_header_extension_size.h index b7fd960da11..48a180812ed 100644 --- a/modules/rtp_rtcp/source/rtp_header_extension_size.h +++ b/modules/rtp_rtcp/source/rtp_header_extension_size.h @@ -10,7 +10,8 @@ #ifndef MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSION_SIZE_H_ #define MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSION_SIZE_H_ -#include "api/array_view.h" +#include + #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -24,7 +25,7 @@ struct RtpExtensionSize { // Calculates rtp header extension size in bytes assuming packet contain // all `extensions` with provided `value_size`. // Counts only extensions present among `registered_extensions`. -int RtpHeaderExtensionSize(ArrayView extensions, +int RtpHeaderExtensionSize(std::span extensions, const RtpHeaderExtensionMap& registered_extensions); } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_header_extensions.cc b/modules/rtp_rtcp/source/rtp_header_extensions.cc index 83afe0fe6bb..15884c814a0 100644 --- a/modules/rtp_rtcp/source/rtp_header_extensions.cc +++ b/modules/rtp_rtcp/source/rtp_header_extensions.cc @@ -18,11 +18,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/units/time_delta.h" #include "api/video/color_space.h" @@ -50,7 +50,7 @@ namespace webrtc { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | len=2 | absolute send time | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool AbsoluteSendTime::Parse(ArrayView data, +bool AbsoluteSendTime::Parse(std::span data, uint32_t* time_24bits) { if (data.size() != 3) return false; @@ -58,7 +58,7 @@ bool AbsoluteSendTime::Parse(ArrayView data, return true; } -bool AbsoluteSendTime::Write(ArrayView data, uint32_t time_24bits) { +bool AbsoluteSendTime::Write(std::span data, uint32_t time_24bits) { RTC_DCHECK_EQ(data.size(), 3); RTC_DCHECK_LE(time_24bits, 0x00FFFFFF); ByteWriter::WriteBigEndian(data.data(), time_24bits); @@ -100,7 +100,7 @@ bool AbsoluteSendTime::Write(ArrayView data, uint32_t time_24bits) { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ... (56-63) | // +-+-+-+-+-+-+-+-+ -bool AbsoluteCaptureTimeExtension::Parse(ArrayView data, +bool AbsoluteCaptureTimeExtension::Parse(std::span data, AbsoluteCaptureTime* extension) { if (data.size() != kValueSizeBytes && data.size() != kValueSizeBytesWithoutEstimatedCaptureClockOffset) { @@ -127,7 +127,7 @@ size_t AbsoluteCaptureTimeExtension::ValueSize( } } -bool AbsoluteCaptureTimeExtension::Write(ArrayView data, +bool AbsoluteCaptureTimeExtension::Write(std::span data, const AbsoluteCaptureTime& extension) { RTC_DCHECK_EQ(data.size(), ValueSize(extension)); @@ -161,7 +161,7 @@ bool AbsoluteCaptureTimeExtension::Write(ArrayView data, // | ID | len=1 |V| level | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Sample Audio Level Encoding Using the Two-Byte Header Format -bool AudioLevelExtension::Parse(ArrayView data, +bool AudioLevelExtension::Parse(std::span data, AudioLevel* extension) { // One-byte and two-byte format share the same data definition. if (data.size() != 1) @@ -172,7 +172,7 @@ bool AudioLevelExtension::Parse(ArrayView data, return true; } -bool AudioLevelExtension::Write(ArrayView data, +bool AudioLevelExtension::Write(std::span data, const AudioLevel& extension) { // One-byte and two-byte format share the same data definition. RTC_DCHECK_EQ(data.size(), 1); @@ -203,7 +203,7 @@ bool AudioLevelExtension::Write(ArrayView data, // |0| level 3 | 0 (pad) | ... | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Sample Audio Level Encoding Using the Two-Byte Header Format -bool CsrcAudioLevel::Parse(ArrayView data, +bool CsrcAudioLevel::Parse(std::span data, std::vector* csrc_audio_levels) { if (data.size() > kRtpCsrcSize) { return false; @@ -215,12 +215,12 @@ bool CsrcAudioLevel::Parse(ArrayView data, return true; } -size_t CsrcAudioLevel::ValueSize(ArrayView csrc_audio_levels) { +size_t CsrcAudioLevel::ValueSize(std::span csrc_audio_levels) { return csrc_audio_levels.size(); } -bool CsrcAudioLevel::Write(ArrayView data, - ArrayView csrc_audio_levels) { +bool CsrcAudioLevel::Write(std::span data, + std::span csrc_audio_levels) { RTC_CHECK_LE(csrc_audio_levels.size(), kRtpCsrcSize); if (csrc_audio_levels.size() != data.size()) { return false; @@ -247,7 +247,7 @@ bool CsrcAudioLevel::Write(ArrayView data, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | len=2 | transmission offset | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool TransmissionOffset::Parse(ArrayView data, +bool TransmissionOffset::Parse(std::span data, int32_t* rtp_time) { if (data.size() != 3) return false; @@ -255,7 +255,7 @@ bool TransmissionOffset::Parse(ArrayView data, return true; } -bool TransmissionOffset::Write(ArrayView data, int32_t rtp_time) { +bool TransmissionOffset::Write(std::span data, int32_t rtp_time) { RTC_DCHECK_EQ(data.size(), 3); RTC_DCHECK_LE(rtp_time, 0x00ffffff); ByteWriter::WriteBigEndian(data.data(), rtp_time); @@ -269,7 +269,7 @@ bool TransmissionOffset::Write(ArrayView data, int32_t rtp_time) { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | L=1 |transport-wide sequence number | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool TransportSequenceNumber::Parse(ArrayView data, +bool TransportSequenceNumber::Parse(std::span data, uint16_t* transport_sequence_number) { if (data.size() != kValueSizeBytes) return false; @@ -277,7 +277,7 @@ bool TransportSequenceNumber::Parse(ArrayView data, return true; } -bool TransportSequenceNumber::Write(ArrayView data, +bool TransportSequenceNumber::Write(std::span data, uint16_t transport_sequence_number) { RTC_DCHECK_EQ(data.size(), ValueSize(transport_sequence_number)); ByteWriter::WriteBigEndian(data.data(), transport_sequence_number); @@ -302,7 +302,7 @@ bool TransportSequenceNumber::Write(ArrayView data, // cover including the current packet. If `seq_count` is zero no feedback is // requested. bool TransportSequenceNumberV2::Parse( - ArrayView data, + std::span data, uint16_t* transport_sequence_number, std::optional* feedback_request) { if (data.size() != kValueSizeBytes && @@ -329,7 +329,7 @@ bool TransportSequenceNumberV2::Parse( } bool TransportSequenceNumberV2::Write( - ArrayView data, + std::span data, uint16_t transport_sequence_number, const std::optional& feedback_request) { RTC_DCHECK_EQ(data.size(), @@ -359,7 +359,7 @@ bool TransportSequenceNumberV2::Write( // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | len=0 |0 0 0 0 C F R R| // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool VideoOrientation::Parse(ArrayView data, +bool VideoOrientation::Parse(std::span data, VideoRotation* rotation) { if (data.size() != 1) return false; @@ -367,20 +367,20 @@ bool VideoOrientation::Parse(ArrayView data, return true; } -bool VideoOrientation::Write(ArrayView data, VideoRotation rotation) { +bool VideoOrientation::Write(std::span data, VideoRotation rotation) { RTC_DCHECK_EQ(data.size(), 1); data[0] = ConvertVideoRotationToCVOByte(rotation); return true; } -bool VideoOrientation::Parse(ArrayView data, uint8_t* value) { +bool VideoOrientation::Parse(std::span data, uint8_t* value) { if (data.size() != 1) return false; *value = data[0]; return true; } -bool VideoOrientation::Write(ArrayView data, uint8_t value) { +bool VideoOrientation::Write(std::span data, uint8_t value) { RTC_DCHECK_EQ(data.size(), 1); data[0] = value; return true; @@ -391,7 +391,7 @@ bool VideoOrientation::Write(ArrayView data, uint8_t value) { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | len=2 | MIN delay | MAX delay | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool PlayoutDelayLimits::Parse(ArrayView data, +bool PlayoutDelayLimits::Parse(std::span data, VideoPlayoutDelay* playout_delay) { RTC_DCHECK(playout_delay); if (data.size() != 3) @@ -402,7 +402,7 @@ bool PlayoutDelayLimits::Parse(ArrayView data, return playout_delay->Set(min_raw * kGranularity, max_raw * kGranularity); } -bool PlayoutDelayLimits::Write(ArrayView data, +bool PlayoutDelayLimits::Write(std::span data, const VideoPlayoutDelay& playout_delay) { RTC_DCHECK_EQ(data.size(), 3); @@ -431,7 +431,7 @@ bool PlayoutDelayLimits::Write(ArrayView data, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | len=0 | Content type | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool VideoContentTypeExtension::Parse(ArrayView data, +bool VideoContentTypeExtension::Parse(std::span data, VideoContentType* content_type) { if (data.size() == 1 && videocontenttypehelpers::IsValidContentType(data[0])) { @@ -445,7 +445,7 @@ bool VideoContentTypeExtension::Parse(ArrayView data, return false; } -bool VideoContentTypeExtension::Write(ArrayView data, +bool VideoContentTypeExtension::Write(std::span data, VideoContentType content_type) { RTC_DCHECK_EQ(data.size(), 1); data[0] = static_cast(content_type); @@ -473,7 +473,7 @@ bool VideoContentTypeExtension::Write(ArrayView data, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | network2 timestamp ms delta | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool VideoTimingExtension::Parse(ArrayView data, +bool VideoTimingExtension::Parse(std::span data, VideoSendTiming* timing) { RTC_DCHECK(timing); // TODO(sprang): Deprecate support for old wire format. @@ -505,7 +505,7 @@ bool VideoTimingExtension::Parse(ArrayView data, return true; } -bool VideoTimingExtension::Write(ArrayView data, +bool VideoTimingExtension::Write(std::span data, const VideoSendTiming& timing) { RTC_DCHECK_EQ(data.size(), 1 + 2 * 6); ByteWriter::WriteBigEndian(data.data() + kFlagsOffset, timing.flags); @@ -527,7 +527,7 @@ bool VideoTimingExtension::Write(ArrayView data, return true; } -bool VideoTimingExtension::Write(ArrayView data, +bool VideoTimingExtension::Write(std::span data, uint16_t time_delta_ms, uint8_t offset) { RTC_DCHECK_GE(data.size(), offset + 2); @@ -571,7 +571,7 @@ bool VideoTimingExtension::Write(ArrayView data, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |range+chr.sit. | // +-+-+-+-+-+-+-+-+ -bool ColorSpaceExtension::Parse(ArrayView data, +bool ColorSpaceExtension::Parse(std::span data, ColorSpace* color_space) { RTC_DCHECK(color_space); if (data.size() != kValueSizeBytes && @@ -602,7 +602,7 @@ bool ColorSpaceExtension::Parse(ArrayView data, color_space->set_hdr_metadata(nullptr); } else { HdrMetadata hdr_metadata; - offset += ParseHdrMetadata(data.subview(offset), &hdr_metadata); + offset += ParseHdrMetadata(data.subspan(offset), &hdr_metadata); if (!hdr_metadata.Validate()) return false; color_space->set_hdr_metadata(&hdr_metadata); @@ -611,7 +611,7 @@ bool ColorSpaceExtension::Parse(ArrayView data, return true; } -bool ColorSpaceExtension::Write(ArrayView data, +bool ColorSpaceExtension::Write(std::span data, const ColorSpace& color_space) { RTC_DCHECK_EQ(data.size(), ValueSize(color_space)); size_t offset = 0; @@ -626,7 +626,7 @@ bool ColorSpaceExtension::Write(ArrayView data, // Write HDR metadata if it exists. if (color_space.hdr_metadata()) { offset += - WriteHdrMetadata(data.subview(offset), *color_space.hdr_metadata()); + WriteHdrMetadata(data.subspan(offset), *color_space.hdr_metadata()); } RTC_DCHECK_EQ(ValueSize(color_space), offset); return true; @@ -649,7 +649,7 @@ uint8_t ColorSpaceExtension::CombineRangeAndChromaSiting( static_cast(chroma_siting_vertical); } -size_t ColorSpaceExtension::ParseHdrMetadata(ArrayView data, +size_t ColorSpaceExtension::ParseHdrMetadata(std::span data, HdrMetadata* hdr_metadata) { RTC_DCHECK_EQ(data.size(), kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata); @@ -696,7 +696,7 @@ size_t ColorSpaceExtension::ParseLuminance(const uint8_t* data, return 2; // Return number of bytes read. } -size_t ColorSpaceExtension::WriteHdrMetadata(ArrayView data, +size_t ColorSpaceExtension::WriteHdrMetadata(std::span data, const HdrMetadata& hdr_metadata) { RTC_DCHECK_EQ(data.size(), kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata); @@ -750,7 +750,7 @@ size_t ColorSpaceExtension::WriteLuminance(uint8_t* data, return 2; // Return number of bytes written. } -bool BaseRtpStringExtension::Parse(ArrayView data, +bool BaseRtpStringExtension::Parse(std::span data, std::string* str) { if (data.empty() || data[0] == 0) // Valid string extension can't be empty. return false; @@ -762,7 +762,7 @@ bool BaseRtpStringExtension::Parse(ArrayView data, return true; } -bool BaseRtpStringExtension::Write(ArrayView data, +bool BaseRtpStringExtension::Write(std::span data, absl::string_view str) { if (str.size() > kMaxValueSizeBytes) { return false; @@ -790,7 +790,7 @@ bool BaseRtpStringExtension::Write(ArrayView data, // | ID | len=1 |N| level | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Sample Audio Level Encoding Using the Two-Byte Header Format -bool InbandComfortNoiseExtension::Parse(ArrayView data, +bool InbandComfortNoiseExtension::Parse(std::span data, std::optional* level) { if (data.size() != kValueSizeBytes) return false; @@ -800,7 +800,7 @@ bool InbandComfortNoiseExtension::Parse(ArrayView data, return true; } -bool InbandComfortNoiseExtension::Write(ArrayView data, +bool InbandComfortNoiseExtension::Write(std::span data, std::optional level) { RTC_DCHECK_EQ(data.size(), kValueSizeBytes); data[0] = 0b0000'0000; @@ -820,7 +820,7 @@ bool InbandComfortNoiseExtension::Write(ArrayView data, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | L=1 | video-frame-tracking-id | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -bool VideoFrameTrackingIdExtension::Parse(ArrayView data, +bool VideoFrameTrackingIdExtension::Parse(std::span data, uint16_t* video_frame_tracking_id) { if (data.size() != kValueSizeBytes) { return false; @@ -829,7 +829,7 @@ bool VideoFrameTrackingIdExtension::Parse(ArrayView data, return true; } -bool VideoFrameTrackingIdExtension::Write(ArrayView data, +bool VideoFrameTrackingIdExtension::Write(std::span data, uint16_t video_frame_tracking_id) { RTC_DCHECK_EQ(data.size(), kValueSizeBytes); ByteWriter::WriteBigEndian(data.data(), video_frame_tracking_id); diff --git a/modules/rtp_rtcp/source/rtp_header_extensions.h b/modules/rtp_rtcp/source/rtp_header_extensions.h index 8fb26e55221..03f846a9052 100644 --- a/modules/rtp_rtcp/source/rtp_header_extensions.h +++ b/modules/rtp_rtcp/source/rtp_header_extensions.h @@ -15,11 +15,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/rtp_parameters.h" #include "api/units/time_delta.h" @@ -51,11 +51,11 @@ class AbsoluteSendTime { return RtpExtension::kAbsSendTimeUri; } - static bool Parse(ArrayView data, uint32_t* time_24bits); + static bool Parse(std::span data, uint32_t* time_24bits); static size_t ValueSize(uint32_t /* time_24bits */) { return kValueSizeBytes; } - static bool Write(ArrayView data, uint32_t time_24bits); + static bool Write(std::span data, uint32_t time_24bits); static constexpr uint32_t To24Bits(Timestamp time) { int64_t time_us = time.us() % (int64_t{1 << 6} * 1'000'000); @@ -87,10 +87,10 @@ class AbsoluteCaptureTimeExtension { return RtpExtension::kAbsoluteCaptureTimeUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, AbsoluteCaptureTime* extension); static size_t ValueSize(const AbsoluteCaptureTime& extension); - static bool Write(ArrayView data, + static bool Write(std::span data, const AbsoluteCaptureTime& extension); }; @@ -103,11 +103,11 @@ class AudioLevelExtension { return RtpExtension::kAudioLevelUri; } - static bool Parse(ArrayView data, AudioLevel* extension); + static bool Parse(std::span data, AudioLevel* extension); static size_t ValueSize(const AudioLevel& /* extension */) { return kValueSizeBytes; } - static bool Write(ArrayView data, const AudioLevel& extension); + static bool Write(std::span data, const AudioLevel& extension); }; class CsrcAudioLevel { @@ -118,11 +118,11 @@ class CsrcAudioLevel { return RtpExtension::kCsrcAudioLevelsUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, std::vector* csrc_audio_levels); - static size_t ValueSize(ArrayView csrc_audio_levels); - static bool Write(ArrayView data, - ArrayView csrc_audio_levels); + static size_t ValueSize(std::span csrc_audio_levels); + static bool Write(std::span data, + std::span csrc_audio_levels); }; class TransmissionOffset { @@ -134,9 +134,9 @@ class TransmissionOffset { return RtpExtension::kTimestampOffsetUri; } - static bool Parse(ArrayView data, int32_t* rtp_time); + static bool Parse(std::span data, int32_t* rtp_time); static size_t ValueSize(int32_t /* rtp_time */) { return kValueSizeBytes; } - static bool Write(ArrayView data, int32_t rtp_time); + static bool Write(std::span data, int32_t rtp_time); }; class TransportSequenceNumber { @@ -148,12 +148,12 @@ class TransportSequenceNumber { return RtpExtension::kTransportSequenceNumberUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, uint16_t* transport_sequence_number); static size_t ValueSize(uint16_t /*transport_sequence_number*/) { return kValueSizeBytes; } - static bool Write(ArrayView data, + static bool Write(std::span data, uint16_t transport_sequence_number); }; @@ -167,7 +167,7 @@ class TransportSequenceNumberV2 { return RtpExtension::kTransportSequenceNumberV2Uri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, uint16_t* transport_sequence_number, std::optional* feedback_request); static size_t ValueSize( @@ -176,7 +176,7 @@ class TransportSequenceNumberV2 { return feedback_request ? kValueSizeBytes : kValueSizeBytesWithoutFeedbackRequest; } - static bool Write(ArrayView data, + static bool Write(std::span data, uint16_t transport_sequence_number, const std::optional& feedback_request); @@ -193,12 +193,12 @@ class VideoOrientation { return RtpExtension::kVideoRotationUri; } - static bool Parse(ArrayView data, VideoRotation* value); + static bool Parse(std::span data, VideoRotation* value); static size_t ValueSize(VideoRotation) { return kValueSizeBytes; } - static bool Write(ArrayView data, VideoRotation value); - static bool Parse(ArrayView data, uint8_t* value); + static bool Write(std::span data, VideoRotation value); + static bool Parse(std::span data, uint8_t* value); static size_t ValueSize(uint8_t /* value */) { return kValueSizeBytes; } - static bool Write(ArrayView data, uint8_t value); + static bool Write(std::span data, uint8_t value); }; class PlayoutDelayLimits { @@ -217,10 +217,10 @@ class PlayoutDelayLimits { // Maximum playout delay value in milliseconds. static constexpr TimeDelta kMax = 0xfff * kGranularity; // 40950. - static bool Parse(ArrayView data, + static bool Parse(std::span data, VideoPlayoutDelay* playout_delay); static size_t ValueSize(const VideoPlayoutDelay&) { return kValueSizeBytes; } - static bool Write(ArrayView data, + static bool Write(std::span data, const VideoPlayoutDelay& playout_delay); }; @@ -233,10 +233,10 @@ class VideoContentTypeExtension { return RtpExtension::kVideoContentTypeUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, VideoContentType* content_type); static size_t ValueSize(VideoContentType) { return kValueSizeBytes; } - static bool Write(ArrayView data, VideoContentType content_type); + static bool Write(std::span data, VideoContentType content_type); }; class VideoTimingExtension { @@ -258,15 +258,15 @@ class VideoTimingExtension { static constexpr uint8_t kNetworkTimestampDeltaOffset = 9; static constexpr uint8_t kNetwork2TimestampDeltaOffset = 11; - static bool Parse(ArrayView data, VideoSendTiming* timing); + static bool Parse(std::span data, VideoSendTiming* timing); static size_t ValueSize(const VideoSendTiming&) { return kValueSizeBytes; } - static bool Write(ArrayView data, const VideoSendTiming& timing); + static bool Write(std::span data, const VideoSendTiming& timing); static size_t ValueSize(uint16_t /* time_delta_ms */, uint8_t /* idx */) { return kValueSizeBytes; } // Writes only single time delta to position idx. - static bool Write(ArrayView data, + static bool Write(std::span data, uint16_t time_delta_ms, uint8_t offset); }; @@ -281,12 +281,12 @@ class ColorSpaceExtension { return RtpExtension::kColorSpaceUri; } - static bool Parse(ArrayView data, ColorSpace* color_space); + static bool Parse(std::span data, ColorSpace* color_space); static size_t ValueSize(const ColorSpace& color_space) { return color_space.hdr_metadata() ? kValueSizeBytes : kValueSizeBytesWithoutHdrMetadata; } - static bool Write(ArrayView data, const ColorSpace& color_space); + static bool Write(std::span data, const ColorSpace& color_space); private: static constexpr int kChromaticityDenominator = 50000; // 0.00002 resolution. @@ -297,12 +297,12 @@ class ColorSpaceExtension { ColorSpace::RangeID range, ColorSpace::ChromaSiting chroma_siting_horizontal, ColorSpace::ChromaSiting chroma_siting_vertical); - static size_t ParseHdrMetadata(ArrayView data, + static size_t ParseHdrMetadata(std::span data, HdrMetadata* hdr_metadata); static size_t ParseChromaticity(const uint8_t* data, HdrMasteringMetadata::Chromaticity* p); static size_t ParseLuminance(const uint8_t* data, float* f, int denominator); - static size_t WriteHdrMetadata(ArrayView data, + static size_t WriteHdrMetadata(std::span data, const HdrMetadata& hdr_metadata); static size_t WriteChromaticity(uint8_t* data, const HdrMasteringMetadata::Chromaticity& p); @@ -318,9 +318,9 @@ class BaseRtpStringExtension { // maximum length that can be encoded with one-byte header extensions. static constexpr uint8_t kMaxValueSizeBytes = 16; - static bool Parse(ArrayView data, std::string* str); + static bool Parse(std::span data, std::string* str); static size_t ValueSize(absl::string_view str) { return str.size(); } - static bool Write(ArrayView data, absl::string_view str); + static bool Write(std::span data, absl::string_view str); }; class RtpStreamId : public BaseRtpStringExtension { @@ -353,12 +353,12 @@ class InbandComfortNoiseExtension { "http://www.webrtc.org/experiments/rtp-hdrext/inband-cn"; static constexpr absl::string_view Uri() { return kUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, std::optional* level); static size_t ValueSize(std::optional /* level */) { return kValueSizeBytes; } - static bool Write(ArrayView data, std::optional level); + static bool Write(std::span data, std::optional level); }; class VideoFrameTrackingIdExtension { @@ -370,12 +370,12 @@ class VideoFrameTrackingIdExtension { return RtpExtension::kVideoFrameTrackingIdUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, uint16_t* video_frame_tracking_id); static size_t ValueSize(uint16_t /*video_frame_tracking_id*/) { return kValueSizeBytes; } - static bool Write(ArrayView data, uint16_t video_frame_tracking_id); + static bool Write(std::span data, uint16_t video_frame_tracking_id); }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet.cc b/modules/rtp_rtcp/source/rtp_packet.cc index 3515521fcd1..2b6cd006df5 100644 --- a/modules/rtp_rtcp/source/rtp_packet.cc +++ b/modules/rtp_rtcp/source/rtp_packet.cc @@ -12,11 +12,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/byte_io.h" @@ -93,7 +93,7 @@ bool RtpPacket::Parse(const uint8_t* buffer, size_t buffer_size) { return true; } -bool RtpPacket::Parse(ArrayView packet) { +bool RtpPacket::Parse(std::span packet) { return Parse(packet.data(), packet.size()); } @@ -216,23 +216,29 @@ void RtpPacket::ZeroMutableExtensions() { } } -void RtpPacket::SetCsrcs(ArrayView csrcs) { +void RtpPacket::SetCsrcs(std::span csrcs) { RTC_DCHECK_EQ(extensions_size_, 0); RTC_DCHECK_EQ(payload_size_, 0); RTC_DCHECK_EQ(padding_size_, 0); - RTC_DCHECK_LE(csrcs.size(), 0x0fu); - RTC_DCHECK_LE(kFixedHeaderSize + 4 * csrcs.size(), capacity()); + + if (csrcs.size() > kMaxCsrcs) { + RTC_LOG(LS_ERROR) << "Truncating CSRC list, length exceeded " << kMaxCsrcs + << ": " << csrcs.size(); + csrcs = csrcs.first(kMaxCsrcs); + } + payload_offset_ = kFixedHeaderSize + 4 * csrcs.size(); + buffer_.SetSize(payload_offset_); // SetSize before WriteAt. + WriteAt(0, (data()[0] & 0xF0) | dchecked_cast(csrcs.size())); size_t offset = kFixedHeaderSize; for (uint32_t csrc : csrcs) { ByteWriter::WriteBigEndian(WriteAt(offset), csrc); offset += 4; } - buffer_.SetSize(payload_offset_); } -ArrayView RtpPacket::AllocateRawExtension(int id, size_t length) { +std::span RtpPacket::AllocateRawExtension(int id, size_t length) { RTC_DCHECK_GE(id, RtpExtension::kMinId); RTC_DCHECK_LE(id, RtpExtension::kMaxId); RTC_DCHECK_GE(length, 1); @@ -241,23 +247,23 @@ ArrayView RtpPacket::AllocateRawExtension(int id, size_t length) { if (extension_entry != nullptr) { // Extension already reserved. Check if same length is used. if (extension_entry->length == length) - return MakeArrayView(WriteAt(extension_entry->offset), length); + return std::span(WriteAt(extension_entry->offset), length); RTC_LOG(LS_ERROR) << "Length mismatch for extension id " << id << ": expected " << static_cast(extension_entry->length) << ". received " << length; - return nullptr; + return {}; } if (payload_size_ > 0) { RTC_LOG(LS_ERROR) << "Can't add new extension id " << id << " after payload was set."; - return nullptr; + return {}; } if (padding_size_ > 0) { RTC_LOG(LS_ERROR) << "Can't add new extension id " << id << " after padding was set."; - return nullptr; + return {}; } const size_t num_csrc = data()[0] & 0x0F; @@ -286,7 +292,7 @@ ArrayView RtpPacket::AllocateRawExtension(int id, size_t length) { << "Extension cannot be registered: Not enough space left in " "buffer to change to two-byte header extension and add new " "extension."; - return nullptr; + return {}; } // Promote already written data to two-byte header format. PromoteToTwoByteHeaderExtension(); @@ -307,7 +313,7 @@ ArrayView RtpPacket::AllocateRawExtension(int id, size_t length) { if (extensions_offset + new_extensions_size > capacity()) { RTC_LOG(LS_ERROR) << "Extension cannot be registered: Not enough space left in buffer."; - return nullptr; + return {}; } // All checks passed, write down the extension headers. @@ -342,7 +348,7 @@ ArrayView RtpPacket::AllocateRawExtension(int id, size_t length) { SetExtensionLengthMaybeAddZeroPadding(extensions_offset); payload_offset_ = extensions_offset + extensions_size_padded; buffer_.SetSize(payload_offset_); - return MakeArrayView(WriteAt(extension_info_offset), extension_info_length); + return std::span(WriteAt(extension_info_offset), extension_info_length); } void RtpPacket::PromoteToTwoByteHeaderExtension() { @@ -402,7 +408,7 @@ uint8_t* RtpPacket::AllocatePayload(size_t size_bytes) { return SetPayloadSize(size_bytes); } -void RtpPacket::SetPayload(ArrayView payload) { +void RtpPacket::SetPayload(std::span payload) { if (payload.empty()) { SetPayloadSize(0); return; @@ -594,36 +600,36 @@ RtpPacket::ExtensionInfo& RtpPacket::FindOrCreateExtensionInfo(int id) { return extension_entries_.back(); } -ArrayView RtpPacket::FindExtension(ExtensionType type) const { +std::span RtpPacket::FindExtension(ExtensionType type) const { uint8_t id = extensions_.GetId(type); if (id == ExtensionManager::kInvalidId) { // Extension not registered. - return nullptr; + return {}; } ExtensionInfo const* extension_info = FindExtensionInfo(id); if (extension_info == nullptr) { - return nullptr; + return {}; } - return MakeArrayView(data() + extension_info->offset, extension_info->length); + return std::span(data() + extension_info->offset, extension_info->length); } -ArrayView RtpPacket::AllocateExtension(ExtensionType type, +std::span RtpPacket::AllocateExtension(ExtensionType type, size_t length) { // TODO(webrtc:7990): Add support for empty extensions (length==0). if (length == 0 || length > RtpExtension::kMaxValueSize || (!extensions_.ExtmapAllowMixed() && length > RtpExtension::kOneByteHeaderExtensionMaxValueSize)) { - return nullptr; + return {}; } uint8_t id = extensions_.GetId(type); if (id == ExtensionManager::kInvalidId) { // Extension not registered. - return nullptr; + return {}; } if (!extensions_.ExtmapAllowMixed() && id > RtpExtension::kOneByteHeaderExtensionMaxId) { - return nullptr; + return {}; } return AllocateRawExtension(id, length); } diff --git a/modules/rtp_rtcp/source/rtp_packet.h b/modules/rtp_rtcp/source/rtp_packet.h index 3d27520b106..027969aeff2 100644 --- a/modules/rtp_rtcp/source/rtp_packet.h +++ b/modules/rtp_rtcp/source/rtp_packet.h @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/copy_on_write_buffer.h" @@ -30,6 +30,12 @@ class RtpPacket { using ExtensionType = RTPExtensionType; using ExtensionManager = RtpHeaderExtensionMap; + // Maximum number of CSRCs in an RTP packet as specified in section + // "5.1 RTP Fixed Header Fields" of RFC 3550. + // Note: This is a different limit than the one that applies to RTCP packets + // (which is specified in section 6.1). + static constexpr size_t kMaxCsrcs = 15; + // `extensions` required for SetExtension/ReserveExtension functions during // packet creating and used if available in Parse function. // Adding and getting extensions will fail until `extensions` is @@ -52,7 +58,7 @@ class RtpPacket { // read or allocate extensions in methods GetExtension, AllocateExtension, // etc.) bool Parse(const uint8_t* buffer, size_t size); - bool Parse(ArrayView packet); + bool Parse(std::span packet); // Parse and move given buffer into Packet. bool Parse(CopyOnWriteBuffer packet); @@ -77,8 +83,8 @@ class RtpPacket { size_t payload_size() const { return payload_size_; } bool has_padding() const { return buffer_[0] & 0x20; } size_t padding_size() const { return padding_size_; } - ArrayView payload() const { - return MakeArrayView(data() + payload_offset_, payload_size_); + std::span payload() const { + return std::span(data() + payload_offset_, payload_size_); } CopyOnWriteBuffer PayloadBuffer() const { return buffer_.Slice(payload_offset_, payload_size_); @@ -91,6 +97,7 @@ class RtpPacket { return payload_offset_ + payload_size_ + padding_size_; } const uint8_t* data() const { return buffer_.cdata(); } + std::span buffer() const { return buffer_; } size_t FreeCapacity() const { return capacity() - size(); } size_t MaxPayloadSize() const { return capacity() - headers_size(); } @@ -118,7 +125,7 @@ class RtpPacket { // Writes csrc list. Assumes: // a) There is enough room left in buffer. // b) Extension headers, payload or padding data has not already been added. - void SetCsrcs(ArrayView csrcs); + void SetCsrcs(std::span csrcs); // Header extensions. template @@ -138,24 +145,24 @@ class RtpPacket { // Returns view of the raw extension or empty view on failure. template - ArrayView GetRawExtension() const; + std::span GetRawExtension() const; template bool SetExtension(const Values&...); template - bool SetRawExtension(ArrayView data); + bool SetRawExtension(std::span data); template bool ReserveExtension(); // Find or allocate an extension `type`. Returns view of size `length` // to write raw extension to or an empty view on failure. - ArrayView AllocateExtension(ExtensionType type, size_t length); + std::span AllocateExtension(ExtensionType type, size_t length); // Find an extension `type`. // Returns view of the raw extension or empty view on failure. - ArrayView FindExtension(ExtensionType type) const; + std::span FindExtension(ExtensionType type) const; // Returns pointer to the payload of size at least `size_bytes`. // Keeps original payload, if any. If `size_bytes` is larger than current @@ -166,7 +173,7 @@ class RtpPacket { uint8_t* AllocatePayload(size_t size_bytes); // Sets payload size to `payload.size()` and copies `payload`. - void SetPayload(ArrayView payload); + void SetPayload(std::span payload); bool SetPadding(size_t padding_size); @@ -196,8 +203,8 @@ class RtpPacket { ExtensionInfo& FindOrCreateExtensionInfo(int id); // Allocates and returns place to store rtp header extension. - // Returns empty arrayview on failure. - ArrayView AllocateRawExtension(int id, size_t length); + // Returns empty std::span on failure. + std::span AllocateRawExtension(int id, size_t length); // Promotes existing one-byte header extensions to two-byte header extensions // by rewriting the data and updates the corresponding extension offsets. @@ -256,7 +263,7 @@ std::optional RtpPacket::GetExtension() const { } template -ArrayView RtpPacket::GetRawExtension() const { +std::span RtpPacket::GetRawExtension() const { return FindExtension(Extension::kId); } @@ -270,8 +277,8 @@ bool RtpPacket::SetExtension(const Values&... values) { } template -bool RtpPacket::SetRawExtension(ArrayView data) { - ArrayView buffer = AllocateExtension(Extension::kId, data.size()); +bool RtpPacket::SetRawExtension(std::span data) { + std::span buffer = AllocateExtension(Extension::kId, data.size()); if (buffer.empty()) { return false; } diff --git a/modules/rtp_rtcp/source/rtp_packet_history.cc b/modules/rtp_rtcp/source/rtp_packet_history.cc index 9b2db996dd1..f2154240fdf 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history.cc @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/function_view.h" #include "api/units/time_delta.h" @@ -299,7 +299,7 @@ std::unique_ptr RtpPacketHistory::GetPayloadPaddingPacket( } void RtpPacketHistory::CullAcknowledgedPackets( - ArrayView sequence_numbers) { + std::span sequence_numbers) { MutexLock lock(&lock_); for (uint16_t sequence_number : sequence_numbers) { int packet_index = GetPacketIndex(sequence_number); diff --git a/modules/rtp_rtcp/source/rtp_packet_history.h b/modules/rtp_rtcp/source/rtp_packet_history.h index ebd2f9119a4..9a8c8825fd3 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.h +++ b/modules/rtp_rtcp/source/rtp_packet_history.h @@ -16,8 +16,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/function_view.h" #include "api/units/time_delta.h" @@ -116,7 +116,7 @@ class RtpPacketHistory { encapsulate); // Cull packets that have been acknowledged as received by the remote end. - void CullAcknowledgedPackets(ArrayView sequence_numbers); + void CullAcknowledgedPackets(std::span sequence_numbers); // Remove all pending packets from the history, but keep storage mode and // capacity. diff --git a/modules/rtp_rtcp/source/rtp_packet_send_info.cc b/modules/rtp_rtcp/source/rtp_packet_send_info.cc deleted file mode 100644 index f0e6091ea76..00000000000 --- a/modules/rtp_rtcp/source/rtp_packet_send_info.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "api/transport/network_types.h" -#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "modules/rtp_rtcp/source/rtp_header_extensions.h" -#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" -#include "rtc_base/checks.h" - -namespace webrtc { - -RtpPacketSendInfo RtpPacketSendInfo::From(const RtpPacketToSend& packet, - const PacedPacketInfo& pacing_info) { - RtpPacketSendInfo packet_info; - if (packet.transport_sequence_number()) { - packet_info.transport_sequence_number = - *packet.transport_sequence_number() & 0xFFFF; - } else { - std::optional packet_id = - packet.GetExtension(); - if (packet_id) { - packet_info.transport_sequence_number = *packet_id; - } - } - - packet_info.rtp_timestamp = packet.Timestamp(); - packet_info.length = packet.size(); - packet_info.pacing_info = pacing_info; - packet_info.packet_type = packet.packet_type(); - - switch (*packet_info.packet_type) { - case RtpPacketMediaType::kAudio: - case RtpPacketMediaType::kVideo: - packet_info.media_ssrc = packet.Ssrc(); - packet_info.rtp_sequence_number = packet.SequenceNumber(); - break; - case RtpPacketMediaType::kRetransmission: - RTC_DCHECK(packet.original_ssrc() && - packet.retransmitted_sequence_number()); - // For retransmissions, we're want to remove the original media packet - // if the retransmit arrives - so populate that in the packet info. - packet_info.media_ssrc = packet.original_ssrc().value_or(0); - packet_info.rtp_sequence_number = - packet.retransmitted_sequence_number().value_or(0); - break; - case RtpPacketMediaType::kPadding: - case RtpPacketMediaType::kForwardErrorCorrection: - // We're not interested in feedback about these packets being received - // or lost. - break; - } - return packet_info; -} - -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet_send_info_unittest.cc b/modules/rtp_rtcp/source/rtp_packet_send_info_unittest.cc deleted file mode 100644 index f7858f948e6..00000000000 --- a/modules/rtp_rtcp/source/rtp_packet_send_info_unittest.cc +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "api/transport/network_types.h" -#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" -#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "modules/rtp_rtcp/source/rtp_header_extensions.h" -#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -RtpPacketToSend BuildPacket(RtpPacketMediaType type) { - RtpHeaderExtensionMap extension_manager; - RtpPacketToSend packet(&extension_manager); - - packet.SetSsrc(1); - packet.SetSequenceNumber(89); - if (type == RtpPacketMediaType::kRetransmission) { - packet.set_original_ssrc(2); - packet.set_retransmitted_sequence_number(678); - } - packet.set_transport_sequence_number(0xFFFFFFFF01); - packet.SetTimestamp(123); - packet.SetPayloadSize(5); - packet.set_packet_type(type); - return packet; -} - -void VerifyDefaultProperties(const RtpPacketSendInfo& send_info, - const RtpPacketToSend& packet, - const PacedPacketInfo& paced_info) { - EXPECT_EQ(send_info.length, packet.size()); - EXPECT_EQ(send_info.rtp_timestamp, packet.Timestamp()); - EXPECT_EQ(send_info.packet_type, packet.packet_type()); - EXPECT_EQ(send_info.pacing_info, paced_info); - if (packet.transport_sequence_number()) { - EXPECT_EQ(send_info.transport_sequence_number, - *packet.transport_sequence_number() & 0xFFFF); - } else { - EXPECT_EQ(send_info.transport_sequence_number, - *packet.GetExtension()); - } -} - -TEST(RtpPacketSendInfoTest, FromConvertsMediaPackets) { - RtpPacketToSend packet = BuildPacket(RtpPacketMediaType::kAudio); - PacedPacketInfo paced_info; - paced_info.probe_cluster_id = 8; - - RtpPacketSendInfo send_info = RtpPacketSendInfo::From(packet, paced_info); - EXPECT_EQ(send_info.media_ssrc, packet.Ssrc()); - VerifyDefaultProperties(send_info, packet, paced_info); -} - -TEST(RtpPacketSendInfoTest, FromConvertsPadding) { - RtpPacketToSend packet = BuildPacket(RtpPacketMediaType::kPadding); - PacedPacketInfo paced_info; - paced_info.probe_cluster_id = 8; - - RtpPacketSendInfo send_info = RtpPacketSendInfo::From(packet, paced_info); - EXPECT_EQ(send_info.media_ssrc, std::nullopt); - VerifyDefaultProperties(send_info, packet, paced_info); -} - -TEST(RtpPacketSendInfoTest, FromConvertsFec) { - RtpPacketToSend packet = - BuildPacket(RtpPacketMediaType::kForwardErrorCorrection); - PacedPacketInfo paced_info; - paced_info.probe_cluster_id = 8; - - RtpPacketSendInfo send_info = RtpPacketSendInfo::From(packet, paced_info); - EXPECT_EQ(send_info.media_ssrc, std::nullopt); - VerifyDefaultProperties(send_info, packet, paced_info); -} - -TEST(RtpPacketSendInfoTest, FromConvertsRetransmission) { - RtpPacketToSend packet = BuildPacket(RtpPacketMediaType::kRetransmission); - PacedPacketInfo paced_info; - paced_info.probe_cluster_id = 8; - - RtpPacketSendInfo send_info = RtpPacketSendInfo::From(packet, paced_info); - EXPECT_EQ(send_info.media_ssrc, *packet.original_ssrc()); - EXPECT_EQ(send_info.rtp_sequence_number, - *packet.retransmitted_sequence_number()); - VerifyDefaultProperties(send_info, packet, paced_info); -} - -TEST(RtpPacketSendInfoTest, FromFallbackToTranportSequenceHeaderExtension) { - RtpHeaderExtensionMap extension_manager; - extension_manager.Register(/*id=*/1); - PacedPacketInfo paced_info; - paced_info.probe_cluster_id = 8; - RtpPacketToSend packet(&extension_manager); - packet.SetSsrc(1); - packet.SetSequenceNumber(89); - const uint16_t kTransportSequenceNumber = 5555; - packet.SetExtension(kTransportSequenceNumber); - packet.SetTimestamp(123); - packet.AllocatePayload(5); - packet.set_packet_type(RtpPacketMediaType::kAudio); - - RtpPacketSendInfo send_info = RtpPacketSendInfo::From(packet, paced_info); - VerifyDefaultProperties(send_info, packet, paced_info); -} - -} // namespace -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet_unittest.cc b/modules/rtp_rtcp/source/rtp_packet_unittest.cc index 4f696985a6d..1343d0a7b46 100644 --- a/modules/rtp_rtcp/source/rtp_packet_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packet_unittest.cc @@ -13,12 +13,13 @@ #include #include #include +#include #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_headers.h" #include "api/units/time_delta.h" #include "api/video/color_space.h" @@ -437,7 +438,7 @@ TEST(RtpPacketTest, SetReservedExtensionsAfterPayload) { } TEST(RtpPacketTest, SetEmptyPayload) { - ArrayView empty_payload; + std::span empty_payload; RtpPacket packet; packet.SetPayload(empty_payload); @@ -446,7 +447,7 @@ TEST(RtpPacketTest, SetEmptyPayload) { TEST(RtpPacketTest, SetEmptyPayloadOverwritesExistingPayload) { const uint8_t payload[] = {1, 2, 3, 4, 2, 0, 42}; - ArrayView empty_payload; + std::span empty_payload; RtpPacket packet; packet.SetPayload(payload); @@ -505,7 +506,7 @@ TEST(RtpPacketTest, UsesZerosForPadding) { RtpPacket packet; EXPECT_TRUE(packet.SetPadding(kPaddingSize)); - EXPECT_THAT(MakeArrayView(packet.data() + 12, kPaddingSize - 1), Each(0)); + EXPECT_THAT(std::span(packet.data() + 12, kPaddingSize - 1), Each(0)); } TEST(RtpPacketTest, CreateOneBytePadding) { @@ -920,11 +921,11 @@ struct UncopyableExtension { static constexpr absl::string_view Uri() { return "uri"; } static size_t ValueSize(const UncopyableValue& /* value */) { return 1; } - static bool Write(ArrayView /* data */, + static bool Write(std::span /* data */, const UncopyableValue& /* value */) { return true; } - static bool Parse(ArrayView /* data */, + static bool Parse(std::span /* data */, UncopyableValue* /* value */) { return true; } @@ -957,12 +958,12 @@ struct ParseByReferenceExtension { static size_t ValueSize(uint8_t /* value1 */, uint8_t /* value2 */) { return 2; } - static bool Write(ArrayView data, uint8_t value1, uint8_t value2) { + static bool Write(std::span data, uint8_t value1, uint8_t value2) { data[0] = value1; data[1] = value2; return true; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, uint8_t& value1, uint8_t& value2) { value1 = data[0]; @@ -1347,5 +1348,27 @@ TEST(RtpPacketTest, SetExtensionWithArray) { ElementsAreArray(extension_data)); } +TEST(RtpPacketTest, SetCsrcsTruncatesWhenExceedingMax) { + RtpPacketToSend packet(nullptr); + packet.SetPayloadType(kPayloadType); + packet.SetSequenceNumber(kSeqNum); + packet.SetTimestamp(kTimestamp); + packet.SetSsrc(kSsrc); + + std::vector many_csrcs; + for (uint32_t i = 0; i < 20; ++i) { + many_csrcs.push_back(kSsrc + i); + } + + // SetCsrcs should truncate to maximum elements allowed. + packet.SetCsrcs(many_csrcs); + + std::vector csrcs = packet.Csrcs(); + EXPECT_EQ(csrcs.size(), RtpPacket::kMaxCsrcs); + for (size_t i = 0; i < RtpPacket::kMaxCsrcs; ++i) { + EXPECT_EQ(csrcs[i], many_csrcs[i]); + } +} + } // namespace } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packetizer_av1.cc b/modules/rtp_rtcp/source/rtp_packetizer_av1.cc index e96460e53b6..e9c61a8972e 100644 --- a/modules/rtp_rtcp/source/rtp_packetizer_av1.cc +++ b/modules/rtp_rtcp/source/rtp_packetizer_av1.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/leb128.h" #include "modules/rtp_rtcp/source/rtp_format.h" @@ -70,7 +70,7 @@ int MaxFragmentSize(int remaining_bytes) { } // namespace -RtpPacketizerAv1::RtpPacketizerAv1(ArrayView payload, +RtpPacketizerAv1::RtpPacketizerAv1(std::span payload, RtpPacketizer::PayloadSizeLimits limits, VideoFrameType frame_type, bool is_last_frame_in_picture) @@ -80,7 +80,7 @@ RtpPacketizerAv1::RtpPacketizerAv1(ArrayView payload, is_last_frame_in_picture_(is_last_frame_in_picture) {} std::vector RtpPacketizerAv1::ParseObus( - ArrayView payload) { + std::span payload) { std::vector result; ByteBufferReader payload_reader(payload); while (payload_reader.Length() > 0) { @@ -99,8 +99,8 @@ std::vector RtpPacketizerAv1::ParseObus( } if (!ObuHasSize(obu.header)) { obu.payload = - MakeArrayView(reinterpret_cast(payload_reader.Data()), - payload_reader.Length()); + std::span(reinterpret_cast(payload_reader.Data()), + payload_reader.Length()); payload_reader.Consume(payload_reader.Length()); } else { uint64_t size = 0; @@ -111,7 +111,7 @@ std::vector RtpPacketizerAv1::ParseObus( << payload_reader.Length(); return {}; } - obu.payload = MakeArrayView( + obu.payload = std::span( reinterpret_cast(payload_reader.Data()), size); payload_reader.Consume(size); } @@ -147,7 +147,7 @@ int RtpPacketizerAv1::AdditionalBytesForPreviousObuElement( } std::vector RtpPacketizerAv1::PacketizeInternal( - ArrayView obus, + std::span obus, PayloadSizeLimits limits) { std::vector packets; if (obus.empty()) { @@ -300,7 +300,7 @@ std::vector RtpPacketizerAv1::PacketizeInternal( } std::vector RtpPacketizerAv1::Packetize( - ArrayView obus, + std::span obus, PayloadSizeLimits limits) { std::vector packets = PacketizeInternal(obus, limits); if (packets.size() <= 1) { diff --git a/modules/rtp_rtcp/source/rtp_packetizer_av1.h b/modules/rtp_rtcp/source/rtp_packetizer_av1.h index b64418aacef..c121877ff8f 100644 --- a/modules/rtp_rtcp/source/rtp_packetizer_av1.h +++ b/modules/rtp_rtcp/source/rtp_packetizer_av1.h @@ -14,9 +14,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/rtp_format.h" @@ -24,7 +24,7 @@ namespace webrtc { class RtpPacketizerAv1 : public RtpPacketizer { public: - RtpPacketizerAv1(ArrayView payload, + RtpPacketizerAv1(std::span payload, PayloadSizeLimits limits, VideoFrameType frame_type, bool is_last_frame_in_picture); @@ -37,7 +37,7 @@ class RtpPacketizerAv1 : public RtpPacketizer { struct Obu { uint8_t header; uint8_t extension_header; // undefined if (header & kXbit) == 0 - ArrayView payload; + std::span payload; int size; // size of the header and payload combined. }; struct Packet { @@ -53,14 +53,14 @@ class RtpPacketizerAv1 : public RtpPacketizer { }; // Parses the payload into serie of OBUs. - static std::vector ParseObus(ArrayView payload); + static std::vector ParseObus(std::span payload); // Returns the number of additional bytes needed to store the previous OBU // element if an additonal OBU element is added to the packet. static int AdditionalBytesForPreviousObuElement(const Packet& packet); - static std::vector PacketizeInternal(ArrayView obus, + static std::vector PacketizeInternal(std::span obus, PayloadSizeLimits limits); // Packetize and try to distribute the payload evenly across packets. - static std::vector Packetize(ArrayView obus, + static std::vector Packetize(std::span obus, PayloadSizeLimits limits); uint8_t AggregationHeader() const; diff --git a/modules/rtp_rtcp/source/rtp_packetizer_av1_unittest.cc b/modules/rtp_rtcp/source/rtp_packetizer_av1_unittest.cc index a7e65134d6b..2a5a8cadc47 100644 --- a/modules/rtp_rtcp/source/rtp_packetizer_av1_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packetizer_av1_unittest.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/video/encoded_image.h" #include "api/video/video_frame_type.h" @@ -40,8 +40,8 @@ constexpr uint8_t kNewCodedVideoSequenceBit = 0b00'00'1000; // Wrapper around rtp_packet to make it look like container of payload bytes. struct RtpPayload { - using value_type = ArrayView::value_type; - using const_iterator = ArrayView::const_iterator; + using value_type = std::span::value_type; + using const_iterator = std::span::iterator; RtpPayload() : rtp_packet(/*extensions=*/nullptr) {} RtpPayload& operator=(RtpPayload&&) = default; @@ -77,7 +77,7 @@ class Av1Frame { }; std::vector Packetize( - ArrayView payload, + std::span payload, RtpPacketizer::PayloadSizeLimits limits, VideoFrameType frame_type = VideoFrameType::kVideoFrameDelta, bool is_last_frame_in_picture = true) { @@ -92,8 +92,8 @@ std::vector Packetize( return result; } -Av1Frame ReassembleFrame(ArrayView rtp_payloads) { - std::vector> payloads(rtp_payloads.size()); +Av1Frame ReassembleFrame(std::span rtp_payloads) { + std::vector> payloads(rtp_payloads.size()); for (size_t i = 0; i < rtp_payloads.size(); ++i) { payloads[i] = rtp_payloads[i]; } diff --git a/modules/rtp_rtcp/source/rtp_packetizer_h265.cc b/modules/rtp_rtcp/source/rtp_packetizer_h265.cc index b25721c0584..03117543948 100644 --- a/modules/rtp_rtcp/source/rtp_packetizer_h265.cc +++ b/modules/rtp_rtcp/source/rtp_packetizer_h265.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "common_video/h264/h264_common.h" #include "common_video/h265/h265_common.h" #include "modules/rtp_rtcp/source/byte_io.h" @@ -26,7 +26,7 @@ namespace webrtc { -RtpPacketizerH265::RtpPacketizerH265(ArrayView payload, +RtpPacketizerH265::RtpPacketizerH265(std::span payload, PayloadSizeLimits limits) : limits_(limits), num_packets_left_(0) { for (const H264::NaluIndex& nalu : H264::FindNaluIndices(payload)) { @@ -36,7 +36,7 @@ RtpPacketizerH265::RtpPacketizerH265(ArrayView payload, return; } input_fragments_.push_back( - payload.subview(nalu.payload_start_offset, nalu.payload_size)); + payload.subspan(nalu.payload_start_offset, nalu.payload_size)); } if (!GeneratePackets()) { @@ -84,7 +84,7 @@ bool RtpPacketizerH265::GeneratePackets() { bool RtpPacketizerH265::PacketizeFu(size_t fragment_index) { // Fragment payload into packets (FU). // Strip out the original header and leave room for the FU header. - ArrayView fragment = input_fragments_[fragment_index]; + std::span fragment = input_fragments_[fragment_index]; PayloadSizeLimits limits = limits_; // Refer to section 4.4.3 in RFC7798, each FU fragment will have a 2-bytes // payload header and a one-byte FU header. DONL is not supported so ignore @@ -125,7 +125,7 @@ bool RtpPacketizerH265::PacketizeFu(size_t fragment_index) { int packet_length = payload_sizes[i]; RTC_CHECK_GT(packet_length, 0); uint16_t header = (fragment[0] << 8) | fragment[1]; - packets_.push({.source_fragment = fragment.subview(offset, packet_length), + packets_.push({.source_fragment = fragment.subspan(offset, packet_length), .first_fragment = (i == 0), .last_fragment = (i == payload_sizes.size() - 1), .aggregated = false, @@ -144,7 +144,7 @@ int RtpPacketizerH265::PacketizeAp(size_t fragment_index) { size_t payload_size_left = limits_.max_payload_len; int aggregated_fragments = 0; size_t fragment_headers_length = 0; - ArrayView fragment = input_fragments_[fragment_index]; + std::span fragment = input_fragments_[fragment_index]; RTC_CHECK_GE(payload_size_left, fragment.size()); ++num_packets_left_; @@ -247,7 +247,7 @@ void RtpPacketizerH265::NextAggregatePacket(RtpPacketToSend* rtp_packet) { uint8_t temporal_id_min = kH265MaxTemporalId; while (packet->aggregated) { // Add NAL unit length field. - ArrayView fragment = packet->source_fragment; + std::span fragment = packet->source_fragment; uint8_t layer_id = ((fragment[0] & kH265LayerIDHMask) << 5) | ((fragment[1] & kH265LayerIDLMask) >> 3); layer_id_min = std::min(layer_id_min, layer_id); @@ -310,7 +310,7 @@ void RtpPacketizerH265::NextFragmentPacket(RtpPacketToSend* rtp_packet) { // Now update payload_hdr_h with FU type. payload_hdr_h = (payload_hdr_h & kH265TypeMaskN) | (H265::NaluType::kFu << 1) | layer_id_h; - ArrayView fragment = packet->source_fragment; + std::span fragment = packet->source_fragment; uint8_t* buffer = rtp_packet->AllocatePayload( kH265FuHeaderSizeBytes + kH265PayloadHeaderSizeBytes + fragment.size()); RTC_CHECK(buffer); diff --git a/modules/rtp_rtcp/source/rtp_packetizer_h265.h b/modules/rtp_rtcp/source/rtp_packetizer_h265.h index 9c57fd887d4..d3fca2e584e 100644 --- a/modules/rtp_rtcp/source/rtp_packetizer_h265.h +++ b/modules/rtp_rtcp/source/rtp_packetizer_h265.h @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" @@ -27,7 +27,7 @@ class RtpPacketizerH265 : public RtpPacketizer { // Initialize with payload from encoder. // The payload_data must be exactly one encoded H.265 frame. // For H265 we only support tx-mode SRST. - RtpPacketizerH265(ArrayView payload, PayloadSizeLimits limits); + RtpPacketizerH265(std::span payload, PayloadSizeLimits limits); RtpPacketizerH265(const RtpPacketizerH265&) = delete; RtpPacketizerH265& operator=(const RtpPacketizerH265&) = delete; @@ -43,13 +43,13 @@ class RtpPacketizerH265 : public RtpPacketizer { private: struct PacketUnit { - ArrayView source_fragment; + std::span source_fragment; bool first_fragment = false; bool last_fragment = false; bool aggregated = false; uint16_t header = 0; }; - std::deque> input_fragments_; + std::deque> input_fragments_; std::queue packets_; bool GeneratePackets(); diff --git a/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc b/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc index a8c78542e69..73985c3752f 100644 --- a/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packetizer_h265_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "common_video/h265/h265_common.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_h265_common.h" @@ -54,60 +54,69 @@ struct NalUnitHeader { // Creates Buffer that looks like nal unit of given header and size. Buffer GenerateNalUnit(NalUnitHeader header, size_t size) { RTC_CHECK_GT(size, 0); - Buffer buffer = Buffer::CreateUninitializedWithSize(size); - buffer[0] = (header.nal_unit_type << 1) | (header.nuh_layer_id >> 5); - buffer[1] = (header.nuh_layer_id << 3) | header.nuh_temporal_id_plus1; - for (size_t i = 2; i < size; ++i) { - buffer[i] = static_cast(i); - } - // Last byte shouldn't be 0, or it may be counted as part of next 4-byte start - // sequence. - buffer[size - 1] |= 0x10; + Buffer buffer = Buffer::CreateWithCapacity(size); + buffer.AppendData(size, [&](std::span buffer_view) { + buffer_view[0] = (header.nal_unit_type << 1) | (header.nuh_layer_id >> 5); + buffer_view[1] = (header.nuh_layer_id << 3) | header.nuh_temporal_id_plus1; + for (size_t i = 2; i < size; ++i) { + buffer_view[i] = static_cast(i); + } + // Last byte shouldn't be 0, or it may be counted as part of next 4-byte + // start sequence. + buffer_view[size - 1] |= 0x10; + return size; + }); return buffer; } // Create frame consisting of nalus of given size. Buffer CreateFrame(std::initializer_list nalu_sizes) { static constexpr int kStartCodeSize = 3; - Buffer frame = Buffer::CreateUninitializedWithSize( - absl::c_accumulate(nalu_sizes, size_t{0}) + - kStartCodeSize * nalu_sizes.size()); - size_t offset = 0; - for (size_t nalu_size : nalu_sizes) { - EXPECT_GE(nalu_size, 1u); - // Insert nalu start code - frame[offset] = 0; - frame[offset + 1] = 0; - frame[offset + 2] = 1; - // Set some valid header. - frame[offset + 3] = 2; - // Fill payload avoiding accidental start codes - if (nalu_size > 1) { - memset(frame.data() + offset + 4, 0x3f, nalu_size - 1); + size_t size = absl::c_accumulate(nalu_sizes, size_t{0}) + + kStartCodeSize * nalu_sizes.size(); + Buffer frame = Buffer::CreateWithCapacity(size); + frame.AppendData(size, [&](std::span frame_view) { + size_t offset = 0; + for (size_t nalu_size : nalu_sizes) { + EXPECT_GE(nalu_size, 1u); + // Insert nalu start code + frame_view[offset] = 0; + frame_view[offset + 1] = 0; + frame_view[offset + 2] = 1; + // Set some valid header. + frame_view[offset + 3] = 2; + // Fill payload avoiding accidental start codes + if (nalu_size > 1) { + memset(frame_view.data() + offset + 4, 0x3f, nalu_size - 1); + } + offset += (kStartCodeSize + nalu_size); } - offset += (kStartCodeSize + nalu_size); - } + return offset; + }); return frame; } // Create frame consisting of given nalus. -Buffer CreateFrame(ArrayView nalus) { +Buffer CreateFrame(std::span nalus) { static constexpr int kStartCodeSize = 3; int frame_size = 0; for (const Buffer& nalu : nalus) { frame_size += (kStartCodeSize + nalu.size()); } - Buffer frame = Buffer::CreateUninitializedWithSize(frame_size); - size_t offset = 0; - for (const Buffer& nalu : nalus) { - // Insert nalu start code - frame[offset] = 0; - frame[offset + 1] = 0; - frame[offset + 2] = 1; - // Copy the nalu unit. - memcpy(frame.data() + offset + 3, nalu.data(), nalu.size()); - offset += (kStartCodeSize + nalu.size()); - } + Buffer frame = Buffer::CreateWithCapacity(frame_size); + frame.AppendData(frame_size, [&](std::span frame_view) { + size_t offset = 0; + for (const Buffer& nalu : nalus) { + // Insert nalu start code + frame_view[offset] = 0; + frame_view[offset + 1] = 0; + frame_view[offset + 2] = 1; + // Copy the nalu unit. + memcpy(frame_view.data() + offset + 3, nalu.data(), nalu.size()); + offset += (kStartCodeSize + nalu.size()); + } + return offset; + }); return frame; } @@ -263,23 +272,23 @@ TEST(RtpPacketizerH265Test, ApRespectsNoPacketReduction) { 3 * kH265LengthFieldSizeBytes + 3 + 3 + 0x123); EXPECT_EQ(type, H265::NaluType::kAp); - payload = payload.subview(kH265NalHeaderSizeBytes); + payload = payload.subspan(kH265NalHeaderSizeBytes); // 1st fragment. - EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), + EXPECT_THAT(payload.subspan(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); // Size. - EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), + EXPECT_THAT(payload.subspan(kH265LengthFieldSizeBytes, 3), ElementsAreArray(nalus[0])); - payload = payload.subview(kH265LengthFieldSizeBytes + 3); + payload = payload.subspan(kH265LengthFieldSizeBytes + 3); // 2nd fragment. - EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), + EXPECT_THAT(payload.subspan(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); // Size. - EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), + EXPECT_THAT(payload.subspan(kH265LengthFieldSizeBytes, 3), ElementsAreArray(nalus[1])); - payload = payload.subview(kH265LengthFieldSizeBytes + 3); + payload = payload.subspan(kH265LengthFieldSizeBytes + 3); // 3rd fragment. - EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), + EXPECT_THAT(payload.subspan(0, kH265LengthFieldSizeBytes), ElementsAre(0x1, 0x23)); // Size. - EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes), + EXPECT_THAT(payload.subspan(kH265LengthFieldSizeBytes), ElementsAreArray(nalus[2])); } @@ -319,21 +328,21 @@ TEST(RtpPacketizerH265Test, ApRespectsLayerIdAndTemporalId) { EXPECT_EQ(type, H265::NaluType::kAp); EXPECT_EQ(layer_id, 0); EXPECT_EQ(temporal_id, 1); - payload = payload.subview(kH265NalHeaderSizeBytes); + payload = payload.subspan(kH265NalHeaderSizeBytes); // 1st fragment. - EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); - EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), + EXPECT_THAT(payload.subspan(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); + EXPECT_THAT(payload.subspan(kH265LengthFieldSizeBytes, 3), ElementsAreArray(nalus[0])); - payload = payload.subview(kH265LengthFieldSizeBytes + 3); + payload = payload.subspan(kH265LengthFieldSizeBytes + 3); // 2nd fragment. - EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); - EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), + EXPECT_THAT(payload.subspan(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); + EXPECT_THAT(payload.subspan(kH265LengthFieldSizeBytes, 3), ElementsAreArray(nalus[1])); - payload = payload.subview(kH265LengthFieldSizeBytes + 3); + payload = payload.subspan(kH265LengthFieldSizeBytes + 3); // 3rd fragment. - EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), + EXPECT_THAT(payload.subspan(0, kH265LengthFieldSizeBytes), ElementsAre(0x1, 0x23)); - EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes), + EXPECT_THAT(payload.subspan(kH265LengthFieldSizeBytes), ElementsAreArray(nalus[2])); } @@ -621,14 +630,14 @@ TEST_P(RtpPacketizerH265ParametrizedTest, MixedApFu) { if (expected_packet.aggregated) { int type = H265::ParseNaluType(packets[i].payload()[0]); EXPECT_THAT(type, H265::NaluType::kAp); - auto payload = packets[i].payload().subview(kH265NalHeaderSizeBytes); + auto payload = packets[i].payload().subspan(kH265NalHeaderSizeBytes); int offset = 0; // Generated AP packet header and payload align for (int j = expected_packet.nalu_index; j < expected_packet.nalu_number; j++) { - EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), + EXPECT_THAT(payload.subspan(0, kH265LengthFieldSizeBytes), ElementsAre(0, nalus[j].size())); - EXPECT_THAT(payload.subview(offset + kH265LengthFieldSizeBytes, + EXPECT_THAT(payload.subspan(offset + kH265LengthFieldSizeBytes, nalus[j].size()), ElementsAreArray(nalus[j])); offset += kH265LengthFieldSizeBytes + nalus[j].size(); @@ -638,9 +647,9 @@ TEST_P(RtpPacketizerH265ParametrizedTest, MixedApFu) { fu_header |= (expected_packet.first_fragment ? kH265SBitMask : 0); fu_header |= (expected_packet.last_fragment ? kH265EBitMask : 0); fu_header |= H265::NaluType::kIdrNLp; - EXPECT_THAT(packets[i].payload().subview(0, kFuHeaderSizeBytes), + EXPECT_THAT(packets[i].payload().subspan(0, kFuHeaderSizeBytes), ElementsAre(99, 2, fu_header)); - EXPECT_THAT(packets[i].payload().subview(kFuHeaderSizeBytes), + EXPECT_THAT(packets[i].payload().subspan(kFuHeaderSizeBytes), ElementsAreArray(nalus[expected_packet.nalu_index].data() + kH265NalHeaderSizeBytes + expected_packet.start_offset, diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc index e552033d0a5..c10b86620c0 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/rtp_headers.h" #include "api/sequence_checker.h" @@ -76,14 +76,27 @@ ModuleRtpRtcpImpl2::RtpSenderContext::RtpSenderContext( &packet_history, config.paced_sender ? config.paced_sender : &non_paced_sender) {} -ModuleRtpRtcpImpl2::ModuleRtpRtcpImpl2(const Environment& env, - const Configuration& configuration) +ModuleRtpRtcpImpl2::ModuleRtpRtcpImpl2( + const Environment& env, + const Configuration& configuration, + absl::AnyInvocable recv_ssrc_callback) : env_(env), worker_queue_(TaskQueueBase::Current()), + recv_ssrc_callback_(std::move(recv_ssrc_callback)), + rtp_sender_(configuration.receiver_only + ? nullptr + : std::make_unique(env_, + *worker_queue_, + configuration)), rtcp_sender_( env_, {.audio = configuration.audio, .local_media_ssrc = configuration.local_media_ssrc, + .recv_ssrc_callback = + (recv_ssrc_callback_ != nullptr) + ? absl::AnyInvocable( + [this] { return RtcpSenderSourceSsrc(); }) + : nullptr, .outgoing_transport = configuration.outgoing_transport, .non_sender_rtt_measurement = configuration.non_sender_rtt_measurement, @@ -108,8 +121,6 @@ ModuleRtpRtcpImpl2::ModuleRtpRtcpImpl2(const Environment& env, RTC_DCHECK(worker_queue_); rtcp_thread_checker_.Detach(); if (!configuration.receiver_only) { - rtp_sender_ = - std::make_unique(env_, *worker_queue_, configuration); rtp_sender_->sequencing_checker.Detach(); // Make sure rtcp sender use same timestamp offset as rtp sender. rtcp_sender_.SetTimestampOffset( @@ -161,7 +172,7 @@ std::optional ModuleRtpRtcpImpl2::FlexfecSsrc() const { } void ModuleRtpRtcpImpl2::IncomingRtcpPacket( - ArrayView rtcp_packet) { + std::span rtcp_packet) { RTC_DCHECK_RUN_ON(&rtcp_thread_checker_); rtcp_receiver_.IncomingPacket(rtcp_packet); } @@ -411,14 +422,14 @@ ModuleRtpRtcpImpl2::FetchFecPackets() { } void ModuleRtpRtcpImpl2::OnAbortedRetransmissions( - ArrayView sequence_numbers) { + std::span sequence_numbers) { RTC_DCHECK(rtp_sender_); RTC_DCHECK_RUN_ON(&rtp_sender_->sequencing_checker); rtp_sender_->packet_sender.OnAbortedRetransmissions(sequence_numbers); } void ModuleRtpRtcpImpl2::OnPacketsAcknowledged( - ArrayView sequence_numbers) { + std::span sequence_numbers) { RTC_DCHECK(rtp_sender_); rtp_sender_->packet_history.CullAcknowledgedPackets(sequence_numbers); } @@ -445,7 +456,7 @@ ModuleRtpRtcpImpl2::GeneratePadding(size_t target_size_bytes) { std::vector ModuleRtpRtcpImpl2::GetSentRtpPacketInfos( - ArrayView sequence_numbers) const { + std::span sequence_numbers) const { RTC_DCHECK(rtp_sender_); return rtp_sender_->packet_sender.GetSentRtpPacketInfos(sequence_numbers); } @@ -468,6 +479,7 @@ size_t ModuleRtpRtcpImpl2::MaxRtpPacketSize() const { } void ModuleRtpRtcpImpl2::SetMaxRtpPacketSize(size_t rtp_packet_size) { + RTC_DCHECK_RUN_ON(&rtcp_module_checker_); RTC_DCHECK_LE(rtp_packet_size, IP_PACKET_SIZE) << "rtp packet size too large: " << rtp_packet_size; RTC_DCHECK_GT(rtp_packet_size, packet_overhead_) @@ -584,6 +596,7 @@ void ModuleRtpRtcpImpl2::SetTmmbn(std::vector bounding_set) { // Send a Negative acknowledgment packet. int32_t ModuleRtpRtcpImpl2::SendNACK(const uint16_t* nack_list, const uint16_t size) { + RTC_DCHECK_RUN_ON(&rtcp_module_checker_); uint16_t nack_length = size; uint16_t start_id = 0; int64_t now_ms = env_.clock().TimeInMilliseconds(); @@ -612,9 +625,8 @@ int32_t ModuleRtpRtcpImpl2::SendNACK(const uint16_t* nack_list, } nack_last_seq_number_sent_ = nack_list[start_id + nack_length - 1]; - return rtcp_sender_.SendRTCP( - GetFeedbackState(), kRtcpNack, - MakeArrayView(&nack_list[start_id], nack_length)); + return rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpNack, + std::span(&nack_list[start_id], nack_length)); } void ModuleRtpRtcpImpl2::SendNack( @@ -623,6 +635,7 @@ void ModuleRtpRtcpImpl2::SendNack( } bool ModuleRtpRtcpImpl2::TimeToSendFullNackList(int64_t now) const { + RTC_DCHECK_RUN_ON(&rtcp_module_checker_); // Use RTT from RtcpRttStats class if provided. int64_t rtt = rtt_ms(); if (rtt == 0) { @@ -709,7 +722,7 @@ void ModuleRtpRtcpImpl2::OnReceivedNack( } void ModuleRtpRtcpImpl2::OnReceivedRtcpReportBlocks( - ArrayView report_blocks) { + std::span report_blocks) { if (rtp_sender_) { uint32_t ssrc = SSRC(); std::optional rtx_ssrc; @@ -832,4 +845,12 @@ void ModuleRtpRtcpImpl2::ScheduleMaybeSendRtcpAtOrAfterTimestamp( duration.RoundUpTo(TimeDelta::Millis(1))); } +uint32_t ModuleRtpRtcpImpl2::RtcpSenderSourceSsrc() { + RTC_DCHECK_RUN_ON(&rtcp_module_checker_); + uint32_t ssrc = recv_ssrc_callback_(); + // Inform the RtcpReceiver that this is now the SSRC to listen for + rtcp_receiver_.set_local_media_ssrc(ssrc); + return ssrc; +} + } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.h b/modules/rtp_rtcp/source/rtp_rtcp_impl2.h index d603c454f55..3c74f9489cb 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.h @@ -16,10 +16,13 @@ #include #include +#include +#include #include +#include "absl/functional/any_invocable.h" +#include "absl/memory/memory.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/rtp_headers.h" #include "api/sequence_checker.h" @@ -42,6 +45,7 @@ #include "modules/rtp_rtcp/source/rtp_sender.h" #include "modules/rtp_rtcp/source/rtp_sender_egress.h" #include "modules/rtp_rtcp/source/rtp_sequence_number_map.h" +#include "rtc_base/checks.h" #include "rtc_base/gtest_prod_util.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/system/no_unique_address.h" @@ -53,17 +57,47 @@ namespace webrtc { struct PacedPacketInfo; struct RTPVideoHeader; +// This class and its members handles sending and receiving of RTP and RTCP +// on behalf of a single media stream (incoming or outgoing). +// +// Threading and locking model: +// Instances of ModuleRtpRtcpImpl2 are created and destroyed on the worker +// queue. +// The RTP sender lives on the RTP packet producing thread, +// identified by rtp_sender_->sequencing_checker; calls that are forwarded +// to the rtp_sender_ need to be called on this thread. +// The RTCPSender allows multithread access using its `mutex_rtcp_sender_`. +// The RTCPReceiver does the same using `mutex_receiver_lock_`. +// These three objects are therefore not marked with guarding in this class. +// Incoming RTCP packets are processed on the thread bound to +// `rtcp_thread_checker_`. +// `mutex_rtt_` allows multi-thread access to the "rtt" member. + class ModuleRtpRtcpImpl2 final : public RtpRtcpInterface, public RTCPReceiver::ModuleRtpRtcp { public: - ModuleRtpRtcpImpl2(const Environment& env, - const RtpRtcpInterface::Configuration& configuration); + static std::unique_ptr CreateSendModule( + const Environment& env, + const RtpRtcpInterface::Configuration& configuration) { + RTC_DCHECK(!configuration.receiver_only); + return absl::WrapUnique( + new ModuleRtpRtcpImpl2(env, configuration, nullptr)); + } + static std::unique_ptr CreateReceiveModule( + const Environment& env, + const RtpRtcpInterface::Configuration& configuration, + absl::AnyInvocable recv_ssrc_callback) { + RTC_DCHECK(configuration.receiver_only); + RTC_DCHECK(recv_ssrc_callback); + return absl::WrapUnique(new ModuleRtpRtcpImpl2( + env, configuration, std::move(recv_ssrc_callback))); + } ~ModuleRtpRtcpImpl2() override; // Receiver part. // Called when we receive an RTCP packet. - void IncomingRtcpPacket(ArrayView incoming_packet) override; + void IncomingRtcpPacket(std::span incoming_packet) override; void SetRemoteSSRC(uint32_t ssrc) override; @@ -157,16 +191,16 @@ class ModuleRtpRtcpImpl2 final : public RtpRtcpInterface, std::vector> FetchFecPackets() override; void OnAbortedRetransmissions( - ArrayView sequence_numbers) override; + std::span sequence_numbers) override; void OnPacketsAcknowledged( - ArrayView sequence_numbers) override; + std::span sequence_numbers) override; std::vector> GeneratePadding( size_t target_size_bytes) override; std::vector GetSentRtpPacketInfos( - ArrayView sequence_numbers) const override; + std::span sequence_numbers) const override; size_t ExpectedPerPacketOverhead() const override; @@ -241,7 +275,7 @@ class ModuleRtpRtcpImpl2 final : public RtpRtcpInterface, void OnReceivedNack( const std::vector& nack_sequence_numbers) override; void OnReceivedRtcpReportBlocks( - ArrayView report_blocks) override; + std::span report_blocks) override; void OnRequestSendReport() override; void SetVideoBitrateAllocation( @@ -272,6 +306,11 @@ class ModuleRtpRtcpImpl2 final : public RtpRtcpInterface, RTPSender packet_generator; }; + // Private constructor, to enforce sender/receiver separation. + ModuleRtpRtcpImpl2(const Environment& env, + const RtpRtcpInterface::Configuration& configuration, + absl::AnyInvocable recv_ssrc_callback); + void set_rtt_ms(int64_t rtt_ms); int64_t rtt_ms() const; @@ -301,21 +340,37 @@ class ModuleRtpRtcpImpl2 final : public RtpRtcpInterface, void ScheduleMaybeSendRtcpAtOrAfterTimestamp(Timestamp execution_time, TimeDelta duration); + // This function is called by the RtcpSender when the module is configured + // for not sending RTP to query for the local SSRC, which may change over + // time. As a side effect, it informs the RtcpReceiver of the currently + // used SSRC. + uint32_t RtcpSenderSourceSsrc(); + const Environment env_; TaskQueueBase* const worker_queue_; + // Thread checker used for guarding the IncomingRtcpPacket call. RTC_NO_UNIQUE_ADDRESS SequenceChecker rtcp_thread_checker_; + // Thread checker used for guarding member variables in this class. + RTC_NO_UNIQUE_ADDRESS SequenceChecker rtcp_module_checker_; + // TODO: issues.webrtc.org/48665180 - figure out why these are different. + + // The function for getting the right SSRC for sending RTCP reports + // Must outlive rtcp_sender_, so placed before it. + absl::AnyInvocable recv_ssrc_callback_ + RTC_GUARDED_BY(rtcp_module_checker_); - std::unique_ptr rtp_sender_; + // These three classes contain their own thread checking. + const std::unique_ptr rtp_sender_; RTCPSender rtcp_sender_; RTCPReceiver rtcp_receiver_; - uint16_t packet_overhead_; + uint16_t packet_overhead_ RTC_GUARDED_BY(rtcp_module_checker_); // Send side - int64_t nack_last_time_sent_full_ms_; - uint16_t nack_last_seq_number_sent_; + int64_t nack_last_time_sent_full_ms_ RTC_GUARDED_BY(rtcp_module_checker_); + uint16_t nack_last_seq_number_sent_ RTC_GUARDED_BY(rtcp_module_checker_); - RtcpRttStats* const rtt_stats_; + RtcpRttStats* const rtt_stats_ RTC_PT_GUARDED_BY(worker_queue_); RepeatingTaskHandle rtt_update_task_ RTC_GUARDED_BY(worker_queue_); // The processed RTT from RtcpRttStats. diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc index 903390ac2f4..e29f98d21c2 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -36,6 +36,7 @@ #include "modules/include/module_fec_types.h" #include "modules/rtp_rtcp/include/flexfec_sender.h" #include "modules/rtp_rtcp/include/receive_statistics.h" +#include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h" #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/include/rtcp_statistics.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" @@ -115,19 +116,19 @@ class SendTransport : public Transport { last_packet_(&header_extensions_), rtcp_packets_(task_queue_factory.CreateTaskQueue( "transport", - TaskQueueFactory::Priority::NORMAL)) {} + TaskQueueFactory::Priority::kNormal)) {} ~SendTransport() override = default; void SetRtpRtcpModule(ModuleRtpRtcpImpl2* receiver) { receiver_ = receiver; } void SimulateNetworkDelay(TimeDelta delay) { delay_ = delay; } - bool SendRtp(ArrayView data, + bool SendRtp(std::span data, const PacketOptions& /* options */) override { EXPECT_TRUE(last_packet_.Parse(data)); ++rtp_packets_sent_; return true; } - bool SendRtcp(ArrayView data, + bool SendRtcp(std::span data, const PacketOptions& /* options */) override { test::RtcpPacketParser parser; parser.Parse(data); @@ -262,7 +263,7 @@ class RtpRtcpModule : public RtcpPacketTypeCounterObserver, config.send_packet_observer = this; config.rtp_stats_callback = this; config.fec_generator = fec_generator_; - impl_ = std::make_unique(env_, config); + impl_ = ModuleRtpRtcpImpl2::CreateSendModule(env_, config); impl_->SetRemoteSSRC(is_sender_ ? kReceiverSsrc : kSenderSsrc); impl_->SetRTCPStatus(RtcpMode::kCompound); } @@ -370,7 +371,8 @@ class RtpRtcpImpl2Test : public ::testing::Test { .flags = false}; const uint8_t payload[100] = {0}; - bool success = module->impl_->OnSendingRtpFrame(0, 0, kPayloadType, true); + bool success = module->impl_->OnSendingRtpFrame( + rtp_timestamp, capture_time_ms, kPayloadType, true); success &= sender->SendVideo( kPayloadType, VideoCodecType::kVideoCodecVP8, rtp_timestamp, @@ -1210,7 +1212,62 @@ TEST_F(RtpRtcpImpl2Test, SendPacketSendsPacketOnTransport) { packet->set_packet_type(RtpPacketMediaType::kAudio); sender_.impl_->SendPacket(std::move(packet), PacedPacketInfo()); + EXPECT_EQ(sender_.RtpSent(), 1); } +TEST_F(RtpRtcpImpl2Test, NtpOffsetValidAfterRrtrDlrrExchanges) { + // Use a fixed one-way delay. + sender_.transport_.SimulateNetworkDelay(kOneWayNetworkDelay); + receiver_.transport_.SimulateNetworkDelay(kOneWayNetworkDelay); + sender_.transport_.SetRtpRtcpModule(receiver_.impl_.get()); + receiver_.transport_.SetRtpRtcpModule(sender_.impl_.get()); + + sender_.impl_->RegisterSendPayloadFrequency(kPayloadType, 90000); + RemoteNtpTimeEstimator ntp_estimator(time_controller_.GetClock()); + + // We need 3 exchanges for the estimator to become valid. + for (int i = 0; i < 3; ++i) { + // 1. Receiver sends RRTR. + AdvanceTime(TimeDelta::Millis(10)); + EXPECT_EQ(0, receiver_.impl_->SendRTCP(kRtcpReport)); + AdvanceTime(kOneWayNetworkDelay); + + // 2. Sender receives RRTR, sends a frame, and then sends SR+DLRR. + AdvanceTime(TimeDelta::Millis(10)); + EXPECT_TRUE(SendFrame(&sender_, sender_video_.get(), kBaseLayerTid)); + AdvanceTime(TimeDelta::Zero()); + EXPECT_EQ(0, sender_.impl_->SendRTCP(kRtcpReport)); + AdvanceTime(kOneWayNetworkDelay); + + // 3. Receiver receives SR+DLRR and updates the estimator. + // This happens on PeriodicUpdate(), which is driven from AdvanceTime. + + std::optional non_sender_rtt_stats = + receiver_.impl_->GetNonSenderRttStats(); + if (non_sender_rtt_stats && non_sender_rtt_stats->round_trip_time) { + TimeDelta rtt = *non_sender_rtt_stats->round_trip_time; + std::optional sr_stats = + receiver_.impl_->GetSenderReportStats(); + if (sr_stats) { + ntp_estimator.UpdateRtcpTimestamp(rtt, + sr_stats->last_remote_ntp_timestamp, + sr_stats->last_remote_rtp_timestamp); + } + } + // Advance time to next iteration. + AdvanceTime(kDefaultReportInterval); + } + + std::optional estimated_offset = + ntp_estimator.EstimateRemoteToLocalClockOffset(); + ASSERT_TRUE(estimated_offset.has_value()); + // Unit of offset is in NTP time resolution, 1/2^32 seconds, + // approximately 0.2 nanoseconds. + // Offset should be close to 0, but some variation is allowed. + // 40000 ticks =~0.08 milliseconds. + constexpr int64_t kNtpTimeTicksOffsetEpsilon = 40'000; + EXPECT_NEAR(*estimated_offset, 0, kNtpTimeTicksOffsetEpsilon); +} + } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_rtcp_interface.h b/modules/rtp_rtcp/source/rtp_rtcp_interface.h index 5d5b44274e9..630fa941632 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_interface.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_interface.h @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/rtp_headers.h" #include "api/rtp_packet_sender.h" @@ -180,7 +180,7 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface { // Receiver functions // ************************************************************************** - virtual void IncomingRtcpPacket(ArrayView incoming_packet) = 0; + virtual void IncomingRtcpPacket(std::span incoming_packet) = 0; virtual void SetRemoteSSRC(uint32_t ssrc) = 0; @@ -340,16 +340,16 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface { virtual std::vector> FetchFecPackets() = 0; virtual void OnAbortedRetransmissions( - ArrayView sequence_numbers) = 0; + std::span sequence_numbers) = 0; virtual void OnPacketsAcknowledged( - ArrayView sequence_numbers) = 0; + std::span sequence_numbers) = 0; virtual std::vector> GeneratePadding( size_t target_size_bytes) = 0; virtual std::vector GetSentRtpPacketInfos( - ArrayView sequence_numbers) const = 0; + std::span sequence_numbers) const = 0; // Returns an expected per packet overhead representing the main RTP header, // any CSRCs, and the registered header extensions that are expected on all diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index ef9d0250f6f..0e78f982a9c 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/rtp_headers.h" #include "api/rtp_packet_sender.h" @@ -198,15 +198,15 @@ RTPSender::~RTPSender() { // to understand performance attributes and possibly remove locks. } -ArrayView RTPSender::FecExtensionSizes() { +std::span RTPSender::FecExtensionSizes() { return kFecOrPaddingExtensionSizes; } -ArrayView RTPSender::VideoExtensionSizes() { +std::span RTPSender::VideoExtensionSizes() { return kVideoExtensionSizes; } -ArrayView RTPSender::AudioExtensionSizes() { +std::span RTPSender::AudioExtensionSizes() { return kAudioExtensionSizes; } @@ -513,7 +513,7 @@ size_t RTPSender::ExpectedPerPacketOverhead() const { } std::unique_ptr RTPSender::AllocatePacket( - ArrayView csrcs) { + std::span csrcs) { MutexLock lock(&send_mutex_); RTC_DCHECK_LE(csrcs.size(), kRtpCsrcSize); if (csrcs.size() > max_num_csrcs_) { @@ -649,9 +649,9 @@ static void CopyHeaderAndExtensionsToRtxPacket(const RtpPacketToSend& packet, continue; } - ArrayView source = packet.FindExtension(extension); + std::span source = packet.FindExtension(extension); - ArrayView destination = + std::span destination = rtx_packet->AllocateExtension(extension, source.size()); // Could happen if any: @@ -662,7 +662,7 @@ static void CopyHeaderAndExtensionsToRtxPacket(const RtpPacketToSend& packet, continue; } - std::memcpy(destination.begin(), source.begin(), destination.size()); + std::memcpy(destination.data(), source.data(), destination.size()); } } diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h index 5535a4061ca..c6490360907 100644 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ b/modules/rtp_rtcp/source/rtp_sender.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/rtp_packet_sender.h" #include "modules/rtp_rtcp/include/flexfec_sender.h" @@ -114,21 +114,21 @@ class RTPSender { RTC_LOCKS_EXCLUDED(send_mutex_); // Size info for header extensions used by FEC packets. - static ArrayView FecExtensionSizes() + static std::span FecExtensionSizes() RTC_LOCKS_EXCLUDED(send_mutex_); // Size info for header extensions used by video packets. - static ArrayView VideoExtensionSizes() + static std::span VideoExtensionSizes() RTC_LOCKS_EXCLUDED(send_mutex_); // Size info for header extensions used by audio packets. - static ArrayView AudioExtensionSizes() + static std::span AudioExtensionSizes() RTC_LOCKS_EXCLUDED(send_mutex_); // Create empty packet, fills ssrc, csrcs and reserve place for header // extensions RtpSender updates before sending. std::unique_ptr AllocatePacket( - ArrayView csrcs = {}) RTC_LOCKS_EXCLUDED(send_mutex_); + std::span csrcs = {}) RTC_LOCKS_EXCLUDED(send_mutex_); // Maximum header overhead per fec/padding packet. size_t FecOrPaddingPacketMaxRtpHeaderLength() const diff --git a/modules/rtp_rtcp/source/rtp_sender_audio.h b/modules/rtp_rtcp/source/rtp_sender_audio.h index 6a87d358cb3..b206ff2f5aa 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio.h +++ b/modules/rtp_rtcp/source/rtp_sender_audio.h @@ -15,9 +15,9 @@ #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/timestamp.h" #include "modules/audio_coding/include/audio_coding_module_typedefs.h" #include "modules/rtp_rtcp/source/absolute_capture_time_sender.h" @@ -48,7 +48,7 @@ class RTPSenderAudio { struct RtpAudioFrame { AudioFrameType type = AudioFrameType::kAudioFrameSpeech; - ArrayView payload; + std::span payload; // Payload id to write to the payload type field of the rtp packet. int payload_id = -1; @@ -65,7 +65,7 @@ class RTPSenderAudio { std::optional audio_level_dbov; // Contributing sources list. - ArrayView csrcs; + std::span csrcs; }; bool SendAudio(const RtpAudioFrame& frame); diff --git a/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc index 7cc4e1ebf1f..6962e0fab45 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_audio_unittest.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -53,13 +53,13 @@ class LoopbackTransportTest : public Transport { kAbsoluteCaptureTimeExtensionId); } - bool SendRtp(ArrayView data, + bool SendRtp(std::span data, const PacketOptions& /*options*/) override { sent_packets_.push_back(RtpPacketReceived(&receivers_extensions_)); EXPECT_TRUE(sent_packets_.back().Parse(data)); return true; } - bool SendRtcp(ArrayView /* data */, + bool SendRtcp(std::span /* data */, const PacketOptions& /* options */) override { return false; } @@ -78,21 +78,22 @@ class RtpSenderAudioTest : public ::testing::Test { RtpSenderAudioTest() : fake_clock_(kStartTime), env_(CreateEnvironment(&fake_clock_)), - rtp_module_(env_, - {.audio = true, - .outgoing_transport = &transport_, - .local_media_ssrc = kSsrc}), + rtp_module_(ModuleRtpRtcpImpl2::CreateSendModule( + env_, + {.audio = true, + .outgoing_transport = &transport_, + .local_media_ssrc = kSsrc})), rtp_sender_audio_( std::make_unique(&fake_clock_, - rtp_module_.RtpSender())) { - rtp_module_.SetSequenceNumber(kSeqNum); + rtp_module_->RtpSender())) { + rtp_module_->SetSequenceNumber(kSeqNum); } AutoThread main_thread_; SimulatedClock fake_clock_; const Environment env_; LoopbackTransportTest transport_; - ModuleRtpRtcpImpl2 rtp_module_; + const std::unique_ptr rtp_module_; std::unique_ptr rtp_sender_audio_; }; @@ -112,8 +113,8 @@ TEST_F(RtpSenderAudioTest, SendAudio) { TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) { const uint8_t kAudioLevel = 0x5a; - rtp_module_.RegisterRtpHeaderExtension(AudioLevelExtension::Uri(), - kAudioLevelExtensionId); + rtp_module_->RegisterRtpHeaderExtension(AudioLevelExtension::Uri(), + kAudioLevelExtensionId); const char payload_name[] = "PAYLOAD_NAME"; const uint8_t payload_type = 127; @@ -158,8 +159,8 @@ TEST_F(RtpSenderAudioTest, SendAudioWithoutAbsoluteCaptureTime) { TEST_F(RtpSenderAudioTest, SendAudioWithAbsoluteCaptureTimeWithCaptureClockOffset) { - rtp_module_.RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), - kAbsoluteCaptureTimeExtensionId); + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), + kAbsoluteCaptureTimeExtensionId); constexpr Timestamp kAbsoluteCaptureTimestamp = Timestamp::Millis(521); const char payload_name[] = "audio"; const uint8_t payload_type = 127; diff --git a/modules/rtp_rtcp/source/rtp_sender_egress.cc b/modules/rtp_rtcp/source/rtp_sender_egress.cc index 2d62e194e58..d4d39c2bdda 100644 --- a/modules/rtp_rtcp/source/rtp_sender_egress.cc +++ b/modules/rtp_rtcp/source/rtp_sender_egress.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/rtc_event_log/rtc_event_log.h" @@ -370,7 +370,7 @@ void RtpSenderEgress::SetTimestampOffset(uint32_t timestamp) { } std::vector RtpSenderEgress::GetSentRtpPacketInfos( - ArrayView sequence_numbers) const { + std::span sequence_numbers) const { RTC_DCHECK_RUN_ON(worker_queue_); RTC_DCHECK(!sequence_numbers.empty()); if (!need_rtp_packet_infos_) { @@ -410,7 +410,7 @@ RtpSenderEgress::FetchFecPackets() { } void RtpSenderEgress::OnAbortedRetransmissions( - ArrayView sequence_numbers) { + std::span sequence_numbers) { RTC_DCHECK_RUN_ON(worker_queue_); // Mark aborted retransmissions as sent, rather than leaving them in // a 'pending' state - otherwise they can not be requested again and @@ -441,7 +441,7 @@ bool RtpSenderEgress::SendPacketToNetwork(const RtpPacketToSend& packet, const PacketOptions& options, const PacedPacketInfo& pacing_info) { RTC_DCHECK_RUN_ON(worker_queue_); - if (transport_ == nullptr || !transport_->SendRtp(packet, options)) { + if (transport_ == nullptr || !transport_->SendRtp(packet.buffer(), options)) { RTC_LOG(LS_WARNING) << "Transport failed to send packet."; return false; } diff --git a/modules/rtp_rtcp/source/rtp_sender_egress.h b/modules/rtp_rtcp/source/rtp_sender_egress.h index df07a6d346e..35779a4bbd6 100644 --- a/modules/rtp_rtcp/source/rtp_sender_egress.h +++ b/modules/rtp_rtcp/source/rtp_sender_egress.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/rtp_packet_sender.h" @@ -93,14 +93,14 @@ class RtpSenderEgress { // recalled, return a vector with all of them (in corresponding order). // If any could not be recalled, return an empty vector. std::vector GetSentRtpPacketInfos( - ArrayView sequence_numbers) const; + std::span sequence_numbers) const; void SetFecProtectionParameters(const FecProtectionParams& delta_params, const FecProtectionParams& key_params); std::vector> FetchFecPackets(); // Clears pending status for these sequence numbers in the packet history. - void OnAbortedRetransmissions(ArrayView sequence_numbers); + void OnAbortedRetransmissions(std::span sequence_numbers); private: struct Packet { diff --git a/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc index 1f073070d48..1c58e13e9fc 100644 --- a/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -92,7 +92,7 @@ class MockStreamDataCountersCallback : public StreamDataCountersCallback { }; struct TransmittedPacket { - TransmittedPacket(ArrayView data, + TransmittedPacket(std::span data, const PacketOptions& packet_options, RtpHeaderExtensionMap* extensions) : packet(extensions), options(packet_options) { @@ -107,7 +107,7 @@ class TestTransport : public Transport { explicit TestTransport(RtpHeaderExtensionMap* extensions) : total_data_sent_(DataSize::Zero()), extensions_(extensions) {} MOCK_METHOD(void, SentRtp, (const PacketOptions& options), ()); - bool SendRtp(ArrayView packet, + bool SendRtp(std::span packet, const PacketOptions& options) override { total_data_sent_ += DataSize::Bytes(packet.size()); last_packet_.emplace(packet, options, extensions_); @@ -115,7 +115,7 @@ class TestTransport : public Transport { return true; } - bool SendRtcp(ArrayView /* packet */, + bool SendRtcp(std::span /* packet */, const PacketOptions& /* options */) override { RTC_CHECK_NOTREACHED(); } @@ -852,7 +852,7 @@ TEST_F(RtpSenderEgressTest, TEST_F(RtpSenderEgressTest, SendPacketUpdatesStats) { const size_t kPayloadSize = 1000; - const ArrayView kNoRtpHeaderExtensionSizes; + const std::span kNoRtpHeaderExtensionSizes; FlexfecSender flexfec(env_, kFlexfectPayloadType, kFlexFecSsrc, kSsrc, /*mid=*/"", /*rtp_header_extensions=*/{}, diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index 945214e552b..d5d2fa0f73a 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -15,17 +15,18 @@ #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/crypto/frame_encryptor_interface.h" #include "api/field_trials_view.h" #include "api/make_ref_counted.h" #include "api/media_types.h" +#include "api/task_queue/task_queue_factory.h" #include "api/transport/rtp/corruption_detection_message.h" #include "api/transport/rtp/dependency_descriptor.h" #include "api/units/data_rate.h" @@ -210,7 +211,11 @@ RTPSenderVideo::RTPSenderVideo(const Config& config) config.frame_transformer, rtp_sender_->SSRC(), rtp_sender_->Rid(), - config.task_queue_factory) + config.task_queue_factory, + config.field_trials->IsEnabled( + "WebRTC-MediaTaskQueuePriorities") + ? TaskQueueFactory::Priority::kVideo + : TaskQueueFactory::Priority::kNormal) : nullptr) { if (frame_transformer_delegate_) frame_transformer_delegate_->Init(); @@ -517,7 +522,7 @@ bool RTPSenderVideo::SendVideo(int payload_type, VideoCodecType codec_type, uint32_t rtp_timestamp, Timestamp capture_time, - ArrayView payload, + std::span payload, size_t encoder_output_size, RTPVideoHeader video_header, TimeDelta expected_retransmission_time, diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index b3c33d6317a..396591a4024 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/frame_transformer_interface.h" #include "api/scoped_refptr.h" @@ -106,7 +106,7 @@ class RTPSenderVideo : public RTPVideoFrameSenderInterface { VideoCodecType codec_type, uint32_t rtp_timestamp, Timestamp capture_time, - ArrayView payload, + std::span payload, size_t encoder_output_size, RTPVideoHeader video_header, TimeDelta expected_retransmission_time, diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc index b6bbfb92175..c54f9f89913 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -61,7 +61,7 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface { encoded_data_(encoded_image.GetEncodedData()), pre_transform_payload_size_(encoded_image.size()), header_(video_header), - frame_type_(encoded_image._frameType), + frame_type_(encoded_image.frame_type()), payload_type_(payload_type), codec_type_(codec_type), timestamp_(rtp_timestamp), @@ -78,9 +78,9 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface { ~TransformableVideoSenderFrame() override = default; // Implements TransformableVideoFrameInterface. - ArrayView GetData() const override { return *encoded_data_; } + std::span GetData() const override { return *encoded_data_; } - void SetData(ArrayView data) override { + void SetData(std::span data) override { encoded_data_ = EncodedImageBuffer::Create(data.data(), data.size()); } @@ -164,14 +164,15 @@ RTPSenderVideoFrameTransformerDelegate::RTPSenderVideoFrameTransformerDelegate( scoped_refptr frame_transformer, uint32_t ssrc, std::string rid, - TaskQueueFactory* task_queue_factory) + TaskQueueFactory* task_queue_factory, + TaskQueueFactory::Priority transformation_queue_priority) : sender_(sender), frame_transformer_(std::move(frame_transformer)), ssrc_(ssrc), rid_(std::move(rid)), - transformation_queue_(task_queue_factory->CreateTaskQueue( - "video_frame_transformer", - TaskQueueFactory::Priority::NORMAL)) {} + transformation_queue_( + task_queue_factory->CreateTaskQueue("VideoFrameTransformerQueue", + transformation_queue_priority)) {} void RTPSenderVideoFrameTransformerDelegate::Init() { frame_transformer_->RegisterTransformedFrameSinkCallback( @@ -289,9 +290,9 @@ std::unique_ptr CloneSenderVideoFrame( original->GetData().data(), original->GetData().size()); EncodedImage encoded_image; encoded_image.SetEncodedData(encoded_image_buffer); - encoded_image._frameType = original->IsKeyFrame() - ? VideoFrameType::kVideoFrameKey - : VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(original->IsKeyFrame() + ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta); // TODO(bugs.webrtc.org/14708): Fill in other EncodedImage parameters // TODO(bugs.webrtc.org/14708): Use an actual RTT estimate for the // retransmission time instead of a const default, in the same way as a diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h index a588a1c7ae7..ad799a04085 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h +++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -43,7 +43,7 @@ class RTPVideoFrameSenderInterface { VideoCodecType codec_type, uint32_t rtp_timestamp, Timestamp capture_time, - ArrayView payload, + std::span payload, size_t encoder_output_size, RTPVideoHeader video_header, TimeDelta expected_retransmission_time, @@ -68,7 +68,9 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback { scoped_refptr frame_transformer, uint32_t ssrc, std::string rid, - TaskQueueFactory* send_transport_queue); + TaskQueueFactory* send_transport_queue, + TaskQueueFactory::Priority transformation_queue_priority = + TaskQueueFactory::Priority::kNormal); void Init(); diff --git a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc index d48d342e264..332b53b7d77 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate_unittest.cc @@ -10,15 +10,16 @@ #include "modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h" +#include #include #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/scoped_refptr.h" @@ -57,7 +58,7 @@ class MockRTPVideoFrameSenderInterface : public RTPVideoFrameSenderInterface { VideoCodecType codec_type, uint32_t rtp_timestamp, Timestamp capture_time, - ArrayView payload, + std::span payload, size_t encoder_output_size, RTPVideoHeader video_header, TimeDelta expected_retransmission_time, @@ -87,8 +88,8 @@ class RtpSenderVideoFrameTransformerDelegateTest : public ::testing::Test { bool key_frame = false) { EncodedImage encoded_image; encoded_image.SetEncodedData(EncodedImageBuffer::Create(1)); - encoded_image._frameType = key_frame ? VideoFrameType::kVideoFrameKey - : VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(key_frame ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta); std::unique_ptr frame = nullptr; EXPECT_CALL(*frame_transformer_, Transform) .WillOnce([&](std::unique_ptr @@ -276,6 +277,7 @@ TEST_F(RtpSenderVideoFrameTransformerDelegateTest, const uint8_t payload_type = 1; const uint32_t timestamp = 2; const std::vector frame_csrcs = {123, 456, 789}; + const std::array buffer = {3, 2, 1}; auto mock_receiver_frame = std::make_unique>(); @@ -286,8 +288,6 @@ TEST_F(RtpSenderVideoFrameTransformerDelegateTest, metadata.SetRTPVideoHeaderCodecSpecifics(RTPVideoHeaderVP8()); metadata.SetCsrcs(frame_csrcs); ON_CALL(*mock_receiver_frame, Metadata).WillByDefault(Return(metadata)); - ArrayView buffer = - (ArrayView)*EncodedImageBuffer::Create(1); ON_CALL(*mock_receiver_frame, GetData).WillByDefault(Return(buffer)); ON_CALL(*mock_receiver_frame, GetPayloadType) .WillByDefault(Return(payload_type)); @@ -300,12 +300,12 @@ TEST_F(RtpSenderVideoFrameTransformerDelegateTest, ASSERT_TRUE(callback); Event event; - EXPECT_CALL( - test_sender_, - SendVideo(payload_type, kVideoCodecVP8, timestamp, - /*capture_time=*/Timestamp::MinusInfinity(), buffer, _, _, - /*expected_retransmission_time=*/TimeDelta::Millis(10), - frame_csrcs)) + EXPECT_CALL(test_sender_, + SendVideo(payload_type, kVideoCodecVP8, timestamp, + /*capture_time=*/Timestamp::MinusInfinity(), + ElementsAreArray(buffer), _, _, + /*expected_retransmission_time=*/TimeDelta::Millis(10), + frame_csrcs)) .WillOnce(WithoutArgs([&] { event.Set(); return true; diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc index 47050f487d5..443b6088366 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/call/transport.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -141,13 +141,13 @@ class LoopbackTransportTest : public Transport { kCorruptionDetectionExtensionId); } - bool SendRtp(ArrayView data, + bool SendRtp(std::span data, const PacketOptions& /* options */) override { sent_packets_.push_back(RtpPacketReceived(&receivers_extensions_)); EXPECT_TRUE(sent_packets_.back().Parse(data)); return true; } - bool SendRtcp(ArrayView /* data */, + bool SendRtcp(std::span /* data */, const PacketOptions& /* options */) override { return false; } @@ -193,20 +193,20 @@ class RtpSenderVideoTest : public ::testing::Test { : fake_clock_(kStartTime), env_(CreateEnvironment(&fake_clock_)), retransmission_rate_limiter_(&fake_clock_, 1000), - rtp_module_( + rtp_module_(ModuleRtpRtcpImpl2::CreateSendModule( env_, {.outgoing_transport = &transport_, .retransmission_rate_limiter = &retransmission_rate_limiter_, .local_media_ssrc = kSsrc, .rtx_send_ssrc = kRtxSsrc, - .rid = "rid"}), + .rid = "rid"})), rtp_sender_video_( std::make_unique(&fake_clock_, - rtp_module_.RtpSender(), + rtp_module_->RtpSender(), env_.field_trials(), raw_packetization)) { - rtp_module_.SetSequenceNumber(kSeqNum); - rtp_module_.SetStartTimestamp(0); + rtp_module_->SetSequenceNumber(kSeqNum); + rtp_module_->SetStartTimestamp(0); } void UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed( @@ -218,14 +218,14 @@ class RtpSenderVideoTest : public ::testing::Test { const Environment env_; LoopbackTransportTest transport_; RateLimiter retransmission_rate_limiter_; - ModuleRtpRtcpImpl2 rtp_module_; + const std::unique_ptr rtp_module_; std::unique_ptr rtp_sender_video_; }; TEST_F(RtpSenderVideoTest, KeyFrameHasCVO) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(VideoOrientation::Uri(), - kVideoRotationExtensionId); + rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), + kVideoRotationExtensionId); RTPVideoHeader hdr; hdr.rotation = kVideoRotation_0; @@ -245,8 +245,8 @@ TEST_F(RtpSenderVideoTest, TimingFrameHasPacketizationTimstampSet) { const int64_t kPacketizationTimeMs = 100; const int64_t kEncodeStartDeltaMs = 10; const int64_t kEncodeFinishDeltaMs = 50; - rtp_module_.RegisterRtpHeaderExtension(VideoTimingExtension::Uri(), - kVideoTimingExtensionId); + rtp_module_->RegisterRtpHeaderExtension(VideoTimingExtension::Uri(), + kVideoTimingExtensionId); const Timestamp kCaptureTimestamp = fake_clock_.CurrentTime(); @@ -271,8 +271,8 @@ TEST_F(RtpSenderVideoTest, TimingFrameHasPacketizationTimstampSet) { TEST_F(RtpSenderVideoTest, WriteCorruptionExtensionIfHeaderContainsFrameInstrumentationData) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(CorruptionDetectionExtension::Uri(), - kCorruptionDetectionExtensionId); + rtp_module_->RegisterRtpHeaderExtension(CorruptionDetectionExtension::Uri(), + kCorruptionDetectionExtensionId); RTPVideoHeader hdr; hdr.frame_type = VideoFrameType::kVideoFrameKey; FrameInstrumentationData data; @@ -309,8 +309,8 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, WriteCorruptionExtensionIfHeaderContainsFrameInstrumentationSyncData) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(CorruptionDetectionExtension::Uri(), - kCorruptionDetectionExtensionId); + rtp_module_->RegisterRtpHeaderExtension(CorruptionDetectionExtension::Uri(), + kCorruptionDetectionExtensionId); RTPVideoHeader hdr; hdr.frame_type = VideoFrameType::kVideoFrameKey; // Send data with sequence index divisible by 2^7 and no sample values in @@ -341,8 +341,8 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, DeltaFrameHasCVOWhenChanged) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(VideoOrientation::Uri(), - kVideoRotationExtensionId); + rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), + kVideoRotationExtensionId); RTPVideoHeader hdr; hdr.rotation = kVideoRotation_90; @@ -365,8 +365,8 @@ TEST_F(RtpSenderVideoTest, DeltaFrameHasCVOWhenChanged) { TEST_F(RtpSenderVideoTest, DeltaFrameHasCVOWhenNonZero) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(VideoOrientation::Uri(), - kVideoRotationExtensionId); + rtp_module_->RegisterRtpHeaderExtension(VideoOrientation::Uri(), + kVideoRotationExtensionId); RTPVideoHeader hdr; hdr.rotation = kVideoRotation_90; @@ -467,7 +467,7 @@ TEST_F(RtpSenderVideoTest, RetransmissionTypesVP8HigherLayers) { header.codec = kVideoCodecVP8; auto& vp8_header = header.video_type_header.emplace(); - for (int tid = 1; tid <= kMaxTemporalStreams; ++tid) { + for (size_t tid = 1; tid <= kMaxTemporalStreams; ++tid) { vp8_header.temporalIdx = tid; EXPECT_FALSE(rtp_sender_video_->AllowRetransmission( @@ -487,7 +487,7 @@ TEST_F(RtpSenderVideoTest, RetransmissionTypesVP9) { header.codec = kVideoCodecVP9; auto& vp9_header = header.video_type_header.emplace(); - for (int tid = 1; tid <= kMaxTemporalStreams; ++tid) { + for (size_t tid = 1; tid <= kMaxTemporalStreams; ++tid) { vp9_header.temporal_idx = tid; EXPECT_FALSE(rtp_sender_video_->AllowRetransmission( @@ -590,15 +590,15 @@ TEST_F(RtpSenderVideoTest, constexpr int kRtxPayloadId = 101; constexpr size_t kMaxPacketSize = 1'000; - rtp_module_.SetMaxRtpPacketSize(kMaxPacketSize); - rtp_module_.RegisterRtpHeaderExtension(RtpMid::Uri(), 1); - rtp_module_.RegisterRtpHeaderExtension(RtpStreamId::Uri(), 2); - rtp_module_.RegisterRtpHeaderExtension(RepairedRtpStreamId::Uri(), 3); - rtp_module_.RegisterRtpHeaderExtension(AbsoluteSendTime::Uri(), 4); - rtp_module_.SetMid("long_mid"); - rtp_module_.SetRtxSendPayloadType(kRtxPayloadId, kMediaPayloadId); - rtp_module_.SetStorePacketsStatus(/*enable=*/true, 10); - rtp_module_.SetRtxSendStatus(kRtxRetransmitted); + rtp_module_->SetMaxRtpPacketSize(kMaxPacketSize); + rtp_module_->RegisterRtpHeaderExtension(RtpMid::Uri(), 1); + rtp_module_->RegisterRtpHeaderExtension(RtpStreamId::Uri(), 2); + rtp_module_->RegisterRtpHeaderExtension(RepairedRtpStreamId::Uri(), 3); + rtp_module_->RegisterRtpHeaderExtension(AbsoluteSendTime::Uri(), 4); + rtp_module_->SetMid("long_mid"); + rtp_module_->SetRtxSendPayloadType(kRtxPayloadId, kMediaPayloadId); + rtp_module_->SetStorePacketsStatus(/*enable=*/true, 10); + rtp_module_->SetRtxSendStatus(kRtxRetransmitted); RTPVideoHeader header; header.codec = kVideoCodecVP8; @@ -620,13 +620,13 @@ TEST_F(RtpSenderVideoTest, rb.SetMediaSsrc(kSsrc); rb.SetExtHighestSeqNum(transport_.last_sent_packet().SequenceNumber()); rr.AddReportBlock(rb); - rtp_module_.IncomingRtcpPacket(rr.Build()); + rtp_module_->IncomingRtcpPacket(rr.Build()); // Test for various frame size close to `kMaxPacketSize` to catch edge cases // when rtx packet barely fit. for (size_t frame_size = 800; frame_size < kMaxPacketSize; ++frame_size) { SCOPED_TRACE(frame_size); - ArrayView payload(kPayload, frame_size); + std::span payload(kPayload, frame_size); EXPECT_TRUE(rtp_sender_video_->SendVideo( kMediaPayloadId, /*codec_type=*/kVideoCodecVP8, /*rtp_timestamp=*/0, @@ -638,7 +638,7 @@ TEST_F(RtpSenderVideoTest, rtcp::Nack nack; nack.SetMediaSsrc(kSsrc); nack.SetPacketIds({media_packet.SequenceNumber()}); - rtp_module_.IncomingRtcpPacket(nack.Build()); + rtp_module_->IncomingRtcpPacket(nack.Build()); const RtpPacketReceived& rtx_packet = transport_.last_sent_packet(); EXPECT_EQ(rtx_packet.Ssrc(), kRtxSsrc); @@ -649,7 +649,7 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, SendsDependencyDescriptorWhenVideoStructureIsSet) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; @@ -720,9 +720,9 @@ TEST_F(RtpSenderVideoTest, SkipsDependencyDescriptorOnDeltaFrameWhenFailedToAttachToKeyFrame) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); - rtp_module_.SetExtmapAllowMixed(false); + rtp_module_->SetExtmapAllowMixed(false); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; // Use many templates so that key dependency descriptor would be too large @@ -775,7 +775,7 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, PropagatesChainDiffsIntoDependencyDescriptor) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; @@ -810,7 +810,7 @@ TEST_F(RtpSenderVideoTest, PropagatesActiveDecodeTargetsIntoDependencyDescriptor) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure; video_structure.num_decode_targets = 2; @@ -845,7 +845,7 @@ TEST_F(RtpSenderVideoTest, SetDiffentVideoStructureAvoidsCollisionWithThePreviousStructure) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); FrameDependencyStructure video_structure1; video_structure1.num_decode_targets = 2; @@ -923,19 +923,19 @@ TEST_F(RtpSenderVideoTest, static constexpr size_t kFrameSize = 100; uint8_t kFrame[kFrameSize] = {1, 2, 3, 4}; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpDependencyDescriptorExtension::Uri(), kDependencyDescriptorId); auto encryptor = make_ref_counted>(); ON_CALL(*encryptor, GetMaxCiphertextByteSize).WillByDefault(ReturnArg<1>()); ON_CALL(*encryptor, Encrypt) .WillByDefault(WithArgs<3, 5>( - [](ArrayView frame, size_t* bytes_written) { + [](std::span frame, size_t* bytes_written) { *bytes_written = frame.size(); return 0; })); RTPSenderVideo::Config config; config.clock = &fake_clock_; - config.rtp_sender = rtp_module_.RtpSender(); + config.rtp_sender = rtp_module_->RtpSender(); config.field_trials = &env_.field_trials(); config.frame_encryptor = encryptor.get(); RTPSenderVideo rtp_sender_video(config); @@ -965,7 +965,7 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, PopulateGenericFrameDescriptor) { const int64_t kFrameId = 100000; uint8_t kFrame[100]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpGenericFrameDescriptorExtension00::Uri(), kGenericDescriptorId); RTPVideoHeader hdr; @@ -998,7 +998,7 @@ void RtpSenderVideoTest:: const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpGenericFrameDescriptorExtension00::Uri(), kGenericDescriptorId); RTPVideoHeader hdr; @@ -1034,7 +1034,7 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, VideoLayersAllocationWithResolutionSentOnKeyFrames) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); @@ -1073,7 +1073,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationWithoutResolutionSentOnDeltaWhenUpdated) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); @@ -1124,7 +1124,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationWithResolutionSentOnDeltaWhenSpatialLayerAdded) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); @@ -1172,7 +1172,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationWithResolutionSentOnLargeFrameRateChange) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); @@ -1216,7 +1216,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationWithoutResolutionSentOnSmallFrameRateChange) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); @@ -1258,7 +1258,7 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, VideoLayersAllocationSentOnDeltaFramesOnlyOnUpdate) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); @@ -1303,7 +1303,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationSentOnDeltaFramesOnlyOnUpdate) { TEST_F(RtpSenderVideoTest, VideoLayersAllocationNotSentOnHigherTemporalLayers) { const size_t kFrameSize = 100; uint8_t kFrame[kFrameSize]; - rtp_module_.RegisterRtpHeaderExtension( + rtp_module_->RegisterRtpHeaderExtension( RtpVideoLayersAllocationExtension::Uri(), kVideoLayersAllocationExtensionId); @@ -1341,8 +1341,8 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationNotSentOnHigherTemporalLayers) { TEST_F(RtpSenderVideoTest, AbsoluteCaptureTimeNotForwardedWhenImageHasNoCaptureTime) { uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), - kAbsoluteCaptureTimeExtensionId); + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), + kAbsoluteCaptureTimeExtensionId); RTPVideoHeader hdr; hdr.frame_type = VideoFrameType::kVideoFrameKey; @@ -1359,13 +1359,13 @@ TEST_F(RtpSenderVideoTest, TEST_F(RtpSenderVideoTest, AbsoluteCaptureTime) { rtp_sender_video_ = std::make_unique( - &fake_clock_, rtp_module_.RtpSender(), env_.field_trials(), + &fake_clock_, rtp_module_->RtpSender(), env_.field_trials(), /*raw_packetization=*/false); constexpr Timestamp kAbsoluteCaptureTimestamp = Timestamp::Millis(12345678); uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), - kAbsoluteCaptureTimeExtensionId); + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), + kAbsoluteCaptureTimeExtensionId); RTPVideoHeader hdr; hdr.frame_type = VideoFrameType::kVideoFrameKey; @@ -1401,8 +1401,8 @@ TEST_F(RtpSenderVideoTest, AbsoluteCaptureTimeWithExtensionProvided) { .estimated_capture_clock_offset = std::optional(456), }; uint8_t kFrame[kMaxPacketLength]; - rtp_module_.RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), - kAbsoluteCaptureTimeExtensionId); + rtp_module_->RegisterRtpHeaderExtension(AbsoluteCaptureTimeExtension::Uri(), + kAbsoluteCaptureTimeExtensionId); RTPVideoHeader hdr; hdr.frame_type = VideoFrameType::kVideoFrameKey; @@ -1433,8 +1433,8 @@ TEST_F(RtpSenderVideoTest, PopulatesPlayoutDelay) { // Single packet frames. constexpr size_t kPacketSize = 123; uint8_t kFrame[kPacketSize]; - rtp_module_.RegisterRtpHeaderExtension(PlayoutDelayLimits::Uri(), - kPlayoutDelayExtensionId); + rtp_module_->RegisterRtpHeaderExtension(PlayoutDelayLimits::Uri(), + kPlayoutDelayExtensionId); const VideoPlayoutDelay kExpectedDelay(TimeDelta::Millis(10), TimeDelta::Millis(20)); @@ -1504,12 +1504,12 @@ TEST_F(RtpSenderVideoTest, SendGenericVideo) { kPayloadTypeGeneric, kCodecType, 1234, fake_clock_.CurrentTime(), kPayload, sizeof(kPayload), video_header, TimeDelta::PlusInfinity(), {})); - ArrayView sent_payload = + std::span sent_payload = transport_.last_sent_packet().payload(); uint8_t generic_header = sent_payload[0]; EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit); EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit); - EXPECT_THAT(sent_payload.subview(1), ElementsAreArray(kPayload)); + EXPECT_THAT(sent_payload.subspan(1), ElementsAreArray(kPayload)); // Send delta frame. const uint8_t kDeltaPayload[] = {13, 42, 32, 93, 13}; @@ -1523,7 +1523,7 @@ TEST_F(RtpSenderVideoTest, SendGenericVideo) { generic_header = sent_payload[0]; EXPECT_FALSE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit); EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit); - EXPECT_THAT(sent_payload.subview(1), ElementsAreArray(kDeltaPayload)); + EXPECT_THAT(sent_payload.subspan(1), ElementsAreArray(kDeltaPayload)); } class RtpSenderVideoRawPacketizationTest : public RtpSenderVideoTest { @@ -1543,7 +1543,7 @@ TEST_F(RtpSenderVideoRawPacketizationTest, SendRawVideo) { fake_clock_.CurrentTime(), kPayload, sizeof(kPayload), video_header, TimeDelta::PlusInfinity(), {})); - ArrayView sent_payload = + std::span sent_payload = transport_.last_sent_packet().payload(); EXPECT_THAT(sent_payload, ElementsAreArray(kPayload)); } @@ -1572,21 +1572,21 @@ class RtpSenderVideoWithFrameTransformerTest : public ::testing::Test { env_(CreateEnvironment(time_controller_.GetClock(), time_controller_.GetTaskQueueFactory())), retransmission_rate_limiter_(time_controller_.GetClock(), 1000), - rtp_module_( + rtp_module_(ModuleRtpRtcpImpl2::CreateSendModule( env_, {.outgoing_transport = &transport_, .retransmission_rate_limiter = &retransmission_rate_limiter_, .local_media_ssrc = kSsrc, - .rid = "myrid"}) { - rtp_module_.SetSequenceNumber(kSeqNum); - rtp_module_.SetStartTimestamp(0); + .rid = "myrid"})) { + rtp_module_->SetSequenceNumber(kSeqNum); + rtp_module_->SetStartTimestamp(0); } std::unique_ptr CreateSenderWithFrameTransformer( scoped_refptr transformer) { RTPSenderVideo::Config config; config.clock = time_controller_.GetClock(); - config.rtp_sender = rtp_module_.RtpSender(); + config.rtp_sender = rtp_module_->RtpSender(); config.field_trials = &env_.field_trials(); config.frame_transformer = transformer; config.task_queue_factory = time_controller_.GetTaskQueueFactory(); @@ -1598,7 +1598,7 @@ class RtpSenderVideoWithFrameTransformerTest : public ::testing::Test { const Environment env_; LoopbackTransportTest transport_; RateLimiter retransmission_rate_limiter_; - ModuleRtpRtcpImpl2 rtp_module_; + const std::unique_ptr rtp_module_; }; std::unique_ptr CreateDefaultEncodedImage() { @@ -1680,7 +1680,7 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, RTPVideoHeader video_header; video_header.frame_type = VideoFrameType::kVideoFrameKey; auto encoder_queue = time_controller_.GetTaskQueueFactory()->CreateTaskQueue( - "encoder_queue", TaskQueueFactory::Priority::NORMAL); + "encoder_queue", TaskQueueFactory::Priority::kNormal); encoder_queue->PostTask([&] { rtp_sender_video->SendEncodedImage( kPayloadType, kType, kTimestamp, *encoded_image, video_header, @@ -1738,7 +1738,7 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, OnTransformedFrameSendsVideo) { callback->OnTransformedFrame(std::move(frame)); }); auto encoder_queue = time_controller_.GetTaskQueueFactory()->CreateTaskQueue( - "encoder_queue", TaskQueueFactory::Priority::NORMAL); + "encoder_queue", TaskQueueFactory::Priority::kNormal); encoder_queue->PostTask([&] { rtp_sender_video->SendEncodedImage(kPayloadType, kType, kTimestamp, *encoded_image, video_header, @@ -1778,7 +1778,7 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, callback->OnTransformedFrame(std::move(frame)); }); auto encoder_queue = time_controller_.GetTaskQueueFactory()->CreateTaskQueue( - "encoder_queue", TaskQueueFactory::Priority::NORMAL); + "encoder_queue", TaskQueueFactory::Priority::kNormal); const int kFramesPerSecond = 25; for (int i = 0; i < kFramesPerSecond; ++i) { encoder_queue->PostTask([&] { @@ -1882,7 +1882,7 @@ TEST_F(RtpSenderVideoWithFrameTransformerTest, callback->OnTransformedFrame(std::move(clone)); }); auto encoder_queue = time_controller_.GetTaskQueueFactory()->CreateTaskQueue( - "encoder_queue", TaskQueueFactory::Priority::NORMAL); + "encoder_queue", TaskQueueFactory::Priority::kNormal); encoder_queue->PostTask([&] { rtp_sender_video->SendEncodedImage(kPayloadType, kType, kTimestamp, *encoded_image, video_header, diff --git a/modules/rtp_rtcp/source/rtp_util.cc b/modules/rtp_rtcp/source/rtp_util.cc index 4d802b6308e..0c62a31b6b0 100644 --- a/modules/rtp_rtcp/source/rtp_util.cc +++ b/modules/rtp_rtcp/source/rtp_util.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "rtc_base/checks.h" @@ -24,7 +24,7 @@ constexpr uint8_t kRtpVersion = 2; constexpr size_t kMinRtpPacketLen = 12; constexpr size_t kMinRtcpPacketLen = 4; -bool HasCorrectRtpVersion(ArrayView packet) { +bool HasCorrectRtpVersion(std::span packet) { return packet[0] >> 6 == kRtpVersion; } @@ -35,27 +35,27 @@ bool PayloadTypeIsReservedForRtcp(uint8_t payload_type) { } // namespace -bool IsRtpPacket(ArrayView packet) { +bool IsRtpPacket(std::span packet) { return packet.size() >= kMinRtpPacketLen && HasCorrectRtpVersion(packet) && !PayloadTypeIsReservedForRtcp(packet[1] & 0x7F); } -bool IsRtcpPacket(ArrayView packet) { +bool IsRtcpPacket(std::span packet) { return packet.size() >= kMinRtcpPacketLen && HasCorrectRtpVersion(packet) && PayloadTypeIsReservedForRtcp(packet[1] & 0x7F); } -int ParseRtpPayloadType(ArrayView rtp_packet) { +int ParseRtpPayloadType(std::span rtp_packet) { RTC_DCHECK(IsRtpPacket(rtp_packet)); return rtp_packet[1] & 0x7F; } -uint16_t ParseRtpSequenceNumber(ArrayView rtp_packet) { +uint16_t ParseRtpSequenceNumber(std::span rtp_packet) { RTC_DCHECK(IsRtpPacket(rtp_packet)); return ByteReader::ReadBigEndian(rtp_packet.data() + 2); } -uint32_t ParseRtpSsrc(ArrayView rtp_packet) { +uint32_t ParseRtpSsrc(std::span rtp_packet) { RTC_DCHECK(IsRtpPacket(rtp_packet)); return ByteReader::ReadBigEndian(rtp_packet.data() + 8); } diff --git a/modules/rtp_rtcp/source/rtp_util.h b/modules/rtp_rtcp/source/rtp_util.h index a183e0e3dfb..90de009add0 100644 --- a/modules/rtp_rtcp/source/rtp_util.h +++ b/modules/rtp_rtcp/source/rtp_util.h @@ -12,19 +12,18 @@ #define MODULES_RTP_RTCP_SOURCE_RTP_UTIL_H_ #include - -#include "api/array_view.h" +#include namespace webrtc { -bool IsRtcpPacket(ArrayView packet); -bool IsRtpPacket(ArrayView packet); +bool IsRtcpPacket(std::span packet); +bool IsRtpPacket(std::span packet); // Returns base rtp header fields of the rtp packet. // Behaviour is undefined when `!IsRtpPacket(rtp_packet)`. -int ParseRtpPayloadType(ArrayView rtp_packet); -uint16_t ParseRtpSequenceNumber(ArrayView rtp_packet); -uint32_t ParseRtpSsrc(ArrayView rtp_packet); +int ParseRtpPayloadType(std::span rtp_packet); +uint16_t ParseRtpSequenceNumber(std::span rtp_packet); +uint32_t ParseRtpSsrc(std::span rtp_packet); } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc index d50902023a0..ad91e35e5ca 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/units/data_rate.h" #include "api/video/video_layers_allocation.h" #include "modules/rtp_rtcp/source/byte_io.h" @@ -109,7 +109,7 @@ SpatialLayersBitmasks SpatialLayersBitmasksPerRtpStream( // for the description of the format. bool RtpVideoLayersAllocationExtension::Write( - ArrayView data, + std::span data, const VideoLayersAllocation& allocation) { RTC_DCHECK(AllocationIsValid(allocation)); RTC_DCHECK_GE(data.size(), ValueSize(allocation)); @@ -178,7 +178,7 @@ bool RtpVideoLayersAllocationExtension::Write( } bool RtpVideoLayersAllocationExtension::Parse( - ArrayView data, + std::span data, VideoLayersAllocation* allocation) { if (data.empty() || allocation == nullptr) { return false; diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h index 520a16371b2..d74a7cd539d 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h @@ -13,9 +13,9 @@ #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/rtp_parameters.h" #include "api/video/video_layers_allocation.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -30,10 +30,10 @@ class RtpVideoLayersAllocationExtension { return RtpExtension::kVideoLayersAllocationUri; } - static bool Parse(ArrayView data, + static bool Parse(std::span data, VideoLayersAllocation* allocation); static size_t ValueSize(const VideoLayersAllocation& allocation); - static bool Write(ArrayView data, + static bool Write(std::span data, const VideoLayersAllocation& allocation); }; diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc index 086a9e8bf4c..d80cc38ebee 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc @@ -10,7 +10,9 @@ #include "modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.h" +#include #include +#include #include "api/units/data_rate.h" #include "api/video/video_layers_allocation.h" @@ -22,10 +24,15 @@ namespace { TEST(RtpVideoLayersAllocationExtension, WriteEmptyLayersAllocationReturnsTrue) { VideoLayersAllocation written_allocation; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); } TEST(RtpVideoLayersAllocationExtension, @@ -35,11 +42,15 @@ TEST(RtpVideoLayersAllocationExtension, VideoLayersAllocation written_allocation; written_allocation.resolution_and_frame_rate_is_valid = true; written_allocation.rtp_stream_index = 0; - - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); VideoLayersAllocation parsed_allocation; EXPECT_TRUE( @@ -73,10 +84,15 @@ TEST(RtpVideoLayersAllocationExtension, /*frame_rate_fps*/ .frame_rate_fps = 0, }, }; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); VideoLayersAllocation parsed_allocation; EXPECT_TRUE( RtpVideoLayersAllocationExtension::Parse(buffer, &parsed_allocation)); @@ -110,10 +126,15 @@ TEST(RtpVideoLayersAllocationExtension, /*height*/ .height = 0, /*frame_rate_fps*/ .frame_rate_fps = 0}, }; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); VideoLayersAllocation parsed_allocation; EXPECT_TRUE( RtpVideoLayersAllocationExtension::Parse(buffer, &parsed_allocation)); @@ -140,10 +161,15 @@ TEST(RtpVideoLayersAllocationExtension, /*height*/ .height = 0, /*frame_rate_fps*/ .frame_rate_fps = 0}, }; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); VideoLayersAllocation parsed_allocation; EXPECT_TRUE( RtpVideoLayersAllocationExtension::Parse(buffer, &parsed_allocation)); @@ -170,10 +196,15 @@ TEST(RtpVideoLayersAllocationExtension, /*height*/ .height = 0, /*frame_rate_fps*/ .frame_rate_fps = 0}, }; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); VideoLayersAllocation parsed_allocation; EXPECT_TRUE( RtpVideoLayersAllocationExtension::Parse(buffer, &parsed_allocation)); @@ -205,10 +236,15 @@ TEST(RtpVideoLayersAllocationExtension, /*frame_rate_fps*/ .frame_rate_fps = 0, }, }; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); VideoLayersAllocation parsed_allocation; EXPECT_TRUE( RtpVideoLayersAllocationExtension::Parse(buffer, &parsed_allocation)); @@ -243,10 +279,15 @@ TEST(RtpVideoLayersAllocationExtension, }, }; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); VideoLayersAllocation parsed_allocation; EXPECT_TRUE( RtpVideoLayersAllocationExtension::Parse(buffer, &parsed_allocation)); @@ -257,10 +298,15 @@ TEST(RtpVideoLayersAllocationExtension, WriteEmptyAllocationCanHaveAnyRtpStreamIndex) { VideoLayersAllocation written_allocation; written_allocation.rtp_stream_index = 1; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + EXPECT_TRUE(RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation)); + return buffer_view.size(); + }); } TEST(RtpVideoLayersAllocationExtension, DiscardsOverLargeDataRate) { @@ -286,10 +332,18 @@ TEST(RtpVideoLayersAllocationExtension, DiscardsInvalidHeight) { /*frame_rate_fps*/ .frame_rate_fps = 8, }, }; - Buffer buffer = Buffer::CreateUninitializedWithSize( + Buffer buffer = Buffer::CreateWithCapacity( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - ASSERT_TRUE( - RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + size_t written = buffer.AppendData( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation), + [&](std::span buffer_view) { + bool result = RtpVideoLayersAllocationExtension::Write( + buffer_view, written_allocation); + EXPECT_TRUE(result); + return result ? buffer_view.size() : 0; + }); + ASSERT_EQ(written, + RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); // Modify the height to be invalid. buffer[buffer.size() - 3] = 0xff; diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc index 7b5f480aa12..9e7f09939ee 100644 --- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc +++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate.cc @@ -13,12 +13,12 @@ #include #include #include +#include #include #include #include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/rtp_packet_infos.h" #include "api/scoped_refptr.h" @@ -27,7 +27,6 @@ #include "api/units/timestamp.h" #include "api/video/encoded_image.h" #include "api/video/video_frame_metadata.h" -#include "api/video/video_frame_type.h" #include "api/video/video_timing.h" #include "api/video_codecs/video_codec.h" #include "modules/rtp_rtcp/source/frame_object.h" @@ -55,11 +54,11 @@ class TransformableVideoReceiverFrame ~TransformableVideoReceiverFrame() override = default; // Implements TransformableVideoFrameInterface. - ArrayView GetData() const override { + std::span GetData() const override { return *frame_->GetEncodedData(); } - void SetData(ArrayView data) override { + void SetData(std::span data) override { frame_->SetEncodedData( EncodedImageBuffer::Create(data.data(), data.size())); } @@ -71,9 +70,7 @@ class TransformableVideoReceiverFrame frame_->SetRtpTimestamp(timestamp); } - bool IsKeyFrame() const override { - return frame_->FrameType() == VideoFrameType::kVideoFrameKey; - } + bool IsKeyFrame() const override { return frame_->IsKey(); } VideoFrameMetadata Metadata() const override { return metadata_; } @@ -235,7 +232,7 @@ void RtpVideoStreamReceiverFrameTransformerDelegate::ManageFrame( VideoFrameMetadata metadata = transformed_frame->Metadata(); RTPVideoHeader video_header = RTPVideoHeader::FromMetadata(metadata); VideoSendTiming timing; - ArrayView data = transformed_frame->GetData(); + std::span data = transformed_frame->GetData(); int64_t receive_time = clock_->CurrentTime().ms(); receiver_->ManageFrame(std::make_unique( /*first_seq_num=*/metadata.GetFrameId().value_or(0), diff --git a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc index 2e4c1b6e08c..989beddcbae 100644 --- a/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_video_stream_receiver_frame_transformer_delegate_unittest.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/rtp_headers.h" @@ -342,7 +342,7 @@ TEST(RtpVideoStreamReceiverFrameTransformerDelegateTest, scoped_refptr buffer = EncodedImageBuffer::Create(1); ON_CALL(*mock_sender_frame, GetData) - .WillByDefault(Return(ArrayView(*buffer))); + .WillByDefault(Return(std::span(*buffer))); scoped_refptr callback; EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameSinkCallback) diff --git a/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc b/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc index 95f3a2a9f1e..b4202f2b6c0 100644 --- a/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc +++ b/modules/rtp_rtcp/source/ulpfec_header_reader_writer.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/forward_error_correction.h" @@ -111,7 +111,7 @@ size_t UlpfecHeaderWriter::FecHeaderSize(size_t packet_mask_size) const { } void UlpfecHeaderWriter::FinalizeFecHeader( - ArrayView protected_streams, + std::span protected_streams, ForwardErrorCorrection::Packet& fec_packet) const { RTC_CHECK_EQ(protected_streams.size(), 1); uint16_t seq_num_base = protected_streams[0].seq_num_base; diff --git a/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h b/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h index b08725eebf1..6c196fa5eb7 100644 --- a/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h +++ b/modules/rtp_rtcp/source/ulpfec_header_reader_writer.h @@ -14,7 +14,8 @@ #include #include -#include "api/array_view.h" +#include + #include "modules/rtp_rtcp/source/forward_error_correction.h" namespace webrtc { @@ -58,7 +59,7 @@ class UlpfecHeaderWriter : public FecHeaderWriter { size_t FecHeaderSize(size_t packet_mask_row_size) const override; void FinalizeFecHeader( - ArrayView protected_streams, + std::span protected_streams, ForwardErrorCorrection::Packet& fec_packet) const override; }; diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer.cc index d44e38f03d5..6afc4275376 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/video/encoded_image.h" #include "rtc_base/checks.h" @@ -22,9 +22,9 @@ namespace webrtc { scoped_refptr VideoRtpDepacketizer::AssembleFrame( - ArrayView> rtp_payloads) { + std::span> rtp_payloads) { size_t frame_size = 0; - for (ArrayView payload : rtp_payloads) { + for (std::span payload : rtp_payloads) { frame_size += payload.size(); } @@ -32,7 +32,7 @@ scoped_refptr VideoRtpDepacketizer::AssembleFrame( EncodedImageBuffer::Create(frame_size); uint8_t* write_at = bitstream->data(); - for (ArrayView payload : rtp_payloads) { + for (std::span payload : rtp_payloads) { memcpy(write_at, payload.data(), payload.size()); write_at += payload.size(); } diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer.h b/modules/rtp_rtcp/source/video_rtp_depacketizer.h index 487be38f626..2a051238adc 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer.h +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer.h @@ -14,8 +14,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/video/encoded_image.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" @@ -34,7 +34,7 @@ class VideoRtpDepacketizer { virtual std::optional Parse( CopyOnWriteBuffer rtp_payload) = 0; virtual scoped_refptr AssembleFrame( - ArrayView> rtp_payloads); + std::span> rtp_payloads); }; } // namespace webrtc diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc index ed7434e88db..625c98e9613 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/video/encoded_image.h" #include "api/video/video_codec_type.h" @@ -93,7 +93,7 @@ class ArrayOfArrayViews { } private: - using Storage = absl::InlinedVector, 2>; + using Storage = absl::InlinedVector, 2>; size_t size_ = 0; Storage data_; @@ -195,10 +195,10 @@ int RtpStartsNewCodedVideoSequence(uint8_t aggregation_header) { // fills ObuInfo::data field. // Returns empty vector on error. VectorObuInfo ParseObus( - ArrayView> rtp_payloads) { + std::span> rtp_payloads) { VectorObuInfo obu_infos; bool expect_continues_obu = false; - for (ArrayView rtp_payload : rtp_payloads) { + for (std::span rtp_payload : rtp_payloads) { ByteBufferReader payload(rtp_payload); uint8_t aggregation_header; if (!payload.ReadUInt8(&aggregation_header)) { @@ -337,7 +337,7 @@ bool CalculateObuSizes(ObuInfo* obu_info) { } // namespace scoped_refptr VideoRtpDepacketizerAv1::AssembleFrame( - ArrayView> rtp_payloads) { + std::span> rtp_payloads) { VectorObuInfo obu_infos = ParseObus(rtp_payloads); if (obu_infos.empty()) { return nullptr; diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h index 4acf209d9c2..446b819fef2 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h @@ -15,8 +15,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/video/encoded_image.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer.h" @@ -32,7 +32,7 @@ class VideoRtpDepacketizerAv1 : public VideoRtpDepacketizer { ~VideoRtpDepacketizerAv1() override = default; scoped_refptr AssembleFrame( - ArrayView> rtp_payloads) override; + std::span> rtp_payloads) override; std::optional Parse(CopyOnWriteBuffer rtp_payload) override; }; diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1_unittest.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1_unittest.cc index aa3a735fca4..17c7f481627 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_av1_unittest.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_av1_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer.h" #include "rtc_base/copy_on_write_buffer.h" @@ -125,10 +125,10 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameSetsOBUPayloadSizeWhenAbsent) { const uint8_t payload1[] = {0b00'01'0000, // aggregation header 0b0'0110'000, // / Frame 20, 30, 40}; // \ OBU - ArrayView payloads[] = {payload1}; + std::span payloads[] = {payload1}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - ArrayView frame_view(*frame); + std::span frame_view(*frame); EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); EXPECT_EQ(frame_view[1], 3); } @@ -140,10 +140,10 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameSetsOBUPayloadSizeWhenPresent) { 20, 30, 40}; // \ obu_payload - ArrayView payloads[] = {payload1}; + std::span payloads[] = {payload1}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - ArrayView frame_view(*frame); + std::span frame_view(*frame); EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); EXPECT_EQ(frame_view[1], 3); } @@ -154,10 +154,10 @@ TEST(VideoRtpDepacketizerAv1Test, 0b0'0110'100, // / Frame 0b010'01'000, // | extension_header 20, 30, 40}; // \ OBU - ArrayView payloads[] = {payload1}; + std::span payloads[] = {payload1}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - ArrayView frame_view(*frame); + std::span frame_view(*frame); EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); EXPECT_EQ(frame_view[2], 3); } @@ -171,10 +171,10 @@ TEST(VideoRtpDepacketizerAv1Test, 20, 30, 40}; // \ obu_payload - ArrayView payloads[] = {payload1}; + std::span payloads[] = {payload1}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - ArrayView frame_view(*frame); + std::span frame_view(*frame); EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); EXPECT_EQ(frame_view[2], 3); } @@ -183,10 +183,10 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromOnePacketWithOneObu) { const uint8_t payload1[] = {0b00'01'0000, // aggregation header 0b0'0110'000, // / Frame 20}; // \ OBU - ArrayView payloads[] = {payload1}; + std::span payloads[] = {payload1}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0110'010, 1, 20)); } @@ -197,10 +197,10 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromOnePacketWithTwoObus) { 10, // \ OBU 0b0'0110'000, // / Frame 20}; // \ OBU - ArrayView payloads[] = {payload1}; + std::span payloads[] = {payload1}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0001'010, 1, 10, // Sequence Header OBU 0b0'0110'010, 1, 20)); // Frame OBU } @@ -210,10 +210,10 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromTwoPacketsWithOneObu) { 0b0'0110'000, 20, 30}; const uint8_t payload2[] = {0b10'01'0000, // aggregation header 40}; - ArrayView payloads[] = {payload1, payload2}; + std::span payloads[] = {payload1, payload2}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0110'010, 3, 20, 30, 40)); } @@ -227,10 +227,10 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromTwoPacketsWithTwoObu) { 30}; // const uint8_t payload2[] = {0b10'01'0000, // aggregation header 40}; // - ArrayView payloads[] = {payload1, payload2}; + std::span payloads[] = {payload1, payload2}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0001'010, 1, 10, // SH 0b0'0110'010, 3, 20, 30, 40)); // Frame } @@ -258,10 +258,10 @@ TEST(VideoRtpDepacketizerAv1Test, const uint8_t payload2[] = {0b10'01'0000, // aggregation header 70, 80, 90}; // \ tail of the frame OBU - ArrayView payloads[] = {payload1, payload2}; + std::span payloads[] = {payload1, payload2}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre( // Sequence header OBU 0b0'0001'010, 1, 10, // Metadata OBU without extension @@ -282,11 +282,11 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameWithOneObuFromManyPackets) { const uint8_t payload4[] = {0b10'01'0000, // aggregation header 18}; - ArrayView payloads[] = {payload1, payload2, payload3, + std::span payloads[] = {payload1, payload2, payload3, payload4}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0110'010, 8, 11, 12, 13, 14, 15, 16, 17, 18)); } @@ -314,11 +314,11 @@ TEST(VideoRtpDepacketizerAv1Test, 32}; const uint8_t payload4[] = {0b10'01'0000, // aggregation header 33, 34, 35, 36}; - ArrayView payloads[] = {payload1, payload2, payload3, + std::span payloads[] = {payload1, payload2, payload3, payload4}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0011'010, 2, 11, 12, // Frame header 0b0'0100'010, 7, 21, 22, 23, 24, 25, 26, 27, // 0b0'0111'010, 2, 11, 12, // @@ -334,11 +334,11 @@ TEST(VideoRtpDepacketizerAv1Test, payload1[2] = 0x01; // in two bytes payload1[3] = 0b0'0110'000; // obu_header with size and extension bits unset. payload1[4 + 42] = 0x42; - ArrayView payloads[] = {payload1}; + std::span payloads[] = {payload1}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); EXPECT_EQ(frame->size(), 2 + 127u); - ArrayView frame_view(*frame); + std::span frame_view(*frame); EXPECT_EQ(frame_view[0], 0b0'0110'010); // obu_header with size bit set. EXPECT_EQ(frame_view[1], 127); // obu payload size, 1 byte enough to encode. // Check 'random' byte from the payload is at the same 'random' offset. @@ -359,11 +359,11 @@ TEST(VideoRtpDepacketizerAv1Test, payload2[1] = 96; // leb128 encoded size of 96 bytes in one byte payload2[2 + 20] = 0x20; - ArrayView payloads[] = {payload1, payload2}; + std::span payloads[] = {payload1, payload2}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); EXPECT_EQ(frame->size(), 3 + 128u); - ArrayView frame_view(*frame); + std::span frame_view(*frame); EXPECT_EQ(frame_view[0], 0b0'0110'010); // obu_header with size bit set. EXPECT_EQ(frame_view[1], 0x80); // obu payload size of 128 bytes. EXPECT_EQ(frame_view[2], 0x01); // encoded in two byes @@ -376,11 +376,11 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromAlmostEmptyPacketStartingAnOBU) { const uint8_t payload1[] = {0b01'01'0000}; const uint8_t payload2[] = {0b10'01'0000, 0b0'0110'000, 10, 20, 30}; - ArrayView payloads[] = {payload1, payload2}; + std::span payloads[] = {payload1, payload2}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0110'010, 3, 10, 20, 30)); } @@ -388,11 +388,11 @@ TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromAlmostEmptyPacketFinishingAnOBU) { const uint8_t payload1[] = {0b01'01'0000, 0b0'0110'000, 10, 20, 30}; const uint8_t payload2[] = {0b10'01'0000}; - ArrayView payloads[] = {payload1, payload2}; + std::span payloads[] = {payload1, payload2}; auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); ASSERT_TRUE(frame); - EXPECT_THAT(ArrayView(*frame), + EXPECT_THAT(std::span(*frame), ElementsAre(0b0'0110'010, 3, 10, 20, 30)); } diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc index 1c6529be806..4e5a005d2e7 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_h264.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "common_video/h264/h264_common.h" @@ -40,9 +40,9 @@ constexpr size_t kNalHeaderSize = 1; constexpr size_t kFuAHeaderSize = 2; constexpr size_t kLengthFieldSize = 2; -std::vector> ParseStapA( - ArrayView data) { - std::vector> nal_units; +std::vector> ParseStapA( + std::span data) { + std::vector> nal_units; ByteBufferReader reader(data); if (!reader.Consume(kNalHeaderSize)) { return nal_units; @@ -64,7 +64,7 @@ std::vector> ParseStapA( std::optional ProcessStapAOrSingleNalu( CopyOnWriteBuffer rtp_payload) { - ArrayView payload_data(rtp_payload); + std::span payload_data(rtp_payload); std::optional parsed_payload( std::in_place); bool modified_buffer = false; @@ -79,7 +79,7 @@ std::optional ProcessStapAOrSingleNalu( .emplace(); uint8_t nal_type = payload_data[0] & kH264TypeMask; - std::vector> nal_units; + std::vector> nal_units; if (nal_type == H264::NaluType::kStapA) { nal_units = ParseStapA(payload_data); if (nal_units.empty()) { @@ -96,12 +96,12 @@ std::optional ProcessStapAOrSingleNalu( parsed_payload->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - for (const ArrayView& nal_unit : nal_units) { + for (const std::span& nal_unit : nal_units) { NaluInfo nalu; nalu.type = nal_unit[0] & kH264TypeMask; nalu.sps_id = -1; nalu.pps_id = -1; - ArrayView nalu_data = nal_unit.subview(H264::kNaluTypeSize); + std::span nalu_data = nal_unit.subspan(H264::kNaluTypeSize); if (nalu_data.empty()) { RTC_LOG(LS_WARNING) << "Skipping empty NAL unit."; @@ -150,7 +150,7 @@ std::optional ProcessStapAOrSingleNalu( } // Append rest of packet. - output_buffer.AppendData(payload_data.subview(end_offset)); + output_buffer.AppendData(payload_data.subspan(end_offset)); modified_buffer = true; [[fallthrough]]; @@ -246,8 +246,8 @@ std::optional ParseFuaNalu( if (original_nal_type == H264::NaluType::kIdr || original_nal_type == H264::NaluType::kSlice) { std::optional slice_header = - PpsParser::ParseSliceHeader(ArrayView(rtp_payload) - .subview(2 * kNalHeaderSize)); + PpsParser::ParseSliceHeader(std::span(rtp_payload) + .subspan(2 * kNalHeaderSize)); if (slice_header) { nalu.pps_id = slice_header->pic_parameter_set_id; is_first_packet_in_frame = slice_header->first_mb_in_slice == 0; diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_h264_unittest.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_h264_unittest.cc index 5739ec019b2..b2cc10c3eea 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_h264_unittest.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_h264_unittest.cc @@ -12,9 +12,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "common_video/h264/h264_common.h" @@ -228,9 +228,9 @@ TEST(VideoRtpDepacketizerH264Test, DepacketizeWithRewriting) { VideoRtpDepacketizerH264 depacketizer; auto parsed = depacketizer.Parse(in_buffer); ASSERT_TRUE(parsed); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(out_buffer)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(out_buffer)); } TEST(VideoRtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) { @@ -273,9 +273,9 @@ TEST(VideoRtpDepacketizerH264Test, DepacketizeWithDoubleRewriting) { ASSERT_TRUE(parsed); std::vector expected_packet_payload( out_buffer.data(), &out_buffer.data()[out_buffer.size()]); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(out_buffer)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(out_buffer)); } TEST(VideoRtpDepacketizerH264Test, StapADelta) { @@ -343,9 +343,9 @@ TEST(VideoRtpDepacketizerH264Test, FuA) { ASSERT_TRUE(parsed1); // We expect that the first packet is one byte shorter since the FU-A header // has been replaced by the original nal header. - EXPECT_THAT(MakeArrayView(parsed1->video_payload.cdata(), - parsed1->video_payload.size()), - ElementsAreArray(kExpected1)); + EXPECT_THAT( + std::span(parsed1->video_payload.cdata(), parsed1->video_payload.size()), + ElementsAreArray(kExpected1)); EXPECT_EQ(parsed1->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_EQ(parsed1->video_header.codec, kVideoCodecH264); EXPECT_TRUE(parsed1->video_header.is_first_packet_in_frame); @@ -363,9 +363,9 @@ TEST(VideoRtpDepacketizerH264Test, FuA) { // Following packets will be 2 bytes shorter since they will only be appended // onto the first packet. auto parsed2 = depacketizer.Parse(CopyOnWriteBuffer(kPayload2)); - EXPECT_THAT(MakeArrayView(parsed2->video_payload.cdata(), - parsed2->video_payload.size()), - ElementsAreArray(kExpected2)); + EXPECT_THAT( + std::span(parsed2->video_payload.cdata(), parsed2->video_payload.size()), + ElementsAreArray(kExpected2)); EXPECT_FALSE(parsed2->video_header.is_first_packet_in_frame); EXPECT_EQ(parsed2->video_header.codec, kVideoCodecH264); { @@ -378,9 +378,9 @@ TEST(VideoRtpDepacketizerH264Test, FuA) { } auto parsed3 = depacketizer.Parse(CopyOnWriteBuffer(kPayload3)); - EXPECT_THAT(MakeArrayView(parsed3->video_payload.cdata(), - parsed3->video_payload.size()), - ElementsAreArray(kExpected3)); + EXPECT_THAT( + std::span(parsed3->video_payload.cdata(), parsed3->video_payload.size()), + ElementsAreArray(kExpected3)); EXPECT_FALSE(parsed3->video_header.is_first_packet_in_frame); EXPECT_EQ(parsed3->video_header.codec, kVideoCodecH264); { diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_h265.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_h265.cc index 4af35dc2c47..7babd6f2985 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_h265.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_h265.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/base/attributes.h" -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "common_video/h265/h265_bitstream_parser.h" @@ -118,7 +118,7 @@ std::optional ProcessApOrSingleNalu( uint8_t nalu_type = (payload_data[start_offset] & kH265TypeMask) >> 1; start_offset += kH265NalHeaderSizeBytes; - ArrayView nalu_data(&payload_data[start_offset], + std::span nalu_data(&payload_data[start_offset], end_offset - start_offset); switch (nalu_type) { case H265::NaluType::kBlaWLp: @@ -213,7 +213,7 @@ std::optional ParseFuNalu( kH265FuHeaderSizeBytes + kH265PayloadHeaderSizeBytes; std::optional first_slice_segment_in_pic_flag = H265BitstreamParser::IsFirstSliceSegmentInPic( - ArrayView(rtp_payload.cdata() + slice_offset, + std::span(rtp_payload.cdata() + slice_offset, rtp_payload.size() - slice_offset)); if (first_slice_segment_in_pic_flag.value_or(false)) { is_first_packet_in_frame = true; diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_h265_unittest.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_h265_unittest.cc index 0e21d001390..25ee05b7b22 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_h265_unittest.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_h265_unittest.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "common_video/h265/h265_common.h" @@ -39,9 +39,9 @@ TEST(VideoRtpDepacketizerH265Test, SingleNalu) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH265); EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); @@ -73,9 +73,9 @@ TEST(VideoRtpDepacketizerH265Test, SingleNaluSpsWithResolution) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH265); EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); EXPECT_EQ(parsed->video_header.width, 1280u); @@ -159,9 +159,9 @@ TEST(VideoRtpDepacketizerH265Test, ApKey) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH265); EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); @@ -218,9 +218,9 @@ TEST(VideoRtpDepacketizerH265Test, ApNaluSpsWithResolution) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH265); EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); @@ -267,9 +267,9 @@ TEST(VideoRtpDepacketizerH265Test, ApDelta) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta); EXPECT_EQ(parsed->video_header.codec, kVideoCodecH265); @@ -309,9 +309,9 @@ TEST(VideoRtpDepacketizerH265Test, Fu) { ASSERT_TRUE(parsed1); // We expect that the first packet is one byte shorter since the FU header // has been replaced by the original nal header. - EXPECT_THAT(MakeArrayView(parsed1->video_payload.cdata(), - parsed1->video_payload.size()), - ElementsAreArray(kExpected1)); + EXPECT_THAT( + std::span(parsed1->video_payload.cdata(), parsed1->video_payload.size()), + ElementsAreArray(kExpected1)); EXPECT_EQ(parsed1->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_EQ(parsed1->video_header.codec, kVideoCodecH265); EXPECT_TRUE(parsed1->video_header.is_first_packet_in_frame); @@ -319,17 +319,17 @@ TEST(VideoRtpDepacketizerH265Test, Fu) { // Following packets will be 2 bytes shorter since they will only be appended // onto the first packet. auto parsed2 = depacketizer.Parse(CopyOnWriteBuffer(packet2)); - EXPECT_THAT(MakeArrayView(parsed2->video_payload.cdata(), - parsed2->video_payload.size()), - ElementsAreArray(kExpected2)); + EXPECT_THAT( + std::span(parsed2->video_payload.cdata(), parsed2->video_payload.size()), + ElementsAreArray(kExpected2)); EXPECT_FALSE(parsed2->video_header.is_first_packet_in_frame); EXPECT_EQ(parsed2->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_EQ(parsed2->video_header.codec, kVideoCodecH265); auto parsed3 = depacketizer.Parse(CopyOnWriteBuffer(packet3)); - EXPECT_THAT(MakeArrayView(parsed3->video_payload.cdata(), - parsed3->video_payload.size()), - ElementsAreArray(kExpected3)); + EXPECT_THAT( + std::span(parsed3->video_payload.cdata(), parsed3->video_payload.size()), + ElementsAreArray(kExpected3)); EXPECT_FALSE(parsed3->video_header.is_first_packet_in_frame); EXPECT_EQ(parsed3->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_EQ(parsed3->video_header.codec, kVideoCodecH265); @@ -448,9 +448,9 @@ TEST(VideoRtpDepacketizerH265Test, ApVpsSpsPpsMultiIdrSlices) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed.has_value()); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameKey); EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); } @@ -484,9 +484,9 @@ TEST(VideoRtpDepacketizerH265Test, ApMultiNonFirstSlicesFromSingleNonIdrFrame) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed.has_value()); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta); EXPECT_FALSE(parsed->video_header.is_first_packet_in_frame); } @@ -520,9 +520,9 @@ TEST(VideoRtpDepacketizerH265Test, ApFirstTwoSlicesFromSingleNonIdrFrame) { depacketizer.Parse(rtp_payload); ASSERT_TRUE(parsed.has_value()); - EXPECT_THAT(MakeArrayView(parsed->video_payload.cdata(), - parsed->video_payload.size()), - ElementsAreArray(expected_packet)); + EXPECT_THAT( + std::span(parsed->video_payload.cdata(), parsed->video_payload.size()), + ElementsAreArray(expected_packet)); EXPECT_EQ(parsed->video_header.frame_type, VideoFrameType::kVideoFrameDelta); EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); } diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.cc index f3f4fb5ad33..9a792962cbe 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" @@ -139,7 +139,7 @@ int ParseVP8Descriptor(RTPVideoHeaderVP8* vp8, std::optional VideoRtpDepacketizerVp8::Parse(CopyOnWriteBuffer rtp_payload) { - ArrayView payload(rtp_payload.cdata(), rtp_payload.size()); + std::span payload(rtp_payload.cdata(), rtp_payload.size()); std::optional result(std::in_place); int offset = ParseRtpPayload(payload, &result->video_header); if (offset == kFailedToParse) @@ -151,7 +151,7 @@ VideoRtpDepacketizerVp8::Parse(CopyOnWriteBuffer rtp_payload) { } int VideoRtpDepacketizerVp8::ParseRtpPayload( - ArrayView rtp_payload, + std::span rtp_payload, RTPVideoHeader* video_header) { RTC_DCHECK(video_header); if (rtp_payload.empty()) { diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h index a01db9a04cc..663ffe67991 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer.h" #include "rtc_base/copy_on_write_buffer.h" @@ -30,7 +30,7 @@ class VideoRtpDepacketizerVp8 : public VideoRtpDepacketizer { // Parses vp8 rtp payload descriptor. // Returns zero on error or vp8 payload header offset on success. - static int ParseRtpPayload(ArrayView rtp_payload, + static int ParseRtpPayload(std::span rtp_payload, RTPVideoHeader* video_header); std::optional Parse(CopyOnWriteBuffer rtp_payload) override; diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8_unittest.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8_unittest.cc index 9fad3a1d2df..e92ee91f4dd 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8_unittest.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/rtp_format_vp8.h" @@ -244,7 +244,7 @@ TEST(VideoRtpDepacketizerVp8Test, ReferencesInputCopyOnWriteBuffer) { } TEST(VideoRtpDepacketizerVp8Test, FailsOnEmptyPayload) { - ArrayView empty; + std::span empty; RTPVideoHeader video_header; EXPECT_EQ(VideoRtpDepacketizerVp8::ParseRtpPayload(empty, &video_header), 0); } diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc index c69f48f952b..75ada1863bf 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/video/video_codec_constants.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" @@ -167,7 +167,7 @@ VideoRtpDepacketizerVp9::Parse(CopyOnWriteBuffer rtp_payload) { } int VideoRtpDepacketizerVp9::ParseRtpPayload( - ArrayView rtp_payload, + std::span rtp_payload, RTPVideoHeader* video_header) { RTC_DCHECK(video_header); // Parse mandatory first byte of payload descriptor. diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h index d5d45ec2c40..84169363789 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer.h" #include "rtc_base/copy_on_write_buffer.h" @@ -30,7 +30,7 @@ class VideoRtpDepacketizerVp9 : public VideoRtpDepacketizer { // Parses vp9 rtp payload descriptor. // Returns zero on error or vp9 payload header offset on success. - static int ParseRtpPayload(ArrayView rtp_payload, + static int ParseRtpPayload(std::span rtp_payload, RTPVideoHeader* video_header); std::optional Parse(CopyOnWriteBuffer rtp_payload) override; diff --git a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc index 91bd7f5143f..de7674c79fe 100644 --- a/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc +++ b/modules/rtp_rtcp/source/video_rtp_depacketizer_vp9_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer.h" @@ -322,7 +322,7 @@ TEST(VideoRtpDepacketizerVp9Test, ParseResolution) { } TEST(VideoRtpDepacketizerVp9Test, ParseFailsForNoPayloadLength) { - ArrayView empty; + std::span empty; RTPVideoHeader video_header; EXPECT_EQ(VideoRtpDepacketizerVp9::ParseRtpPayload(empty, &video_header), 0); diff --git a/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc b/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc index 21591b42ba7..e0278abaf9c 100644 --- a/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc +++ b/modules/rtp_rtcp/test/testFec/test_packet_masks_metrics.cc @@ -48,9 +48,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/include/module_fec_types.h" #include "modules/rtp_rtcp/source/forward_error_correction_internal.h" #include "modules/rtp_rtcp/test/testFec/average_residual_loss_xor_codes.h" @@ -433,7 +433,8 @@ class FecPacketMaskMetricsTest : public ::testing::Test { } } // Done with loop over total number of packets. RTC_DCHECK_LE(num_media_packets_lost, num_media_packets); - RTC_DCHECK_LE(num_packets_lost, tot_num_packets && num_packets_lost > 0); + RTC_DCHECK_GE(num_packets_lost, 0); + RTC_DCHECK_LE(num_packets_lost, tot_num_packets); double residual_loss = 0.0; // Only need to compute residual loss (number of recovered packets) for // configurations that have at least one media packet lost. @@ -730,7 +731,7 @@ class FecPacketMaskMetricsTest : public ::testing::Test { for (int num_fec_packets = 1; num_fec_packets <= num_media_packets; num_fec_packets++) { memset(packet_mask.get(), 0, num_media_packets * mask_bytes_fec_packet); - ArrayView mask = + std::span mask = mask_table.LookUp(num_media_packets, num_fec_packets); memcpy(packet_mask.get(), &mask[0], mask.size()); // Convert to bit mask. diff --git a/modules/third_party/portaudio/README.chromium b/modules/third_party/portaudio/README.chromium index d912d912e2b..d7d899c05d6 100644 --- a/modules/third_party/portaudio/README.chromium +++ b/modules/third_party/portaudio/README.chromium @@ -1,11 +1,13 @@ Name: Portaudio library for mac Short Name: portaudio -URL: https://github.com/PortAudio/portaudio/tree/master/src/common -Version: 9d8563100d841300f1689b186d131347ad43a0f6 +URL: https://github.com/PortAudio/portaudio +Version: N/A +Revision: 9d8563100d841300f1689b186d131347ad43a0f6 Date: 2022-04-12 License: MIT License File: LICENSE Security Critical: yes +Update Mechanism: Manual Shipped: yes Description: diff --git a/modules/video_capture/BUILD.gn b/modules/video_capture/BUILD.gn index 1c413490762..ef4c4ff8483 100644 --- a/modules/video_capture/BUILD.gn +++ b/modules/video_capture/BUILD.gn @@ -193,7 +193,7 @@ if (!build_with_chromium || is_linux || is_chromeos) { "../../api/video:video_rtp_headers", "../../common_video", "../../rtc_base:checks", - "../../rtc_base:gunit_helpers", + "../../rtc_base:macromagic", "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../system_wrappers", diff --git a/modules/video_capture/OWNERS b/modules/video_capture/OWNERS index 364d66d36f7..65306a0a1c0 100644 --- a/modules/video_capture/OWNERS +++ b/modules/video_capture/OWNERS @@ -1,4 +1,3 @@ ilnik@webrtc.org -mflodman@webrtc.org perkj@webrtc.org tkchin@webrtc.org diff --git a/modules/video_capture/device_info_impl.cc b/modules/video_capture/device_info_impl.cc index f96fda09038..5df4cb6a45c 100644 --- a/modules/video_capture/device_info_impl.cc +++ b/modules/video_capture/device_info_impl.cc @@ -156,6 +156,9 @@ int32_t DeviceInfoImpl::GetBestMatchedCapability( capability.videoType == VideoType::kYUY2 || capability.videoType == VideoType::kYV12 || capability.videoType == VideoType::kNV12)) { + bestWidth = capability.width; + bestHeight = capability.height; + bestFrameRate = capability.maxFPS; bestVideoType = capability.videoType; bestformatIndex = tmp; } @@ -163,7 +166,11 @@ int32_t DeviceInfoImpl::GetBestMatchedCapability( // camera for encoding if it is supported. if (capability.height == requested.height && capability.width == requested.width && - capability.maxFPS >= requested.maxFPS) { + capability.maxFPS >= requested.maxFPS && + capability.maxFPS != bestFrameRate) { + bestWidth = capability.width; + bestHeight = capability.height; + bestFrameRate = capability.maxFPS; bestVideoType = capability.videoType; bestformatIndex = tmp; } diff --git a/modules/video_capture/linux/pipewire_session.cc b/modules/video_capture/linux/pipewire_session.cc index 63f0c8c99d4..ba841a7f193 100644 --- a/modules/video_capture/linux/pipewire_session.cc +++ b/modules/video_capture/linux/pipewire_session.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include "absl/strings/string_view.h" #include "common_video/libyuv/include/webrtc_libyuv.h" @@ -75,7 +76,9 @@ VideoType PipeWireRawFormatToVideoType(uint32_t id) { void PipeWireNode::PipeWireNodeDeleter::operator()( PipeWireNode* node) const noexcept { spa_hook_remove(&node->node_listener_); + spa_hook_remove(&node->proxy_listener_); pw_proxy_destroy(node->proxy_); + pw_node_info_free(node->info_); } // static @@ -105,6 +108,13 @@ PipeWireNode::PipeWireNode(PipeWireSession* session, }; pw_node_add_listener(reinterpret_cast(proxy_), &node_listener_, &node_events, this); + + static const pw_proxy_events proxy_events{ + .version = PW_VERSION_PROXY_EVENTS, + .done = OnProxyDone, + }; + + pw_proxy_add_listener(proxy_, &proxy_listener_, &proxy_events, this); } // static @@ -112,6 +122,12 @@ RTC_NO_SANITIZE("cfi-icall") void PipeWireNode::OnNodeInfo(void* data, const pw_node_info* info) { PipeWireNode* that = static_cast(data); + that->info_ = pw_node_info_update(that->info_, info); + if (!that->info_) + return; + + info = that->info_; + if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS) { const char* vid_str; const char* pid_str; @@ -133,14 +149,20 @@ void PipeWireNode::OnNodeInfo(void* data, const pw_node_info* info) { if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) { for (uint32_t i = 0; i < info->n_params; i++) { + if (info->params[i].user == 0) + continue; + info->params[i].user = 0; + uint32_t id = info->params[i].id; if (id == SPA_PARAM_EnumFormat && info->params[i].flags & SPA_PARAM_INFO_READ) { + that->pending_capabilities_.clear(); pw_node_enum_params(reinterpret_cast(that->proxy_), 0, id, 0, UINT32_MAX, nullptr); + that->sync_seq_ = pw_proxy_sync(that->proxy_, that->sync_seq_); + that->session_->PipeWireSync(); break; } } - that->session_->PipeWireSync(); } } @@ -208,7 +230,18 @@ void PipeWireNode::OnNodeParam(void* data, << cap.width << "x" << cap.height << "@" << cap.maxFPS << ")"; - that->capabilities_.push_back(cap); + that->pending_capabilities_.push_back(cap); +} + +// static +void PipeWireNode::OnProxyDone(void* data, int seq) { + PipeWireNode* that = static_cast(data); + + if (seq != that->sync_seq_) + return; + + that->capabilities_ = std::move(that->pending_capabilities_); + that->pending_capabilities_.clear(); } // static diff --git a/modules/video_capture/linux/pipewire_session.h b/modules/video_capture/linux/pipewire_session.h index d192c09444b..f03ada75281 100644 --- a/modules/video_capture/linux/pipewire_session.h +++ b/modules/video_capture/linux/pipewire_session.h @@ -72,15 +72,20 @@ class PipeWireNode { uint32_t next, const spa_pod* param); static bool ParseFormat(const spa_pod* param, VideoCaptureCapability* cap); + static void OnProxyDone(void* data, int seq); struct pw_proxy* proxy_; struct spa_hook node_listener_; + struct spa_hook proxy_listener_; PipeWireSession* session_; uint32_t id_; std::string display_name_; std::string unique_id_; std::string model_id_; + struct pw_node_info* info_ = nullptr; + int sync_seq_ = 0; std::vector capabilities_; + std::vector pending_capabilities_; }; class CameraPortalNotifier : public CameraPortal::PortalNotifier { diff --git a/modules/video_capture/linux/video_capture_pipewire.cc b/modules/video_capture/linux/video_capture_pipewire.cc index 5ae2b6f565e..ca354980165 100644 --- a/modules/video_capture/linux/video_capture_pipewire.cc +++ b/modules/video_capture/linux/video_capture_pipewire.cc @@ -250,15 +250,20 @@ int32_t VideoCaptureModulePipeWire::StartCapture( return 0; } +RTC_NO_SANITIZE("cfi-icall") int32_t VideoCaptureModulePipeWire::StopCapture() { RTC_DCHECK_RUN_ON(&api_checker_); PipeWireThreadLoopLock thread_loop_lock(session_->pw_main_loop_); + // PipeWireSession is guarded by API checker so just make sure we do // race detection when the PipeWire loop is locked/stopped to not run // any callback at this point. RTC_CHECK_RUNS_SERIALIZED(&capture_checker_); if (stream_) { + // Removing the listener first guarantees no callbacks will fire after this + // point. + spa_hook_remove(&stream_listener_); pw_stream_destroy(stream_); stream_ = nullptr; } diff --git a/modules/video_capture/linux/video_capture_pipewire.h b/modules/video_capture/linux/video_capture_pipewire.h index e285573035f..9be497940a3 100644 --- a/modules/video_capture/linux/video_capture_pipewire.h +++ b/modules/video_capture/linux/video_capture_pipewire.h @@ -63,7 +63,7 @@ class VideoCaptureModulePipeWire : public VideoCaptureImpl { RTC_GUARDED_BY(capture_checker_); struct pw_stream* stream_ RTC_GUARDED_BY(capture_checker_) = nullptr; - struct spa_hook stream_listener_ RTC_GUARDED_BY(capture_checker_); + struct spa_hook stream_listener_ RTC_GUARDED_BY(api_checker_) = {}; }; } // namespace videocapturemodule } // namespace webrtc diff --git a/modules/video_capture/linux/video_capture_v4l2.cc b/modules/video_capture/linux/video_capture_v4l2.cc index 1cb83f82d57..3f836efc951 100644 --- a/modules/video_capture/linux/video_capture_v4l2.cc +++ b/modules/video_capture/linux/video_capture_v4l2.cc @@ -108,9 +108,10 @@ int32_t VideoCaptureModuleV4L2::Init(const char* deviceUniqueIdUTF8) { VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() { RTC_DCHECK_RUN_ON(&api_checker_); - RTC_CHECK_RUNS_SERIALIZED(&capture_checker_); StopCapture(); + + RTC_CHECK_RUNS_SERIALIZED(&capture_checker_); if (_deviceFd != -1) close(_deviceFd); } diff --git a/modules/video_capture/test/video_capture_unittest.cc b/modules/video_capture/test/video_capture_unittest.cc index 8998324b60a..01a47384fcd 100644 --- a/modules/video_capture/test/video_capture_unittest.cc +++ b/modules/video_capture/test/video_capture_unittest.cc @@ -12,10 +12,13 @@ #include #include +#include +#include #include #include #include #include +#include #include #include "api/scoped_refptr.h" @@ -24,10 +27,13 @@ #include "api/video/video_frame.h" #include "api/video/video_rotation.h" #include "api/video/video_sink_interface.h" +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "modules/video_capture/device_info_impl.h" #include "modules/video_capture/video_capture_defines.h" #include "modules/video_capture/video_capture_factory.h" #include "rtc_base/checks.h" #include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" #include "system_wrappers/include/clock.h" #include "test/frame_utils.h" #include "test/gmock.h" @@ -46,6 +52,66 @@ static const int kTestWidth = 352; static const int kTestFramerate = 30; #endif +class DeviceInfoImplForTest final : public videocapturemodule::DeviceInfoImpl { + public: + explicit DeviceInfoImplForTest(VideoCaptureCapabilities capabilities) + : capabilities_(std::move(capabilities)) {} + + uint32_t NumberOfDevices() override { return 1; } + + int32_t GetDeviceName(uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* /* productUniqueIdUTF8 */, + uint32_t /* productUniqueIdUTF8Length */) override { + if (deviceNumber != 0) + return -1; + const char kName[] = "Fake Device"; + const char kId[] = "fake"; + if (deviceNameLength < sizeof(kName) || + deviceUniqueIdUTF8Length < sizeof(kId)) { + return -1; + } + snprintf(deviceNameUTF8, deviceNameLength, "%s", kName); + snprintf(deviceUniqueIdUTF8, deviceUniqueIdUTF8Length, "%s", kId); + return 0; + } + + int32_t DisplayCaptureSettingsDialogBox(const char* /* deviceUniqueIdUTF8 */, + const char* /* dialogTitle */, + void* /* parentWindow */, + uint32_t /* positionX */, + uint32_t /* positionY */) override { + return -1; + } + + protected: + int32_t Init() override { return 0; } + + int32_t CreateCapabilityMap(const char* deviceUniqueIdUTF8) override + RTC_EXCLUSIVE_LOCKS_REQUIRED(_apiLock) { + _captureCapabilities = capabilities_; + UpdateDeviceName(deviceUniqueIdUTF8); + return static_cast(_captureCapabilities.size()); + } + + private: + void UpdateDeviceName(const char* deviceUniqueIdUTF8) + RTC_EXCLUSIVE_LOCKS_REQUIRED(_apiLock) { + free(_lastUsedDeviceName); + _lastUsedDeviceNameLength = + static_cast(strlen(deviceUniqueIdUTF8)); + _lastUsedDeviceName = + static_cast(malloc(_lastUsedDeviceNameLength + 1)); + memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, + _lastUsedDeviceNameLength + 1); + } + + VideoCaptureCapabilities capabilities_; +}; + class TestVideoCaptureCallback : public VideoSinkInterface { public: explicit TestVideoCaptureCallback(Clock& clock) @@ -182,6 +248,46 @@ class VideoCaptureTest : public ::testing::Test { unsigned int number_of_devices_; }; +TEST(DeviceInfoImplTest, PrefersRequestedFormatOverSameFpsExactMatch) { + VideoCaptureCapability requested; + requested.width = 640; + requested.height = 480; + requested.maxFPS = 30; + requested.videoType = VideoType::kI420; + + VideoCaptureCapability cap0; + cap0.width = 640; + cap0.height = 480; + cap0.maxFPS = 60; + cap0.videoType = VideoType::kYUY2; + + VideoCaptureCapability cap1; + cap1.width = 640; + cap1.height = 480; + cap1.maxFPS = 30; + cap1.videoType = VideoType::kI420; + + VideoCaptureCapability cap2; + cap2.width = 640; + cap2.height = 480; + cap2.maxFPS = 30; + cap2.videoType = VideoType::kNV12; + + DeviceInfoImplForTest device_info({cap0, cap1, cap2}); + VideoCaptureCapability resulting; + + // Keep the preferred format when a later capability has the same size/fps. + // Without this, a later same-fps match can override the preferred format. + int32_t index = + device_info.GetBestMatchedCapability("fake", requested, resulting); + + EXPECT_EQ(index, 1); + EXPECT_EQ(resulting.width, requested.width); + EXPECT_EQ(resulting.height, requested.height); + EXPECT_EQ(resulting.maxFPS, requested.maxFPS); + EXPECT_EQ(resulting.videoType, VideoType::kI420); +} + #ifdef WEBRTC_MAC // Currently fails on Mac 64-bit, see // https://bugs.chromium.org/p/webrtc/issues/detail?id=5406 diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index e1f7d9e7a4e..74c0115bb60 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -23,12 +23,8 @@ rtc_library("encoded_frame") { "../../api/video:video_frame", "../../api/video:video_frame_type", "../../api/video:video_rtp_headers", - "../../modules:module_api_public", - "../../modules/rtp_rtcp:rtp_video_header", - "../../rtc_base:checks", - "../../rtc_base/experiments:alr_experiment", "../../rtc_base/system:rtc_export", - "../../system_wrappers", + "../rtp_rtcp:rtp_video_header", ] } @@ -39,7 +35,6 @@ rtc_library("chain_diff_calculator") { ] deps = [ - "../../rtc_base:checks", "../../rtc_base:logging", "//third_party/abseil-cpp/absl/container:inlined_vector", ] @@ -52,7 +47,6 @@ rtc_library("frame_dependencies_calculator") { ] deps = [ - "../../api:array_view", "../../common_video/generic_frame_descriptor", "../../rtc_base:checks", "../../rtc_base:logging", @@ -82,7 +76,6 @@ rtc_library("nack_requester") { "../../rtc_base:macromagic", "../../rtc_base:mod_ops", "../../rtc_base:rtc_numerics", - "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/system:no_unique_address", "../../rtc_base/task_utils:repeating_task", "../../system_wrappers", @@ -96,10 +89,6 @@ rtc_library("packet_buffer") { ] deps = [ ":codec_globals_headers", - "../../api:array_view", - "../../api:rtp_packet_info", - "../../api/units:timestamp", - "../../api/video:encoded_image", "../../api/video:video_frame", "../../api/video:video_frame_type", "../../common_video", @@ -123,10 +112,6 @@ rtc_library("h26x_packet_buffer") { ":codec_globals_headers", ":h264_sprop_parameter_sets", ":packet_buffer", - "../../api:array_view", - "../../api:rtp_packet_info", - "../../api/units:timestamp", - "../../api/video:encoded_image", "../../api/video:video_frame", "../../api/video:video_frame_type", "../../common_video", @@ -134,7 +119,6 @@ rtc_library("h26x_packet_buffer") { "../../rtc_base:copy_on_write_buffer", "../../rtc_base:logging", "../../rtc_base:rtc_numerics", - "../rtp_rtcp:rtp_rtcp_format", "../rtp_rtcp:rtp_video_header", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:core_headers", @@ -206,8 +190,6 @@ rtc_library("video_coding") { deps = [ ":codec_globals_headers", ":encoded_frame", - ":frame_helpers", - ":h264_sprop_parameter_sets", ":video_codec_interface", ":video_coding_utility", ":webrtc_vp8_scalability", @@ -215,39 +197,27 @@ rtc_library("video_coding") { "..:module_api", "..:module_api_public", "..:module_fec_api", - "../../api:array_view", "../../api:fec_controller_api", "../../api:field_trials_view", - "../../api:rtp_headers", "../../api:rtp_packet_info", "../../api:scoped_refptr", "../../api:sequence_checker", "../../api/environment", - "../../api/task_queue", "../../api/units:data_rate", - "../../api/units:data_size", - "../../api/units:frequency", "../../api/units:time_delta", "../../api/units:timestamp", - "../../api/video:builtin_video_bitrate_allocator_factory", "../../api/video:encoded_frame", "../../api/video:encoded_image", "../../api/video:render_resolution", - "../../api/video:video_adaptation", - "../../api/video:video_bitrate_allocation", - "../../api/video:video_bitrate_allocator", - "../../api/video:video_bitrate_allocator_factory", "../../api/video:video_codec_constants", "../../api/video:video_frame", "../../api/video:video_frame_type", "../../api/video:video_rtp_headers", "../../api/video/corruption_detection:frame_instrumentation_data", - "../../api/video/corruption_detection:frame_instrumentation_evaluation", "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", "../../common_video", "../../common_video:corruption_score_calculator", - "../../rtc_base:base64", "../../rtc_base:buffer", "../../rtc_base:byte_buffer", "../../rtc_base:checks", @@ -256,30 +226,19 @@ rtc_library("video_coding") { "../../rtc_base:logging", "../../rtc_base:macromagic", "../../rtc_base:mod_ops", - "../../rtc_base:rtc_event", "../../rtc_base:rtc_numerics", "../../rtc_base:safe_conversions", - "../../rtc_base:stringutils", - "../../rtc_base:threading", - "../../rtc_base:timeutils", - "../../rtc_base/experiments:alr_experiment", - "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/experiments:min_video_bitrate_experiment", "../../rtc_base/experiments:rate_control_settings", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:no_unique_address", - "../../rtc_base/task_utils:repeating_task", "../../system_wrappers", "../../system_wrappers:metrics", "../../video/config:encoder_config", "../rtp_rtcp", - "../rtp_rtcp:rtp_rtcp_format", "../rtp_rtcp:rtp_video_header", "codecs/av1:av1_svc_config", "svc:scalability_mode_util", - "timing:inter_frame_delay_variation_calculator", - "timing:jitter_estimator", - "timing:rtt_filter", "timing:timing_module", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/container:inlined_vector", @@ -307,68 +266,11 @@ rtc_library("video_codec_interface") { "../../api/video/corruption_detection:frame_instrumentation_data", "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", - "../../common_video", "../../common_video/generic_frame_descriptor", - "../../rtc_base:checks", "../../rtc_base/system:rtc_export", ] } -rtc_library("video_coding_legacy") { - visibility = [ ":video_coding_unittests" ] - sources = [ - "include/video_coding.h", - "video_coding_impl.cc", - "video_coding_impl.h", - "video_receiver.cc", - ] - deps = [ - ":codec_globals_headers", - ":encoded_frame", - ":video_codec_interface", - ":video_coding", - "..:module_api", - "..:module_api_public", - "../../api:field_trials_view", - "../../api:rtp_headers", - "../../api:rtp_packet_info", - "../../api:sequence_checker", - "../../api/environment", - "../../api/units:timestamp", - "../../api/video:encoded_image", - "../../api/video:render_resolution", - "../../api/video:video_frame", - "../../api/video:video_frame_type", - "../../api/video:video_rtp_headers", - "../../api/video_codecs:video_codecs_api", - "../../common_video", - "../../common_video:corruption_score_calculator", - "../../modules/rtp_rtcp:rtp_video_header", - "../../rtc_base:checks", - "../../rtc_base:event_tracer", - "../../rtc_base:logging", - "../../rtc_base:macromagic", - "../../rtc_base:one_time_event", - "../../rtc_base:rtc_event", - "../../rtc_base:safe_conversions", - "../../rtc_base/synchronization:mutex", - "../../system_wrappers", - "../rtp_rtcp:rtp_rtcp_format", - "../rtp_rtcp:rtp_video_header", - "deprecated:deprecated_decoding_state", - "deprecated:deprecated_event_wrapper", - "deprecated:deprecated_frame_buffer", - "deprecated:deprecated_jitter_buffer", - "deprecated:deprecated_jitter_buffer_common", - "deprecated:deprecated_packet", - "deprecated:deprecated_receiver", - "deprecated:deprecated_session_info", - "timing:inter_frame_delay_variation_calculator", - "timing:jitter_estimator", - "timing:timing_module", - ] -} - rtc_source_set("codec_globals_headers") { visibility = [ "*" ] sources = [ @@ -397,7 +299,6 @@ rtc_library("frame_sampler") { "..:module_api_public", "../../api/units:time_delta", "../../api/video:video_frame", - "../../rtc_base:timeutils", ] } @@ -434,8 +335,6 @@ rtc_library("video_coding_utility") { ] deps = [ - ":video_codec_interface", - "../../api:array_view", "../../api:field_trials_view", "../../api:scoped_refptr", "../../api:sequence_checker", @@ -443,39 +342,30 @@ rtc_library("video_coding_utility") { "../../api/task_queue", "../../api/units:data_rate", "../../api/units:time_delta", - "../../api/video:encoded_frame", "../../api/video:encoded_image", - "../../api/video:video_adaptation", "../../api/video:video_bitrate_allocation", "../../api/video:video_bitrate_allocator", "../../api/video:video_codec_constants", "../../api/video:video_frame", "../../api/video:video_frame_type", - "../../api/video/corruption_detection:corruption_detection_settings_generator", - "../../api/video/corruption_detection:filter_settings", "../../api/video_codecs:video_codecs_api", "../../common_video", - "../../modules/rtp_rtcp", "../../rtc_base:bitstream_reader", "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:macromagic", "../../rtc_base:rate_statistics", - "../../rtc_base:refcount", "../../rtc_base:rtc_numerics", "../../rtc_base:stringutils", - "../../rtc_base:timeutils", "../../rtc_base:weak_ptr", "../../rtc_base/experiments:encoder_info_settings", "../../rtc_base/experiments:quality_scaler_settings", "../../rtc_base/experiments:quality_scaling_experiment", "../../rtc_base/experiments:rate_control_settings", "../../rtc_base/synchronization:mutex", - "../../rtc_base/system:arch", "../../rtc_base/system:file_wrapper", "../../rtc_base/system:no_unique_address", "../../rtc_base/system:rtc_export", - "../../rtc_base/task_utils:repeating_task", "../../video/config:encoder_config", "../rtp_rtcp:rtp_rtcp_format", "svc:scalability_mode_util", @@ -491,7 +381,6 @@ rtc_library("encoder_speed_controller_impl") { "utility/encoder_speed_controller_impl.h", ] deps = [ - "../../api:array_view", "../../api/units:time_delta", "../../api/units:timestamp", "../../api/video_codecs:video_codecs_api", @@ -521,7 +410,6 @@ rtc_library("webrtc_h264") { ":video_coding_utility", "../../api:scoped_refptr", "../../api/environment", - "../../api/transport/rtp:dependency_descriptor", "../../api/units:data_rate", "../../api/video:encoded_image", "../../api/video:render_resolution", @@ -536,15 +424,12 @@ rtc_library("webrtc_h264") { "../../api/video_codecs:video_codecs_api", "../../common_video", "../../media:media_constants", - "../../media:rtc_media_base", "../../rtc_base:checks", "../../rtc_base:event_tracer", "../../rtc_base:logging", - "../../rtc_base:timeutils", "../../rtc_base/experiments:psnr_experiment", "../../rtc_base/system:rtc_export", "../../system_wrappers:metrics", - "../rtp_rtcp:rtp_rtcp_format", "svc:scalability_structures", "svc:scalable_video_controller", "//third_party/abseil-cpp/absl/base:nullability", @@ -611,7 +496,6 @@ rtc_library("webrtc_vp8") { ":video_coding_utility", ":webrtc_libvpx_interface", ":webrtc_vp8_scalability", - ":webrtc_vp8_temporal_layers", "../../api:fec_controller_api", "../../api:field_trials_view", "../../api:scoped_refptr", @@ -643,7 +527,6 @@ rtc_library("webrtc_vp8") { "../../rtc_base/experiments:rate_control_settings", "../../system_wrappers:metrics", "../rtp_rtcp:rtp_rtcp_format", - "svc:scalability_mode_util", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/container:inlined_vector", @@ -677,8 +560,6 @@ rtc_library("webrtc_vp8_temporal_layers") { deps = [ ":codec_globals_headers", ":video_codec_interface", - ":video_coding_utility", - "../../api:fec_controller_api", "../../api/environment", "../../api/transport/rtp:dependency_descriptor", "../../api/video:video_codec_constants", @@ -703,15 +584,9 @@ rtc_library("webrtc_vp9_helpers") { deps = [ ":codec_globals_headers", - ":video_codec_interface", - "../../api/video:video_bitrate_allocation", - "../../api/video:video_bitrate_allocator", - "../../api/video:video_codec_constants", "../../api/video:video_frame", "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", - "../../common_video", - "../../media:video_common", "../../rtc_base:checks", "../../rtc_base:logging", "svc:scalability_mode_util", @@ -740,7 +615,6 @@ rtc_library("webrtc_vp9") { ":video_codec_interface", ":video_coding_utility", ":webrtc_libvpx_interface", - "../../api:array_view", "../../api:fec_controller_api", "../../api:field_trials_view", "../../api:refcountedbase", @@ -840,7 +714,6 @@ if (rtc_include_tests) { ":video_codec_interface", "../../api:create_frame_generator", "../../api:frame_generator_api", - "../../api/transport/rtp:dependency_descriptor", "../../api/units:timestamp", "../../api/video:encoded_image", "../../api/video:render_resolution", @@ -860,7 +733,6 @@ if (rtc_include_tests) { deps = [ ":video_codec_interface", - ":video_coding", ":video_coding_utility", "../../api:mock_video_decoder", "../../api:mock_video_encoder", @@ -874,7 +746,6 @@ if (rtc_include_tests) { "../../api/video:video_frame_type", "../../api/video:video_rtp_headers", "../../api/video_codecs:video_codecs_api", - "../../common_video", "../../rtc_base:checks", "../../test:test_support", ] @@ -891,12 +762,10 @@ if (rtc_include_tests) { deps = [ ":codec_globals_headers", + ":simulcast_test_fixture_impl", ":video_codec_interface", - ":video_coding", ":video_coding_utility", ":videocodec_test_stats_impl", - ":webrtc_vp9_helpers", - "..:module_api", "../../api:create_frame_generator", "../../api:field_trials", "../../api:frame_generator_api", @@ -906,24 +775,18 @@ if (rtc_include_tests) { "../../api:videocodec_test_stats_api", "../../api/environment", "../../api/environment:environment_factory", - "../../api/numerics", "../../api/task_queue", - "../../api/task_queue:default_task_queue_factory", - "../../api/test/metrics:global_metrics_logger_and_exporter", "../../api/units:time_delta", "../../api/video:builtin_video_bitrate_allocator_factory", "../../api/video:encoded_image", "../../api/video:resolution", - "../../api/video:video_bitrate_allocation", "../../api/video:video_bitrate_allocator", "../../api/video:video_bitrate_allocator_factory", - "../../api/video:video_codec_constants", "../../api/video:video_frame", "../../api/video:video_frame_type", "../../api/video:video_rtp_headers", "../../api/video_codecs:video_codecs_api", "../../common_video", - "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:macromagic", "../../rtc_base:rtc_event", @@ -982,12 +845,10 @@ if (rtc_include_tests) { ] deps = [ ":codec_globals_headers", - ":video_codec_interface", ":video_codecs_test_framework", ":video_coding_utility", ":videocodec_test_stats_impl", ":webrtc_vp9_helpers", - "../../api:array_view", "../../api:field_trials_view", "../../api:make_ref_counted", "../../api:rtp_parameters", @@ -997,13 +858,10 @@ if (rtc_include_tests) { "../../api/environment:environment_factory", "../../api/test/metrics:global_metrics_logger_and_exporter", "../../api/test/metrics:metric", - "../../api/test/video:function_video_factory", "../../api/video:encoded_image", "../../api/video:resolution", - "../../api/video:video_bitrate_allocation", "../../api/video:video_codec_constants", "../../api/video:video_frame", - "../../api/video:video_frame_type", "../../api/video_codecs:video_codecs_api", "../../api/video_codecs:video_decoder_factory_template", "../../api/video_codecs:video_decoder_factory_template_dav1d_adapter", @@ -1017,7 +875,6 @@ if (rtc_include_tests) { "../../api/video_codecs:video_encoder_factory_template_open_h264_adapter", "../../common_video", "../../media:media_constants", - "../../media:rtc_audio_video", "../../rtc_base:checks", "../../rtc_base:cpu_info", "../../rtc_base:logging", @@ -1026,7 +883,6 @@ if (rtc_include_tests) { "../../rtc_base:task_queue_for_test", "../../rtc_base:threading", "../../rtc_base:timeutils", - "../../rtc_base/system:file_wrapper", "../../test:create_test_field_trials", "../../test:fileutils", "../../test:test_support", @@ -1048,16 +904,11 @@ if (rtc_include_tests) { ] deps = [ "../../api:videocodec_test_stats_api", - "../../api/numerics", - "../../api/test/metrics:global_metrics_logger_and_exporter", - "../../api/test/metrics:metric", "../../api/units:data_rate", "../../api/units:frequency", "../../api/video:video_frame_type", "../../rtc_base:checks", "../../rtc_base:rtc_numerics", - "../../rtc_base:stringutils", - "../../test:test_common", "../rtp_rtcp:rtp_rtcp_format", ] } @@ -1068,8 +919,6 @@ if (rtc_include_tests) { sources = [ "codecs/test/video_codec_test.cc" ] deps = [ - ":video_codec_interface", - "../../api:field_trials", "../../api/environment", "../../api/environment:environment_factory", "../../api/test/metrics:global_metrics_logger_and_exporter", @@ -1080,7 +929,6 @@ if (rtc_include_tests) { "../../api/video_codecs:builtin_video_encoder_factory", "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", - "../../modules/video_coding/svc:scalability_mode_util", "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:stringutils", @@ -1089,6 +937,7 @@ if (rtc_include_tests) { "../../test:test_flags", "../../test:test_support", "../../test:video_codec_tester", + "svc:scalability_mode_util", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/functional:any_invocable", ] @@ -1138,12 +987,11 @@ if (rtc_include_tests) { ":webrtc_vp8", ":webrtc_vp9", ":webrtc_vp9_helpers", - "../../api:array_view", "../../api:create_frame_generator", "../../api:create_videocodec_test_fixture_api", "../../api:field_trials", "../../api:frame_generator_api", - "../../api:mock_video_codec_factory", + "../../api:make_ref_counted", "../../api:mock_video_decoder", "../../api:mock_video_encoder", "../../api:scoped_refptr", @@ -1151,7 +999,6 @@ if (rtc_include_tests) { "../../api:videocodec_test_stats_api", "../../api/environment", "../../api/environment:environment_factory", - "../../api/test/metrics:global_metrics_logger_and_exporter", "../../api/test/video:function_video_factory", "../../api/units:data_rate", "../../api/units:time_delta", @@ -1163,25 +1010,19 @@ if (rtc_include_tests) { "../../api/video:video_frame", "../../api/video:video_frame_type", "../../api/video:video_rtp_headers", - "../../api/video_codecs:rtc_software_fallback_wrappers", "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", "../../common_video", - "../../common_video/test:utilities", - "../../media:codec", "../../media:media_constants", "../../media:rtc_internal_video_codecs", "../../media:rtc_simulcast_encoder_adapter", "../../rtc_base:checks", - "../../rtc_base:refcount", - "../../rtc_base:stringutils", "../../rtc_base:timeutils", "../../test:create_test_field_trials", "../../test:fileutils", "../../test:test_support", "../../test:video_test_common", "../rtp_rtcp:rtp_rtcp_format", - "codecs/av1:dav1d_decoder", "svc:scalability_mode_util", "//third_party/abseil-cpp/absl/container:inlined_vector", "//third_party/abseil-cpp/absl/memory", @@ -1254,7 +1095,6 @@ if (rtc_include_tests) { "utility/vp9_uncompressed_header_parser_unittest.cc", "video_codec_initializer_unittest.cc", "video_receiver2_unittest.cc", - "video_receiver_unittest.cc", ] if (rtc_use_h264) { sources += [ @@ -1266,40 +1106,34 @@ if (rtc_include_tests) { deps = [ ":chain_diff_calculator", ":codec_globals_headers", - ":encoded_frame", - ":encoder_speed_controller_impl", ":frame_dependencies_calculator", ":frame_helpers", - ":frame_sampler", ":h264_sprop_parameter_sets", ":h26x_packet_buffer", ":nack_requester", ":packet_buffer", - ":simulcast_test_fixture_impl", ":video_codec_interface", ":video_codecs_test_framework", ":video_coding", - ":video_coding_legacy", ":video_coding_utility", ":videocodec_test_impl", ":videocodec_test_stats_impl", ":webrtc_h264", ":webrtc_vp8", ":webrtc_vp8_temporal_layers", - ":webrtc_vp9", ":webrtc_vp9_helpers", "..:module_api", "..:module_fec_api", - "../../api:array_view", + "../../api:create_frame_generator", "../../api:create_simulcast_test_fixture_api", "../../api:fec_controller_api", "../../api:field_trials", "../../api:field_trials_view", + "../../api:frame_generator_api", "../../api:make_ref_counted", "../../api:mock_fec_controller_override", "../../api:mock_video_decoder", "../../api:mock_video_encoder", - "../../api:rtp_headers", "../../api:rtp_packet_info", "../../api:scoped_refptr", "../../api:simulcast_test_fixture_api", @@ -1307,20 +1141,15 @@ if (rtc_include_tests) { "../../api/environment", "../../api/environment:environment_factory", "../../api/task_queue", - "../../api/task_queue:default_task_queue_factory", "../../api/test/video:function_video_factory", "../../api/transport/rtp:dependency_descriptor", "../../api/units:data_rate", - "../../api/units:data_size", - "../../api/units:frequency", "../../api/units:time_delta", "../../api/units:timestamp", "../../api/video:builtin_video_bitrate_allocator_factory", "../../api/video:encoded_frame", "../../api/video:encoded_image", - "../../api/video:frame_buffer", "../../api/video:render_resolution", - "../../api/video:video_adaptation", "../../api/video:video_bitrate_allocation", "../../api/video:video_bitrate_allocator", "../../api/video:video_bitrate_allocator_factory", @@ -1329,7 +1158,6 @@ if (rtc_include_tests) { "../../api/video:video_frame_type", "../../api/video:video_rtp_headers", "../../api/video/corruption_detection:corruption_detection_unittests", - "../../api/video/corruption_detection:filter_settings", "../../api/video/corruption_detection:frame_instrumentation_data", "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", @@ -1339,30 +1167,23 @@ if (rtc_include_tests) { "../../common_video/generic_frame_descriptor", "../../common_video/test:utilities", "../../media:media_constants", - "../../media:rtc_internal_video_codecs", "../../rtc_base:checks", "../../rtc_base:copy_on_write_buffer", - "../../rtc_base:gunit_helpers", - "../../rtc_base:histogram_percentile_counter", - "../../rtc_base:platform_thread", "../../rtc_base:random", - "../../rtc_base:refcount", "../../rtc_base:rtc_base_tests_utils", "../../rtc_base:rtc_event", "../../rtc_base:rtc_numerics", + "../../rtc_base:safe_conversions", "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", "../../rtc_base:threading", - "../../rtc_base:timeutils", "../../rtc_base/experiments:encoder_info_settings", - "../../rtc_base/synchronization:mutex", "../../rtc_base/system:file_wrapper", "../../rtc_base/system:unused", "../../system_wrappers", "../../system_wrappers:metrics", "../../test:create_test_environment", "../../test:create_test_field_trials", - "../../test:fake_encoded_frame", "../../test:fake_video_codecs", "../../test:fileutils", "../../test:run_loop", @@ -1370,21 +1191,14 @@ if (rtc_include_tests) { "../../test:video_test_common", "../../test:video_test_support", "../../test/time_controller", - "../../third_party/libyuv", "../../video/config:encoder_config", "../rtp_rtcp", "../rtp_rtcp:rtp_rtcp_format", "../rtp_rtcp:rtp_video_header", "codecs/av1:video_coding_codecs_av1_tests", - "deprecated:deprecated_frame_buffer", - "deprecated:deprecated_jitter_buffer_common", - "deprecated:deprecated_packet", - "deprecated:deprecated_session_info", - "deprecated:deprecated_stream_generator", "svc:scalability_structure_tests", "svc:simulcast_to_svc_converter_tests", "svc:svc_rate_allocator_tests", - "timing:jitter_estimator", "timing:timing_module", "//third_party/abseil-cpp/absl/container:inlined_vector", ] diff --git a/modules/video_coding/DEPS b/modules/video_coding/DEPS index 87944916815..e71c53f96c2 100644 --- a/modules/video_coding/DEPS +++ b/modules/video_coding/DEPS @@ -11,10 +11,10 @@ include_rules = [ ] specific_include_rules = { - "multiplex_encoder_adapter\.cc": [ + "multiplex_encoder_adapter\\.cc": [ "+media/base", ], - ".*test.*\.cc": [ + ".*test.*\\.cc": [ "+media/base", "+media/engine", "+video/config", diff --git a/modules/video_coding/OWNERS b/modules/video_coding/OWNERS index 5073079d345..f9f3e9bb09a 100644 --- a/modules/video_coding/OWNERS +++ b/modules/video_coding/OWNERS @@ -1,8 +1,6 @@ asapersson@webrtc.org brandtr@webrtc.org ilnik@webrtc.org -marpan@webrtc.org philipel@webrtc.org sprang@webrtc.org ssilkin@webrtc.org -stefan@webrtc.org diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn index 7403c800415..a5bb945da11 100644 --- a/modules/video_coding/codecs/av1/BUILD.gn +++ b/modules/video_coding/codecs/av1/BUILD.gn @@ -61,9 +61,8 @@ rtc_library("libaom_av1_encoder") { ] deps = [ ":libaom_speed_config_factory", + "../..:frame_sampler", "../..:video_codec_interface", - "../..:video_coding_utility", - "../../:frame_sampler", "../../../../api:field_trials_view", "../../../../api:scoped_refptr", "../../../../api/environment", @@ -75,19 +74,16 @@ rtc_library("libaom_av1_encoder") { "../../../../api/video:video_frame", "../../../../api/video:video_frame_type", "../../../../api/video:video_rtp_headers", - "../../../../api/video_codecs:encoder_speed_controller_factory", "../../../../api/video_codecs:scalability_mode", "../../../../api/video_codecs:video_codecs_api", - "../../../../common_video", "../../../../common_video/generic_frame_descriptor", - "../../../../modules/rtp_rtcp:rtp_rtcp_format", "../../../../rtc_base:checks", "../../../../rtc_base:logging", - "../../../../rtc_base:rtc_numerics", "../../../../rtc_base/experiments:encoder_info_settings", "../../../../rtc_base/experiments:encoder_speed_experiment", "../../../../rtc_base/experiments:psnr_experiment", "../../../../system_wrappers", + "../../../rtp_rtcp:rtp_rtcp_format", "../../svc:scalability_structures", "../../svc:scalable_video_controller", "//third_party/abseil-cpp/absl/algorithm:container", @@ -99,17 +95,44 @@ rtc_library("libaom_av1_encoder") { ] } +rtc_library("libaom_av1_encoder_v2") { + visibility = [ "../../../../api/video_codecs:libaom_av1_encoder_factory" ] + poisonous = [ "software_video_codecs" ] + sources = [ + "libaom_av1_encoder_v2.cc", + "libaom_av1_encoder_v2.h", + ] + deps = [ + "../../../../api:scoped_refptr", + "../../../../api/units:data_rate", + "../../../../api/units:data_size", + "../../../../api/units:time_delta", + "../../../../api/video:resolution", + "../../../../api/video:video_codec_constants", + "../../../../api/video:video_frame", + "../../../../api/video_codecs:video_codecs_api", + "../../../../api/video_codecs:video_encoder_factory_interface", + "../../../../api/video_codecs:video_encoder_interface", + "../../../../api/video_codecs:video_encoding_general", + "../../../../rtc_base:checks", + "../../../../rtc_base:logging", + "../../../../rtc_base:rtc_numerics", + "../../../../rtc_base:stringutils", + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/cleanup", + "//third_party/libaom", + ] +} + rtc_library("libaom_speed_config_factory") { sources = [ "libaom_speed_config_factory.cc", "libaom_speed_config_factory.h", ] deps = [ - "../..:video_codec_interface", - "../..:video_coding_utility", "../../../../api:field_trials_view", + "../../../../api/units:time_delta", "../../../../api/video_codecs:video_codecs_api", - "../../../../rtc_base:logging", "../../../../rtc_base/experiments:psnr_experiment", ] } @@ -127,8 +150,9 @@ if (rtc_include_tests) { ":av1_svc_config", ":dav1d_decoder", "../..:video_codec_interface", - "../../../../api:array_view", "../../../../api:field_trials", + "../../../../api:make_ref_counted", + "../../../../api:scoped_refptr", "../../../../api/environment", "../../../../api/environment:environment_factory", "../../../../api/transport/rtp:dependency_descriptor", @@ -166,6 +190,7 @@ if (rtc_include_tests) { "../../../../api/units:data_size", "../../../../api/units:time_delta", "../../../../modules/rtp_rtcp:rtp_rtcp_format", + "../../../../test:create_test_environment", "../../../../test:fileutils", "../../../../test:test_support", "../../../../test:video_test_support", diff --git a/modules/video_coding/codecs/av1/dav1d_decoder.cc b/modules/video_coding/codecs/av1/dav1d_decoder.cc index 7a52dfa1a3c..9356dcb5459 100644 --- a/modules/video_coding/codecs/av1/dav1d_decoder.cc +++ b/modules/video_coding/codecs/av1/dav1d_decoder.cc @@ -82,9 +82,6 @@ class ScopedDav1dPicture : public RefCountedNonVirtual { constexpr char kDav1dName[] = "dav1d"; -// Calling `dav1d_data_wrap` requires a `free_callback` to be registered. -void NullFreeCallback(const uint8_t* /* buffer */, void* /* opaque */) {} - Dav1dDecoder::Dav1dDecoder() = default; Dav1dDecoder::Dav1dDecoder(const Environment& env) @@ -142,9 +139,25 @@ int32_t Dav1dDecoder::Decode(const EncodedImage& encoded_image, ScopedDav1dData scoped_dav1d_data; Dav1dData& dav1d_data = scoped_dav1d_data.Data(); - dav1d_data_wrap(&dav1d_data, encoded_image.data(), encoded_image.size(), - /*free_callback=*/&NullFreeCallback, - /*user_data=*/nullptr); + + // Calling GetEncodedData will create a new `scoped_refptr` and increment the + // ref count. By simply releasing we are now responsible for decrementing + // the ref count when appropriate, which is when dav1d calls the + // `free_callback` to indicate that the buffer is no longer needed. + EncodedImageBufferInterface* bitstream_buffer = + encoded_image.GetEncodedData().release(); + + // Note that the `bitstream_buffer` can have a higher capacity than what is + // actually being used, so `encoded_image.size()` should be used to get the + // actual size of the bitstream. + dav1d_data_wrap( + &dav1d_data, encoded_image.data(), encoded_image.size(), + /*free_callback=*/ + [](const uint8_t* /* buffer */, void* user_data) { + auto* bb = static_cast(user_data); + bb->Release(); + }, + /*user_data=*/bitstream_buffer); if (int decode_res = dav1d_send_data(context_, &dav1d_data)) { RTC_LOG(LS_WARNING) diff --git a/modules/video_coding/codecs/av1/dav1d_decoder_unittest.cc b/modules/video_coding/codecs/av1/dav1d_decoder_unittest.cc index 1c6477bac11..edb969e99d7 100644 --- a/modules/video_coding/codecs/av1/dav1d_decoder_unittest.cc +++ b/modules/video_coding/codecs/av1/dav1d_decoder_unittest.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" #include "api/video/color_space.h" @@ -45,7 +45,7 @@ constexpr uint8_t kAv1FrameWithBT709FullRangeColorSpace[] = { 0x22, 0x02, 0x02, 0x03, 0x08, 0x32, 0x0e, 0x10, 0x00, 0xac, 0x02, 0x05, 0x14, 0x20, 0x81, 0x00, 0x02, 0x00, 0x95, 0xe1, 0xe0}; -EncodedImage CreateEncodedImage(ArrayView data) { +EncodedImage CreateEncodedImage(std::span data) { EncodedImage image; image.SetEncodedData(EncodedImageBuffer::Create(data.data(), data.size())); return image; @@ -67,13 +67,9 @@ class TestAv1Decoder : public DecodedImageCallback { TestAv1Decoder(const TestAv1Decoder&) = delete; TestAv1Decoder& operator=(const TestAv1Decoder&) = delete; - void Decode(const EncodedImage& image) { - ASSERT_THAT(decoder_, NotNull()); + int32_t Decode(const EncodedImage& image) { decoded_frame_ = std::nullopt; - int32_t error = - decoder_->Decode(image, /*render_time_ms=*/image.capture_time_ms_); - ASSERT_EQ(error, WEBRTC_VIDEO_CODEC_OK); - ASSERT_THAT(decoded_frame_, Not(Eq(std::nullopt))); + return decoder_->Decode(image, /*render_time_ms=*/image.capture_time_ms_); } VideoFrame& decoded_frame() { return *decoded_frame_; } @@ -95,8 +91,9 @@ class TestAv1Decoder : public DecodedImageCallback { TEST(Dav1dDecoderTest, KeepsDecodedResolutionByDefault) { TestAv1Decoder decoder(CreateEnvironment()); - decoder.Decode( - CreateEncodedImage(kAv1FrameWith36x20EncodededAnd32x16RenderResolution)); + EXPECT_EQ(decoder.Decode(CreateEncodedImage( + kAv1FrameWith36x20EncodededAnd32x16RenderResolution)), + WEBRTC_VIDEO_CODEC_OK); EXPECT_EQ(decoder.decoded_frame().width(), 36); EXPECT_EQ(decoder.decoded_frame().height(), 20); } @@ -104,8 +101,9 @@ TEST(Dav1dDecoderTest, KeepsDecodedResolutionByDefault) { TEST(Dav1dDecoderTest, CropsToRenderResolutionWhenCropIsEnabled) { TestAv1Decoder decoder(CreateEnvironment(CreateTestFieldTrialsPtr( "WebRTC-Dav1dDecoder-CropToRenderResolution/Enabled/"))); - decoder.Decode( - CreateEncodedImage(kAv1FrameWith36x20EncodededAnd32x16RenderResolution)); + EXPECT_EQ(decoder.Decode(CreateEncodedImage( + kAv1FrameWith36x20EncodededAnd32x16RenderResolution)), + WEBRTC_VIDEO_CODEC_OK); EXPECT_EQ(decoder.decoded_frame().width(), 32); EXPECT_EQ(decoder.decoded_frame().height(), 16); } @@ -113,16 +111,18 @@ TEST(Dav1dDecoderTest, CropsToRenderResolutionWhenCropIsEnabled) { TEST(Dav1dDecoderTest, DoesNotCropToRenderResolutionWhenCropIsDisabled) { TestAv1Decoder decoder(CreateEnvironment(CreateTestFieldTrialsPtr( "WebRTC-Dav1dDecoder-CropToRenderResolution/Disabled/"))); - decoder.Decode( - CreateEncodedImage(kAv1FrameWith36x20EncodededAnd32x16RenderResolution)); + EXPECT_EQ(decoder.Decode(CreateEncodedImage( + kAv1FrameWith36x20EncodededAnd32x16RenderResolution)), + WEBRTC_VIDEO_CODEC_OK); EXPECT_EQ(decoder.decoded_frame().width(), 36); EXPECT_EQ(decoder.decoded_frame().height(), 20); } TEST(Dav1dDecoderTest, SetsColorSpaceOnDecodedFrame) { TestAv1Decoder decoder(CreateEnvironment()); - decoder.Decode( - CreateEncodedImage(kAv1FrameWithBT709FullRangeColorSpace)); + EXPECT_EQ( + decoder.Decode(CreateEncodedImage(kAv1FrameWithBT709FullRangeColorSpace)), + WEBRTC_VIDEO_CODEC_OK); auto color_space = decoder.decoded_frame().color_space(); EXPECT_TRUE(color_space.has_value()); EXPECT_EQ(color_space->primaries(), ColorSpace::PrimaryID::kBT709); @@ -131,6 +131,37 @@ TEST(Dav1dDecoderTest, SetsColorSpaceOnDecodedFrame) { EXPECT_EQ(color_space->range(), ColorSpace::RangeID::kFull); } +TEST(Dav1dDecoderTest, FailDecoderOnTiles) { + // This test tests buffer management, any failures would be detected + // by running this test under ASAN. + static constexpr uint8_t kTile0[] = { + 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x3c, 0xff, 0xbc, 0x01, 0xa0, 0x08, + 0x32, 0xbd, 0x01, 0x10, 0x60, 0xb0, 0x01, 0xa6, 0x9a, 0x68, 0x50, 0x91, + 0xb0, 0x80, 0xb0, 0xd9, 0x8a, 0x40, 0xe4, 0x3e, 0xfb, 0x06, 0x1f, 0x0e, + 0xcc, 0xdc, 0xcb, 0x8c, 0x2d, 0x5c, 0xb0, 0x52, 0xe0, 0xb3, 0x89, 0x2b, + 0x6b, 0x8b, 0x56, 0xd3, 0x0a, 0x70, 0xad, 0x26, 0xf0, 0xca, 0xfa, 0xad, + 0x27, 0xcc, 0x5b, 0x8c, 0x0c, 0x72, 0x76, 0xa7, 0x42, 0x69, 0x4c, 0x47, + 0xf4, 0x2b, 0xa9, 0x5e, 0x13, 0x81, 0x69, 0x47, 0x40, 0x13, 0x3e, 0xac, + 0xef, 0x0f, 0x74, 0x02, 0x79, 0xa7, 0x50, 0xf4, 0x50, 0x82, 0x4d, 0x4f, + 0x70, 0x02, 0x9c, 0x0f, 0x61, 0xbc, 0x1c, 0x22, 0xc2, 0xac, 0x9f, 0x88, + 0xbd, 0x2a, 0xdc, 0xdb, 0xf0, 0xbe, 0x95, 0x54, 0x6e, 0xda, 0x08, 0x76, + 0x2c, 0x86, 0xd2, 0x0f, 0x86, 0xc1, 0x86, 0xcb, 0x98, 0x35, 0xfc, 0x2c, + 0xe7, 0x51, 0x44, 0x62, 0xfe, 0xf1, 0x97, 0x1f, 0xb0, 0x7f, 0x14, 0xc4, + 0xef, 0xb0, 0x01, 0x7c, 0x07, 0x22, 0x3e, 0xda, 0x2b, 0xef, 0x59, 0xcc, + 0x57, 0x65, 0xc5, 0x22, 0xe9, 0x61, 0x99, 0x0c, 0xd6, 0x07, 0x68, 0x1a, + 0x26, 0xad, 0x9b, 0x1e, 0x91, 0x2c, 0xee, 0xe2, 0xe4, 0x7c, 0x4d, 0x07, + 0x51, 0x72, 0x89, 0x51, 0xbf, 0x17, 0x67, 0xc0, 0x6d, 0x02, 0x69, 0x92, + 0x20, 0x7b, 0xeb, 0x11, 0xea, 0xc1, 0x43, 0x59, 0x4f, 0x75, 0xbb, 0x20}; + static constexpr uint8_t kTile1[] = { + 0x22, 0x1e, 0xe0, 0xc1, 0xb6, 0x2f, 0x07, 0xd0, 0x0b, 0xaf, 0x14, + 0xbf, 0x51, 0xa3, 0x0d, 0xf4, 0xb6, 0x45, 0xd8, 0x16, 0xda, 0x8c, + 0xb0, 0xbf, 0x29, 0x39, 0xdf, 0x9a, 0x9e, 0x9c, 0x69, 0x80}; + + TestAv1Decoder decoder(CreateEnvironment()); + decoder.Decode(CreateEncodedImage(kTile0)); + decoder.Decode(CreateEncodedImage(kTile1)); +} + } // namespace } // namespace test } // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc index 3f340d675ab..1079cb8d0a3 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc @@ -63,6 +63,16 @@ #define MOBILE_ARM #endif +#ifndef AOM_EFLAG_CALCULATE_PSNR +#define AOM_EFLAG_CALCULATE_PSNR (1 << 3) +#endif + +#if defined(WEBRTC_ENCODER_PSNR_STATS) +constexpr bool kEnablePsnrStats = true; +#else +constexpr bool kEnablePsnrStats = false; +#endif + #define SET_ENCODER_PARAM_OR_RETURN_ERROR(param_id, param_value) \ do { \ if (!SetEncoderControlParameters(param_id, param_value)) { \ @@ -116,6 +126,8 @@ EncoderSpeedController::EncodeResults ToSpeedControllerEncodeResult( .speed = speed, .encode_time = encode_result.encode_time, .qp = image.qp_ / 4, // Use [0, 63] range instead of [0, 255]. + .psnr = image.psnr().has_value() ? std::optional(image.psnr()->y) + : std::nullopt, .frame_info = frame_info}; } @@ -183,8 +195,7 @@ class LibaomAv1Encoder final : public VideoEncoder { CodecSpecificInfo CreateCodecSpecificInfo( const EncodedImage& image, - const ScalableVideoController::LayerFrameConfig& layer_frame, - bool end_of_picture); + const ScalableVideoController::LayerFrameConfig& layer_frame); std::unique_ptr svc_controller_; std::optional scalability_mode_; @@ -205,6 +216,7 @@ class LibaomAv1Encoder final : public VideoEncoder { EncodedImageCallback* encoded_image_callback_; double framerate_fps_; // Current target frame rate. int64_t timestamp_; + const Environment& env_; const LibaomAv1EncoderInfoSettings encoder_info_override_; // TODO(webrtc:351644568): Remove this kill-switch after the feature is fully // deployed. @@ -262,8 +274,9 @@ LibaomAv1Encoder::LibaomAv1Encoder(const Environment& env, encoded_image_callback_(nullptr), framerate_fps_(0), timestamp_(0), - encoder_info_override_(env.field_trials()), - post_encode_frame_drop_(!env.field_trials().IsDisabled( + env_(env), + encoder_info_override_(env_.field_trials()), + post_encode_frame_drop_(!env_.field_trials().IsDisabled( "WebRTC-LibaomAv1Encoder-PostEncodeFrameDrop")), psnr_experiment_(env.field_trials()), psnr_frame_sampler_(psnr_experiment_.SamplingInterval()), @@ -441,16 +454,17 @@ int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, speed_config_factory.GetSpeedConfig( encoder_settings_.spatialLayers[si].width, encoder_settings_.spatialLayers[si].height, - svc_controller_->StreamConfig().num_temporal_layers); + svc_controller_->StreamConfig().num_temporal_layers, + env_.field_trials()); speed_controllers_.push_back( EncoderSpeedController::Create(speed_config, GetFrameInterval(si))); } } else { EncoderSpeedController::Config speed_config = - speed_config_factory.GetSpeedConfig(encoder_settings_.width, - encoder_settings_.height, - /*num_temporal_layers=*/1); + speed_config_factory.GetSpeedConfig( + encoder_settings_.width, encoder_settings_.height, + /*num_temporal_layers=*/1, env_.field_trials()); speed_controllers_.push_back(EncoderSpeedController::Create( speed_config, GetFrameInterval(/*spatial_index=*/0))); } @@ -864,6 +878,13 @@ int32_t LibaomAv1Encoder::Encode( mapped_buffer->height()); auto i420_buffer = mapped_buffer->GetI420(); RTC_DCHECK(i420_buffer); + + // TODO: crbug.com/492213293 - Remove once the root cause is fixed. + if (i420_buffer->StrideU() != i420_buffer->StrideV()) { + RTC_LOG(LS_ERROR) << "Libaom requires the U and V strides to be equal."; + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + RTC_CHECK_EQ(i420_buffer->width(), frame_for_encode_->d_w); RTC_CHECK_EQ(i420_buffer->height(), frame_for_encode_->d_h); frame_for_encode_->planes[AOM_PLANE_Y] = @@ -905,6 +926,9 @@ int32_t LibaomAv1Encoder::Encode( svc_params_ ? svc_params_->number_spatial_layers : 1; auto next_layer_frame = layer_frames.begin(); std::vector> encoded_images; + // Index into `encoded_images` indicating the last active layer which produced + // an encoded image. Used to correctly set `end_of_picture`. + std::optional last_encoded_image_index; for (size_t sid = 0; sid < num_spatial_layers; ++sid) { // The libaom AV1 encoder requires that `aom_codec_encode` is called for // every spatial layer, even if the configured bitrate for that layer is @@ -921,7 +945,6 @@ int32_t LibaomAv1Encoder::Encode( non_encoded_layer_frame.emplace().S(sid); layer_frame = &*non_encoded_layer_frame; } - const bool end_of_picture = (next_layer_frame == layer_frames.end()); aom_enc_frame_flags_t flags = layer_frame->IsKeyframe() ? AOM_EFLAG_FORCE_KF : 0; @@ -931,25 +954,50 @@ int32_t LibaomAv1Encoder::Encode( SetSvcRefFrameConfig(*layer_frame); } -#if defined(WEBRTC_ENCODER_PSNR_STATS) && defined(AOM_EFLAG_CALCULATE_PSNR) - if (psnr_experiment_.IsEnabled() && - psnr_frame_sampler_.ShouldBeSampled(frame)) { - flags |= AOM_EFLAG_CALCULATE_PSNR; - } -#endif - + EncodeResult output; if (!speed_controllers_.empty()) { RTC_DCHECK_GT(speed_controllers_.size(), sid); EncoderSpeedController& speed_controller = *speed_controllers_[sid]; EncoderSpeedController::FrameEncodingInfo frame_info{ .reference_type = AsSpeedControllerFrameType(*layer_frame), - .is_repeat_frame = frame.is_repeat_frame()}; + .is_repeat_frame = frame.is_repeat_frame(), + .timestamp = Timestamp::Millis(frame.render_time_ms())}; EncoderSpeedController::EncodeSettings settings = speed_controller.GetEncodeSettings(frame_info); + if (settings.calculate_psnr && kEnablePsnrStats) { + flags |= AOM_EFLAG_CALCULATE_PSNR; + } + + // Encode with baseline settings in case a speed control probe is + // requested. + std::optional baseline_output; + if (settings.baseline_comparison_speed.has_value()) { + // Configure the desired speed setting. + SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_SET_CPUUSED, + *settings.baseline_comparison_speed); + + // Encode the frame, with the encoder internal state frozen - so that + // rate control etc is not updated. + baseline_output = DoEncode( + duration, flags | AOM_EFLAG_FREEZE_INTERNAL_STATE, layer_frame); + + if (baseline_output->status_code != AOM_CODEC_OK) { + RTC_LOG(LS_WARNING) + << "LibaomAv1Encoder::Encode returned error: '" + << aom_codec_err_to_string(baseline_output->status_code) << "'."; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + if (!baseline_output->encoded_image.has_value()) { + // Frame dropped, no point in trying to encode a second time. + continue; + } + } + SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_SET_CPUUSED, settings.speed); - EncodeResult output = DoEncode(duration, flags, layer_frame); + output = DoEncode(duration, flags, layer_frame); if (output.status_code != AOM_CODEC_OK) { RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned error: '" @@ -957,65 +1005,90 @@ int32_t LibaomAv1Encoder::Encode( return WEBRTC_VIDEO_CODEC_ERROR; } - if (!output.encoded_image.has_value()) { + if (non_encoded_layer_frame || !output.encoded_image.has_value()) { // Frame dropped, presumably by rate controller. This is not an error. - continue; + if (baseline_output.has_value() && + baseline_output->encoded_image.has_value()) { + // Second encoding dropped frame, but not the first. This is + // unexpected. Use baseline encoding as main frame instead. + output = *baseline_output; + baseline_output.reset(); + RTC_LOG(LS_WARNING) << "AV1 Encoder unexpectedly dropped frame on " + << "second encoding of PSNR probe."; + } else { + EncodedImage dropped_image; + dropped_image.SetSpatialIndex(sid); + dropped_image.set_end_of_temporal_unit(sid == num_spatial_layers - 1); + encoded_images.emplace_back(std::move(dropped_image), + CodecSpecificInfo()); + continue; + } } - RTC_DCHECK(output.encoded_image.has_value()); - speed_controller.OnEncodedFrame( - ToSpeedControllerEncodeResult(output, frame_info, settings.speed)); + ToSpeedControllerEncodeResult(output, frame_info, settings.speed), + baseline_output.has_value() + ? std::optional( + ToSpeedControllerEncodeResult( + *baseline_output, frame_info, + *settings.baseline_comparison_speed)) + : std::nullopt); - RTC_DCHECK_GT(output.encoded_image->size(), 0u); - PopulateEncodedImageFromVideoFrame(frame, *output.encoded_image); - CodecSpecificInfo codec_specifics = CreateCodecSpecificInfo( - *output.encoded_image, *layer_frame, end_of_picture); + } else { + // No speed controller used. - if (non_encoded_layer_frame) { - continue; + if (kEnablePsnrStats && psnr_experiment_.IsEnabled() && + psnr_frame_sampler_.ShouldBeSampled(frame)) { + flags |= AOM_EFLAG_CALCULATE_PSNR; } - encoded_images.emplace_back(std::move(*output.encoded_image), - std::move(codec_specifics)); - } else { - // No speed controller used. - EncodeResult output = DoEncode(duration, flags, layer_frame); + output = DoEncode(duration, flags, layer_frame); if (output.status_code != AOM_CODEC_OK) { RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned error: '" << aom_codec_err_to_string(output.status_code) << "'."; return WEBRTC_VIDEO_CODEC_ERROR; } - if (!output.encoded_image.has_value()) { - // Status code OK but no image - the encoder dropped the frame, - // presumable due to rate control. This is not an error. + if (non_encoded_layer_frame || !output.encoded_image.has_value()) { + // Frame dropped, presumably by rate controller. This is not an error. + EncodedImage dropped_image; + dropped_image.SetSpatialIndex(sid); + dropped_image.set_end_of_temporal_unit(sid == num_spatial_layers - 1); + encoded_images.emplace_back(std::move(dropped_image), + CodecSpecificInfo()); continue; } + } - if (non_encoded_layer_frame) { - continue; - } + RTC_DCHECK(output.encoded_image.has_value()); + RTC_DCHECK_GT(output.encoded_image->size(), 0u); - RTC_DCHECK_GT(output.encoded_image->size(), 0u); - PopulateEncodedImageFromVideoFrame(frame, *output.encoded_image); - CodecSpecificInfo codec_specifics = CreateCodecSpecificInfo( - *output.encoded_image, *layer_frame, end_of_picture); + PopulateEncodedImageFromVideoFrame(frame, *output.encoded_image); + CodecSpecificInfo codec_specifics = + CreateCodecSpecificInfo(*output.encoded_image, *layer_frame); - encoded_images.emplace_back(std::move(*output.encoded_image), - std::move(codec_specifics)); - } + output.encoded_image->set_end_of_temporal_unit(sid == + num_spatial_layers - 1); + last_encoded_image_index = encoded_images.size(); + encoded_images.emplace_back(std::move(*output.encoded_image), + std::move(codec_specifics)); } - if (!encoded_images.empty()) { - encoded_images.back().second.end_of_picture = true; - } - for (auto& [encoded_image, codec_specifics] : encoded_images) { - encoded_image_callback_->OnEncodedImage(encoded_image, &codec_specifics); - if (encoded_image.SpatialIndex().has_value()) { - last_encoded_timestamp_by_sid_[*encoded_image.SpatialIndex()] = - frame.rtp_timestamp(); + for (size_t i = 0; i < encoded_images.size(); ++i) { + auto& [encoded_image, codec_specifics] = encoded_images[i]; + codec_specifics.end_of_picture = i == last_encoded_image_index; + if (encoded_image.size() > 0) { + encoded_image_callback_->OnEncodedImage(encoded_image, &codec_specifics); + if (encoded_image.SpatialIndex().has_value()) { + last_encoded_timestamp_by_sid_[*encoded_image.SpatialIndex()] = + frame.rtp_timestamp(); + } + continue; } + // Size 0 indicates a dropped frame (inserted above). + encoded_image_callback_->OnFrameDropped( + frame.rtp_timestamp(), *encoded_image.SpatialIndex(), + *encoded_image.is_end_of_temporal_unit()); } return WEBRTC_VIDEO_CODEC_OK; @@ -1062,9 +1135,9 @@ EncodeResult LibaomAv1Encoder::DoEncode( layer_frame->Keyframe(); } - encoded_image._frameType = layer_frame->IsKeyframe() - ? VideoFrameType::kVideoFrameKey - : VideoFrameType::kVideoFrameDelta; + encoded_image.set_frame_type(layer_frame->IsKeyframe() + ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta); encoded_image.content_type_ = VideoContentType::UNSPECIFIED; // If encoded image width/height info are added to aom_codec_cx_pkt_t, @@ -1108,11 +1181,9 @@ EncodeResult LibaomAv1Encoder::DoEncode( CodecSpecificInfo LibaomAv1Encoder::CreateCodecSpecificInfo( const EncodedImage& image, - const ScalableVideoController::LayerFrameConfig& layer_frame, - bool end_of_picture) { + const ScalableVideoController::LayerFrameConfig& layer_frame) { CodecSpecificInfo codec_specific_info; codec_specific_info.codecType = kVideoCodecAV1; - codec_specific_info.end_of_picture = end_of_picture; codec_specific_info.scalability_mode = scalability_mode_; bool is_keyframe = layer_frame.IsKeyframe(); codec_specific_info.generic_frame_info = diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc index c41582a2c57..6cd6cba6e6c 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc @@ -176,9 +176,9 @@ TEST_P(LibaomAv1EncoderTest, std::vector encoded_frames = EncodedVideoFrameProducer(*encoder).SetNumInputFrames(1).Encode(); ASSERT_THAT(encoded_frames, SizeIs(2)); - EXPECT_THAT(encoded_frames[0].encoded_image._frameType, + EXPECT_THAT(encoded_frames[0].encoded_image.frame_type(), Eq(VideoFrameType::kVideoFrameKey)); - EXPECT_THAT(encoded_frames[1].encoded_image._frameType, + EXPECT_THAT(encoded_frames[1].encoded_image.frame_type(), Eq(VideoFrameType::kVideoFrameDelta)); } @@ -199,9 +199,9 @@ TEST_P(LibaomAv1EncoderTest, NoBitrateOnTopSpatialLayerProduceDeltaFrames) { std::vector encoded_frames = EncodedVideoFrameProducer(*encoder).SetNumInputFrames(2).Encode(); ASSERT_THAT(encoded_frames, SizeIs(2)); - EXPECT_THAT(encoded_frames[0].encoded_image._frameType, + EXPECT_THAT(encoded_frames[0].encoded_image.frame_type(), Eq(VideoFrameType::kVideoFrameKey)); - EXPECT_THAT(encoded_frames[1].encoded_image._frameType, + EXPECT_THAT(encoded_frames[1].encoded_image.frame_type(), Eq(VideoFrameType::kVideoFrameDelta)); } @@ -436,9 +436,9 @@ TEST_P(LibaomAv1EncoderTest, RtpTimestampWrap) { .SetRtpTimestamp(std::numeric_limits::max()) .Encode(); ASSERT_THAT(encoded_frames, SizeIs(2)); - EXPECT_THAT(encoded_frames[0].encoded_image._frameType, + EXPECT_THAT(encoded_frames[0].encoded_image.frame_type(), Eq(VideoFrameType::kVideoFrameKey)); - EXPECT_THAT(encoded_frames[1].encoded_image._frameType, + EXPECT_THAT(encoded_frames[1].encoded_image.frame_type(), Eq(VideoFrameType::kVideoFrameDelta)); } @@ -505,6 +505,9 @@ TEST_P(LibaomAv1EncoderTest, AdheresToTargetBitrateDespiteUnevenFrameTiming) { bytes_encoded_ += DataSize::Bytes(encoded_image.size()); return Result(Result::Error::OK); } + void OnFrameDropped(uint32_t rtp_timestamp, + int spatial_id, + bool is_end_of_temporal_unit) override {} DataSize bytes_encoded_ = DataSize::Zero(); } callback; @@ -611,6 +614,9 @@ TEST_P(LibaomAv1EncoderTest, PostEncodeFrameDrop) { frames_encoded_++; return Result(Result::Error::OK); } + void OnFrameDropped(uint32_t rtp_timestamp, + int spatial_id, + bool is_end_of_temporal_unit) override {} int frames_encoded_ = 0; } callback; @@ -853,5 +859,179 @@ TEST_P(LibaomAv1EncoderTest, L1T2RepeatFrameNotDroppedIfDeltaTooLarge) { WEBRTC_VIDEO_CODEC_OK); } +TEST_P(LibaomAv1EncoderTest, TriggerFrameDropOnBaseLayer) { + std::unique_ptr encoder = + CreateLibaomAv1Encoder(CreateTestEnvironment()); + VideoCodec codec_settings = DefaultCodecSettings(); + codec_settings.width = 640; + codec_settings.height = 360; + codec_settings.SetScalabilityMode(ScalabilityMode::kL2T1); + codec_settings.SetFrameDropEnabled(true); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + MockEncodedImageCallback callback; + encoder->RegisterEncodeCompleteCallback(&callback); + + VideoEncoder::RateControlParameters rate_parameters; + rate_parameters.framerate_fps = 30; + // Set sufficient bitrate for both layers initially. + // Layer 0: 50 kbps. + rate_parameters.bitrate.SetBitrate(0, 0, 50000); + // Layer 1: 50 kbps. + rate_parameters.bitrate.SetBitrate(1, 0, 50000); + encoder->SetRates(rate_parameters); + + auto frame_generator = test::CreateSquareFrameGenerator( + codec_settings.width, codec_settings.height, + test::FrameGeneratorInterface::OutputType::kI420, std::nullopt); + + // Encode a keyframe first. Both layers should be encoded. + EXPECT_CALL( + callback, + OnEncodedImage( + AllOf(Property(&EncodedImage::SpatialIndex, Eq(0)), + Property(&EncodedImage::is_end_of_temporal_unit, Eq(false))), + Pointee(Field(&CodecSpecificInfo::end_of_picture, Eq(false))))) + .WillOnce(Return(EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK))); + EXPECT_CALL( + callback, + OnEncodedImage( + AllOf(Property(&EncodedImage::SpatialIndex, Eq(1)), + Property(&EncodedImage::is_end_of_temporal_unit, Eq(true))), + Pointee(Field(&CodecSpecificInfo::end_of_picture, Eq(true))))) + .WillOnce(Return(EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK))); + + VideoFrame key_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(1000) + .build(); + + std::vector key_frame_types = { + VideoFrameType::kVideoFrameKey}; + ASSERT_EQ(encoder->Encode(key_frame, &key_frame_types), + WEBRTC_VIDEO_CODEC_OK); + + // Now lower Layer 1 bitrate to force a drop. + // Layer 1: 0 kbps (force drop). + rate_parameters.bitrate.SetBitrate(1, 0, 0); + encoder->SetRates(rate_parameters); + + // Expect OnEncodedImage for spatial layer 0. + // Since L1 is dropped, L0 is the last encoded frame, so + // is_end_of_temporal_unit should be true. + EXPECT_CALL( + callback, + OnEncodedImage( + AllOf(Property(&EncodedImage::SpatialIndex, Eq(0)), + Property(&EncodedImage::is_end_of_temporal_unit, Eq(false))), + Pointee(Field(&CodecSpecificInfo::end_of_picture, Eq(true))))) + .WillOnce(Return(EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK))); + + // Expect OnFrameDropped for spatial layer 1. + EXPECT_CALL(callback, OnFrameDropped(_, /*spatial_id=*/1, + /*is_end_of_temporal_unit=*/true)); + + VideoFrame delta_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(2000) + .build(); + + std::vector delta_frame_types = { + VideoFrameType::kVideoFrameDelta}; + ASSERT_EQ(encoder->Encode(delta_frame, &delta_frame_types), + WEBRTC_VIDEO_CODEC_OK); +} + +TEST_P(LibaomAv1EncoderTest, TriggerFrameDropOnLayer0WhileLayer1Encoded) { + std::unique_ptr encoder = + CreateLibaomAv1Encoder(CreateTestEnvironment()); + VideoCodec codec_settings = DefaultCodecSettings(); + codec_settings.SetScalabilityMode(ScalabilityMode::kS2T1); + codec_settings.SetFrameDropEnabled(true); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + MockEncodedImageCallback callback; + encoder->RegisterEncodeCompleteCallback(&callback); + + VideoEncoder::RateControlParameters rate_parameters; + rate_parameters.framerate_fps = 30; + // Set moderate bitrate for both layers initially to prevent excessive buffer + // accumulation, but ensure it's enough to encode. + // Layer 0: 30 kbps. + rate_parameters.bitrate.SetBitrate(0, 0, 30000); + // Layer 1: High bitrate. + rate_parameters.bitrate.SetBitrate(1, 0, 1000000); + encoder->SetRates(rate_parameters); + + auto frame_generator = test::CreateSquareFrameGenerator( + codec_settings.width, codec_settings.height, + test::FrameGeneratorInterface::OutputType::kI420, std::nullopt); + + // Encode a keyframe first. Both layers should be encoded. + EXPECT_CALL( + callback, + OnEncodedImage( + AllOf(Property(&EncodedImage::SpatialIndex, Eq(0)), + Property(&EncodedImage::is_end_of_temporal_unit, Eq(false))), + Pointee(Field(&CodecSpecificInfo::end_of_picture, Eq(false))))) + .WillOnce(Return(EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK))); + EXPECT_CALL( + callback, + OnEncodedImage( + AllOf(Property(&EncodedImage::SpatialIndex, Eq(1)), + Property(&EncodedImage::is_end_of_temporal_unit, Eq(true))), + Pointee(Field(&CodecSpecificInfo::end_of_picture, Eq(true))))) + .WillOnce(Return(EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK))); + + VideoFrame key_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(1000) + .build(); + + std::vector key_frame_types = { + VideoFrameType::kVideoFrameKey}; + ASSERT_EQ(encoder->Encode(key_frame, &key_frame_types), + WEBRTC_VIDEO_CODEC_OK); + + // Now lower Layer 0 bitrate to force a drop. + // Layer 0: 1 kbps. + rate_parameters.bitrate.SetBitrate(0, 0, 1000); + encoder->SetRates(rate_parameters); + + // Expect OnFrameDropped for spatial layer 0. + EXPECT_CALL(callback, OnFrameDropped(_, /*spatial_id=*/0, + /*is_end_of_temporal_unit=*/false)); + // Expect OnEncodedImage for spatial layer 1. + EXPECT_CALL( + callback, + OnEncodedImage( + AllOf(Property(&EncodedImage::SpatialIndex, Eq(1)), + Property(&EncodedImage::is_end_of_temporal_unit, Eq(true))), + Pointee(Field(&CodecSpecificInfo::end_of_picture, Eq(true))))) + .WillOnce(Return(EncodedImageCallback::Result( + EncodedImageCallback::Result::Error::OK))); + + VideoFrame delta_frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(2000) + .build(); + + std::vector delta_frame_types = { + VideoFrameType::kVideoFrameDelta}; + ASSERT_EQ(encoder->Encode(delta_frame, &delta_frame_types), + WEBRTC_VIDEO_CODEC_OK); +} + } // namespace } // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.cc new file mode 100644 index 00000000000..255e8d8807f --- /dev/null +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.cc @@ -0,0 +1,807 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_coding/codecs/av1/libaom_av1_encoder_v2.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/cleanup/cleanup.h" +#include "api/scoped_refptr.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/video/resolution.h" +#include "api/video/video_frame_buffer.h" +#include "api/video_codecs/video_codec.h" +#include "api/video_codecs/video_encoder_factory_interface.h" +#include "api/video_codecs/video_encoder_interface.h" +#include "api/video_codecs/video_encoding_general.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/rational.h" +#include "rtc_base/strings/string_builder.h" +#include "third_party/libaom/source/libaom/aom/aom_codec.h" +#include "third_party/libaom/source/libaom/aom/aom_encoder.h" +#include "third_party/libaom/source/libaom/aom/aom_image.h" +#include "third_party/libaom/source/libaom/aom/aomcx.h" + +#define SET_OR_RETURN(param_id, param_value) \ + do { \ + if (!SetEncoderControlParameters(&ctx_, param_id, param_value)) { \ + return; \ + } \ + } while (0) + +#define SET_OR_RETURN_FALSE(param_id, param_value) \ + do { \ + if (!SetEncoderControlParameters(&ctx_, param_id, param_value)) { \ + return false; \ + } \ + } while (0) + +namespace webrtc { + +using FrameEncodeSettings = VideoEncoderInterface::FrameEncodeSettings; +using Cbr = FrameEncodeSettings::Cbr; +using Cqp = FrameEncodeSettings::Cqp; +using aom_img_ptr = std::unique_ptr; + +namespace { + +constexpr int kMaxQp = 63; +constexpr int kNumBuffers = 8; +constexpr int kMaxReferences = 3; +constexpr int kMinEffortLevel = -2; +constexpr int kMaxEffortLevel = 2; +constexpr int kMaxSpatialLayersLimit = 4; +constexpr int kMaxTemporalLayers = 4; +constexpr int kRtpTicksPerSecond = 90000; + +static_assert(kMaxSpatialLayersLimit <= AOM_MAX_SS_LAYERS); +static_assert(kMaxTemporalLayers <= AOM_MAX_TS_LAYERS); + +constexpr std::array kSupportedScalingFactors = { + {{.numerator = 8, .denominator = 1}, + {.numerator = 4, .denominator = 1}, + {.numerator = 2, .denominator = 1}, + {.numerator = 1, .denominator = 1}, + {.numerator = 1, .denominator = 2}, + {.numerator = 1, .denominator = 4}, + {.numerator = 1, .denominator = 8}}}; + +std::optional GetScalingFactor(const Resolution& from, + const Resolution& to) { + auto it = absl::c_find_if(kSupportedScalingFactors, [&](const Rational& r) { + return (from.width * r.numerator / r.denominator) == to.width && + (from.height * r.numerator / r.denominator) == to.height; + }); + + if (it != kSupportedScalingFactors.end()) { + return *it; + } + + return {}; +} + +template +bool SetEncoderControlParameters(aom_codec_ctx_t* ctx, int id, T value) { + aom_codec_err_t error_code = aom_codec_control(ctx, id, value); + if (error_code != AOM_CODEC_OK) { + RTC_LOG(LS_WARNING) << "aom_codec_control returned " << error_code + << " with id: " << id << "."; + } + return error_code == AOM_CODEC_OK; +} + +struct ThreadTilesAndSuperblockSizeInfo { + int num_threads; + int exp_tile_rows; + int exp_tile_colums; + aom_superblock_size_t superblock_size; +}; + +ThreadTilesAndSuperblockSizeInfo GetThreadingTilesAndSuperblockSize( + int width, + int height, + int max_number_of_threads) { + ThreadTilesAndSuperblockSizeInfo res; + const int num_pixels = width * height; + if (num_pixels >= 1920 * 1080 && max_number_of_threads > 8) { + res.num_threads = 8; + res.exp_tile_rows = 2; + res.exp_tile_colums = 1; + } else if (num_pixels >= 640 * 360 && max_number_of_threads > 4) { + res.num_threads = 4; + res.exp_tile_rows = 1; + res.exp_tile_colums = 1; + } else if (num_pixels >= 320 * 180 && max_number_of_threads > 2) { + res.num_threads = 2; + res.exp_tile_rows = 1; + res.exp_tile_colums = 0; + } else { + res.num_threads = 1; + res.exp_tile_rows = 0; + res.exp_tile_colums = 0; + } + + if (res.num_threads > 4 && num_pixels >= 960 * 540) { + res.superblock_size = AOM_SUPERBLOCK_SIZE_64X64; + } else { + res.superblock_size = AOM_SUPERBLOCK_SIZE_DYNAMIC; + } + + RTC_LOG(LS_WARNING) << __FUNCTION__ << " res.num_threads=" << res.num_threads + << " res.exp_tile_rows=" << res.exp_tile_rows + << " res.exp_tile_colums=" << res.exp_tile_colums + << " res.superblock_size=" << res.superblock_size; + + return res; +} + +bool ValidateEncodeParams( + const VideoFrameBuffer& /* frame_buffer */, + const VideoEncoderInterface::TemporalUnitSettings& /* tu_settings */, + const std::vector& frame_settings, + const std::array, 8>& last_resolution_in_buffer, + aom_rc_mode rc_mode) { + if (frame_settings.empty()) { + RTC_LOG(LS_ERROR) << "No frame settings provided."; + return false; + } + + auto in_range = [](int low, int high, int val) { + return low <= val && val < high; + }; + + for (size_t i = 0; i < frame_settings.size(); ++i) { + const FrameEncodeSettings& settings = frame_settings[i]; + + if (!settings.frame_output) { + RTC_LOG(LS_ERROR) << "No frame output provided."; + return false; + } + + if (!in_range(kMinEffortLevel, kMaxEffortLevel + 1, + settings.effort_level)) { + RTC_LOG(LS_ERROR) << "Unsupported effort level " << settings.effort_level; + return false; + } + + if (!in_range(0, kMaxSpatialLayersLimit, settings.spatial_id)) { + RTC_LOG(LS_ERROR) << "invalid spatial id " << settings.spatial_id; + return false; + } + + if (!in_range(0, kMaxTemporalLayers, settings.temporal_id)) { + RTC_LOG(LS_ERROR) << "invalid temporal id " << settings.temporal_id; + return false; + } + + if ((settings.frame_type == VideoEncoderInterface::FrameType::kKeyframe || + settings.frame_type == + VideoEncoderInterface::FrameType::kStartFrame) && + !settings.reference_buffers.empty()) { + RTC_LOG(LS_ERROR) << "Reference buffers can not be used for keyframes."; + return false; + } + + if ((settings.frame_type == VideoEncoderInterface::FrameType::kKeyframe || + settings.frame_type == + VideoEncoderInterface::FrameType::kStartFrame) && + !settings.update_buffer) { + RTC_LOG(LS_ERROR) + << "Buffer to update must be specified for keyframe/startframe"; + return false; + } + + if (settings.update_buffer && + !in_range(0, kNumBuffers, *settings.update_buffer)) { + RTC_LOG(LS_ERROR) << "Invalid update buffer id."; + return false; + } + + if (settings.reference_buffers.size() > kMaxReferences) { + RTC_LOG(LS_ERROR) << "Too many referenced buffers."; + return false; + } + + for (size_t j = 0; j < settings.reference_buffers.size(); ++j) { + if (!in_range(0, kNumBuffers, settings.reference_buffers[j])) { + RTC_LOG(LS_ERROR) << "Invalid reference buffer id."; + return false; + } + + // Figure out which frame resolution a certain buffer will hold when the + // frame described by `settings` is encoded. + std::optional referenced_resolution; + bool keyframe_on_previous_layer = false; + + // Will some other frame in this temporal unit update the buffer? + for (size_t k = 0; k < i; ++k) { + if (frame_settings[k].frame_type == + VideoEncoderInterface::FrameType::kKeyframe) { + keyframe_on_previous_layer = true; + referenced_resolution.reset(); + } + if (frame_settings[k].update_buffer == settings.reference_buffers[j]) { + referenced_resolution = frame_settings[k].resolution; + } + } + + // Not updated by another frame in the temporal unit, what is the + // resolution of the last frame stored into that buffer? + if (!referenced_resolution && !keyframe_on_previous_layer) { + referenced_resolution = + last_resolution_in_buffer[settings.reference_buffers[j]]; + } + + if (!referenced_resolution) { + RTC_LOG(LS_ERROR) << "Referenced buffer holds no frame."; + return false; + } + + if (!GetScalingFactor(*referenced_resolution, settings.resolution)) { + RTC_LOG(LS_ERROR) + << "Required resolution scaling factor not supported."; + return false; + } + + for (size_t l = i + 1; l < settings.reference_buffers.size(); ++l) { + if (settings.reference_buffers[i] == settings.reference_buffers[l]) { + RTC_LOG(LS_ERROR) << "Duplicate reference buffer specified."; + return false; + } + } + } + + if ((rc_mode == AOM_CBR && + std::holds_alternative(settings.rate_options)) || + (rc_mode == AOM_Q && + std::holds_alternative(settings.rate_options))) { + RTC_LOG(LS_ERROR) << "Invalid rate options, encoder configured with " + << (rc_mode == AOM_CBR ? "AOM_CBR" : "AOM_Q"); + return false; + } + + for (size_t j = i + 1; j < frame_settings.size(); ++j) { + if (settings.spatial_id >= frame_settings[j].spatial_id) { + RTC_LOG(LS_ERROR) << "Frame spatial id specified out of order."; + return false; + } + } + } + + return true; +} + +void PrepareInputImage(const VideoFrameBuffer& input_buffer, + aom_img_ptr& out_aom_image) { + aom_img_fmt_t input_format; + switch (input_buffer.type()) { + case VideoFrameBuffer::Type::kI420: + input_format = AOM_IMG_FMT_I420; + break; + case VideoFrameBuffer::Type::kNV12: + input_format = AOM_IMG_FMT_NV12; + break; + default: + RTC_CHECK_NOTREACHED(); + return; + } + + if (!out_aom_image || out_aom_image->fmt != input_format || + static_cast(out_aom_image->w) != input_buffer.width() || + static_cast(out_aom_image->h) != input_buffer.height()) { + out_aom_image.reset( + aom_img_wrap(/*img=*/nullptr, input_format, input_buffer.width(), + input_buffer.height(), /*align=*/1, /*img_data=*/nullptr)); + + RTC_LOG(LS_WARNING) << __FUNCTION__ << " input_format=" << input_format + << " input_buffer.width()=" << input_buffer.width() + << " input_buffer.height()=" << input_buffer.height() + << " w=" << out_aom_image->w + << " h=" << out_aom_image->h + << " d_w=" << out_aom_image->d_w + << " d_h=" << out_aom_image->d_h + << " r_w=" << out_aom_image->r_w + << " r_h=" << out_aom_image->r_h; + } + + if (input_format == AOM_IMG_FMT_I420) { + const I420BufferInterface* i420_buffer = input_buffer.GetI420(); + RTC_DCHECK(i420_buffer); + out_aom_image->planes[AOM_PLANE_Y] = + const_cast(i420_buffer->DataY()); + out_aom_image->planes[AOM_PLANE_U] = + const_cast(i420_buffer->DataU()); + out_aom_image->planes[AOM_PLANE_V] = + const_cast(i420_buffer->DataV()); + out_aom_image->stride[AOM_PLANE_Y] = i420_buffer->StrideY(); + out_aom_image->stride[AOM_PLANE_U] = i420_buffer->StrideU(); + out_aom_image->stride[AOM_PLANE_V] = i420_buffer->StrideV(); + } else { + const NV12BufferInterface* nv12_buffer = input_buffer.GetNV12(); + RTC_DCHECK(nv12_buffer); + out_aom_image->planes[AOM_PLANE_Y] = + const_cast(nv12_buffer->DataY()); + out_aom_image->planes[AOM_PLANE_U] = + const_cast(nv12_buffer->DataUV()); + out_aom_image->planes[AOM_PLANE_V] = nullptr; + out_aom_image->stride[AOM_PLANE_Y] = nv12_buffer->StrideY(); + out_aom_image->stride[AOM_PLANE_U] = nv12_buffer->StrideUV(); + out_aom_image->stride[AOM_PLANE_V] = 0; + } +} + +aom_svc_ref_frame_config_t GetSvcRefFrameConfig( + const FrameEncodeSettings& settings) { + // Buffer alias to use for each position. In particular when there are two + // buffers being used, prefer to alias them as LAST and GOLDEN, since the AV1 + // bitstream format has dedicated fields for them. See last_frame_idx and + // golden_frame_idx in the av1 spec + // https://aomediacodec.github.io/av1-spec/av1-spec.pdf. + + // Libaom is also compiled for RTC, which limits the number of references to + // at most three, and they must be aliased as LAST, GOLDEN and ALTREF. Also + // note that libaom favors LAST the most, and GOLDEN second most, so buffers + // should be specified in order of how useful they are for prediction. Libaom + // could be updated to make LAST, GOLDEN and ALTREF equivalent, but that is + // not a priority for now. All aliases can be used to update buffers. + // TD: Automatically select LAST, GOLDEN and ALTREF depending on previous + // buffer usage. + static constexpr std::array kPreferedAlias = {0, // LAST + 3, // GOLDEN + 6, // ALTREF + 1, 2, 4, 5}; + + aom_svc_ref_frame_config_t ref_frame_config = {}; + std::span ref_idx_view(ref_frame_config.ref_idx, 7); + std::span reference_view(ref_frame_config.reference, 7); + std::span refresh_view(ref_frame_config.refresh, 8); + + int alias_index = 0; + if (!settings.reference_buffers.empty()) { + for (size_t i = 0; i < settings.reference_buffers.size(); ++i) { + ref_idx_view[kPreferedAlias[alias_index]] = settings.reference_buffers[i]; + reference_view[kPreferedAlias[alias_index]] = 1; + alias_index++; + } + + // Delta frames must not alias unused buffers, and since start frames only + // update some buffers it is not safe to leave unused aliases to simply + // point to buffer 0. + for (size_t i = settings.reference_buffers.size(); i < ref_idx_view.size(); + ++i) { + ref_idx_view[kPreferedAlias[i]] = settings.reference_buffers.back(); + } + } + + if (settings.update_buffer) { + if (!absl::c_linear_search(settings.reference_buffers, + *settings.update_buffer)) { + ref_idx_view[kPreferedAlias[alias_index]] = *settings.update_buffer; + alias_index++; + } + refresh_view[*settings.update_buffer] = 1; + } + + char buf[256]; + SimpleStringBuilder sb(buf); + sb << " spatial_id=" << settings.spatial_id; + sb << " ref_idx=[ "; + for (auto r : ref_frame_config.ref_idx) { + sb << r << " "; + } + sb << "] reference=[ "; + for (auto r : ref_frame_config.reference) { + sb << r << " "; + } + sb << "] refresh=[ "; + for (auto r : ref_frame_config.refresh) { + sb << r << " "; + } + sb << "]"; + + RTC_LOG(LS_WARNING) << __FUNCTION__ << sb.str(); + + return ref_frame_config; +} + +aom_svc_params_t GetSvcParams( + const VideoFrameBuffer& frame_buffer, + const std::vector& frame_settings) { + aom_svc_params_t svc_params = {}; + svc_params.number_spatial_layers = frame_settings.back().spatial_id + 1; + svc_params.number_temporal_layers = kMaxTemporalLayers; + + std::span framerate_factor_view(svc_params.framerate_factor, + AOM_MAX_TS_LAYERS); + std::span scaling_factor_num_view(svc_params.scaling_factor_num, + AOM_MAX_SS_LAYERS); + std::span scaling_factor_den_view(svc_params.scaling_factor_den, + AOM_MAX_SS_LAYERS); + std::span layer_target_bitrate_view(svc_params.layer_target_bitrate, + AOM_MAX_LAYERS); + std::span max_quantizers_view(svc_params.max_quantizers, AOM_MAX_LAYERS); + std::span min_quantizers_view(svc_params.min_quantizers, AOM_MAX_LAYERS); + + // TD: What about svc_params.framerate_factor? + // If `framerate_factors` are left at 0 then configured bitrate values will + // not be picked up by libaom. + for (int tid = 0; tid < svc_params.number_temporal_layers; ++tid) { + framerate_factor_view[tid] = 1; + } + + // If the scaling factor is left at zero for unused layers a division by zero + // will happen inside libaom, default all layers to one. + for (int sid = 0; sid < svc_params.number_spatial_layers; ++sid) { + scaling_factor_num_view[sid] = 1; + scaling_factor_den_view[sid] = 1; + } + + for (const FrameEncodeSettings& settings : frame_settings) { + std::optional scaling_factor = GetScalingFactor( + {.width = frame_buffer.width(), .height = frame_buffer.height()}, + settings.resolution); + RTC_CHECK(scaling_factor); + scaling_factor_num_view[settings.spatial_id] = scaling_factor->numerator; + scaling_factor_den_view[settings.spatial_id] = scaling_factor->denominator; + + const int flat_layer_id = + settings.spatial_id * svc_params.number_temporal_layers + + settings.temporal_id; + + RTC_LOG(LS_WARNING) << __FUNCTION__ << " flat_layer_id=" << flat_layer_id + << " num=" + << scaling_factor_num_view[settings.spatial_id] + << " den=" + << scaling_factor_den_view[settings.spatial_id]; + + std::visit( + [&](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + // Libaom calculates the total bitrate across all spatial layers by + // summing the bitrate of the last temporal layer in each spatial + // layer. This means the bitrate for the top temporal layer always + // has to be set even if that temporal layer is not being encoded. + const int last_temporal_layer_in_spatial_layer_id = + settings.spatial_id * svc_params.number_temporal_layers + + (kMaxTemporalLayers - 1); + layer_target_bitrate_view[last_temporal_layer_in_spatial_layer_id] = + arg.target_bitrate.kbps(); + + layer_target_bitrate_view[flat_layer_id] = + arg.target_bitrate.kbps(); + // When libaom is configured with `AOM_CBR` it will still limit QP + // to stay between `min_quantizers` and `max_quantizers'. Set + // `max_quantizers` to max QP to avoid the encoder overshooting. + max_quantizers_view[flat_layer_id] = kMaxQp; + min_quantizers_view[flat_layer_id] = 0; + } else if constexpr (std::is_same_v) { + // When libaom is configured with `AOM_Q` it will still look at the + // `layer_target_bitrate` to determine whether the layer is disabled + // or not. Set `layer_target_bitrate` to 1 so that libaom knows the + // layer is active. + layer_target_bitrate_view[flat_layer_id] = 1; + max_quantizers_view[flat_layer_id] = arg.target_qp; + min_quantizers_view[flat_layer_id] = arg.target_qp; + // TD: Does libaom look at both max and min? Shouldn't it just be + // one of them? + RTC_LOG(LS_WARNING) << __FUNCTION__ << " svc_params.qp[" + << flat_layer_id << "]=" << arg.target_qp; + } + }, + settings.rate_options); + } + + char buf[512]; + SimpleStringBuilder sb(buf); + sb << "GetSvcParams" << " layer bitrates kbps"; + for (int s = 0; s < svc_params.number_spatial_layers; ++s) { + sb << " S" << s << "=[ "; + for (int t = 0; t < svc_params.number_temporal_layers; ++t) { + int id = s * svc_params.number_temporal_layers + t; + sb << "T" << t << "=" << layer_target_bitrate_view[id] << " "; + } + sb << "]"; + } + + RTC_LOG(LS_WARNING) << sb.str(); + + return svc_params; +} + +} // namespace + +LibaomAv1EncoderV2::~LibaomAv1EncoderV2() { + aom_codec_destroy(&ctx_); +} + +bool LibaomAv1EncoderV2::InitEncode( + const VideoEncoderFactoryInterface::StaticEncoderSettings& settings, + const std::map& encoder_specific_settings) { + if (!encoder_specific_settings.empty()) { + RTC_LOG(LS_ERROR) + << "libaom av1 encoder accepts no encoder specific settings"; + return false; + } + + if (aom_codec_err_t ret = aom_codec_enc_config_default( + aom_codec_av1_cx(), &cfg_, AOM_USAGE_REALTIME); + ret != AOM_CODEC_OK) { + RTC_LOG(LS_ERROR) << "aom_codec_enc_config_default returned " << ret; + return false; + } + + max_number_of_threads_ = settings.max_number_of_threads; + + // The encode resolution is set dynamically for each call to `Encode`, but for + // `aom_codec_enc_init` to not fail we set it here as well. + cfg_.g_w = settings.max_encode_dimensions.width; + cfg_.g_h = settings.max_encode_dimensions.height; + cfg_.g_timebase.num = 1; + // TD: does 90khz timebase make sense, use microseconds instead maybe? + cfg_.g_timebase.den = kRtpTicksPerSecond; + cfg_.g_input_bit_depth = settings.encoding_format.bit_depth; + cfg_.kf_mode = AOM_KF_DISABLED; + // TD: rc_undershoot_pct and rc_overshoot_pct should probably be removed. + cfg_.rc_undershoot_pct = 50; + cfg_.rc_overshoot_pct = 50; + auto* cbr = + std::get_if( + &settings.rc_mode); + cfg_.rc_buf_initial_sz = cbr ? cbr->target_buffer_size.ms() : 600; + cfg_.rc_buf_optimal_sz = cbr ? cbr->target_buffer_size.ms() : 600; + cfg_.rc_buf_sz = cbr ? cbr->max_buffer_size.ms() : 1000; + cfg_.g_usage = AOM_USAGE_REALTIME; + cfg_.g_pass = AOM_RC_ONE_PASS; + cfg_.g_lag_in_frames = 0; + cfg_.g_error_resilient = 0; + cfg_.rc_end_usage = cbr ? AOM_CBR : AOM_Q; + + if (aom_codec_err_t ret = + aom_codec_enc_init(&ctx_, aom_codec_av1_cx(), &cfg_, /*flags=*/0); + ret != AOM_CODEC_OK) { + RTC_LOG(LS_ERROR) << "aom_codec_enc_init returned " << ret; + return false; + } + + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_CDEF, 1); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_TPL_MODEL, 0); + SET_OR_RETURN_FALSE(AV1E_SET_DELTAQ_MODE, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_ORDER_HINT, 0); + SET_OR_RETURN_FALSE(AV1E_SET_AQ_MODE, 3); + SET_OR_RETURN_FALSE(AOME_SET_MAX_INTRA_BITRATE_PCT, 300); + SET_OR_RETURN_FALSE(AV1E_SET_COEFF_COST_UPD_FREQ, 3); + SET_OR_RETURN_FALSE(AV1E_SET_MODE_COST_UPD_FREQ, 3); + SET_OR_RETURN_FALSE(AV1E_SET_MV_COST_UPD_FREQ, 3); + SET_OR_RETURN_FALSE(AV1E_SET_ROW_MT, 1); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_OBMC, 0); + SET_OR_RETURN_FALSE(AV1E_SET_NOISE_SENSITIVITY, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_WARPED_MOTION, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_GLOBAL_MOTION, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_REF_FRAME_MVS, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_CFL_INTRA, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_SMOOTH_INTRA, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_ANGLE_DELTA, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_FILTER_INTRA, 0); + SET_OR_RETURN_FALSE(AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1); + SET_OR_RETURN_FALSE(AV1E_SET_DISABLE_TRELLIS_QUANT, 1); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_DIST_WTD_COMP, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_DIFF_WTD_COMP, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_DUAL_FILTER, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTERINTRA_COMP, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_INTRABC, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_MASKED_COMP, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_PAETH_INTRA, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_QM, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_RECT_PARTITIONS, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_RESTORATION, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0); + SET_OR_RETURN_FALSE(AV1E_SET_ENABLE_TX64, 0); + SET_OR_RETURN_FALSE(AV1E_SET_MAX_REFERENCE_FRAMES, 3); + + return true; +} + +void LibaomAv1EncoderV2::Encode( + scoped_refptr frame_buffer, + const TemporalUnitSettings& tu_settings, + std::vector frame_settings) { + // On return call `EncodeComplete` with EncodingError result unless they + // were already called with an EncodedData result. + absl::Cleanup on_return = [&] { + for (FrameEncodeSettings& settings : frame_settings) { + if (settings.frame_output) { + settings.frame_output->EncodeComplete( + VideoEncoderInterface::EncodingError()); + } + } + }; + + if (!ValidateEncodeParams(*frame_buffer, tu_settings, frame_settings, + last_resolution_in_buffer_, cfg_.rc_end_usage)) { + return; + } + + if (current_content_type_ != tu_settings.content_hint) { + if (tu_settings.content_hint == VideoCodecMode::kScreensharing) { + // TD: Set speed 11? + SET_OR_RETURN(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN); + SET_OR_RETURN(AV1E_SET_ENABLE_PALETTE, 1); + } else { + SET_OR_RETURN(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_DEFAULT); + SET_OR_RETURN(AV1E_SET_ENABLE_PALETTE, 0); + } + current_content_type_ = tu_settings.content_hint; + } + + if (cfg_.rc_end_usage == AOM_CBR) { + DataRate accum_rate = DataRate::Zero(); + for (const FrameEncodeSettings& settings : frame_settings) { + accum_rate += std::get(settings.rate_options).target_bitrate; + } + cfg_.rc_target_bitrate = accum_rate.kbps(); + RTC_LOG(LS_WARNING) << __FUNCTION__ + << " cfg_.rc_target_bitrate=" << cfg_.rc_target_bitrate; + } + + if (static_cast(cfg_.g_w) != frame_buffer->width() || + static_cast(cfg_.g_h) != frame_buffer->height()) { + RTC_LOG(LS_WARNING) << __FUNCTION__ << " resolution changed from " + << cfg_.g_w << "x" << cfg_.g_h << " to " + << frame_buffer->width() << "x" + << frame_buffer->height(); + ThreadTilesAndSuperblockSizeInfo ttsbi = GetThreadingTilesAndSuperblockSize( + frame_buffer->width(), frame_buffer->height(), max_number_of_threads_); + SET_OR_RETURN(AV1E_SET_SUPERBLOCK_SIZE, ttsbi.superblock_size); + SET_OR_RETURN(AV1E_SET_TILE_ROWS, ttsbi.exp_tile_rows); + SET_OR_RETURN(AV1E_SET_TILE_COLUMNS, ttsbi.exp_tile_colums); + cfg_.g_threads = ttsbi.num_threads; + cfg_.g_w = frame_buffer->width(); + cfg_.g_h = frame_buffer->height(); + } + + PrepareInputImage(*frame_buffer, image_to_encode_); + + // The bitrates calculated internally in libaom when `AV1E_SET_SVC_PARAMS` is + // called depends on the currently configured `cfg_.rc_target_bitrate`. If the + // total target bitrate is not updated first a division by zero could happen. + if (aom_codec_err_t ret = aom_codec_enc_config_set(&ctx_, &cfg_); + ret != AOM_CODEC_OK) { + RTC_LOG(LS_ERROR) << "aom_codec_enc_config_set returned " << ret; + return; + } + aom_svc_params_t svc_params = GetSvcParams(*frame_buffer, frame_settings); + SET_OR_RETURN(AV1E_SET_SVC_PARAMS, &svc_params); + + // The libaom AV1 encoder requires that `aom_codec_encode` is called for + // every spatial layer, even if no frame should be encoded for that layer. + std::array + settings_for_spatial_id; + settings_for_spatial_id.fill(nullptr); + FrameEncodeSettings settings_for_unused_layer; + for (FrameEncodeSettings& settings : frame_settings) { + settings_for_spatial_id[settings.spatial_id] = &settings; + } + + for (int sid = frame_settings[0].spatial_id; + sid < svc_params.number_spatial_layers; ++sid) { + const bool layer_enabled = settings_for_spatial_id[sid] != nullptr; + FrameEncodeSettings& settings = layer_enabled + ? *settings_for_spatial_id[sid] + : settings_for_unused_layer; + + aom_svc_layer_id_t layer_id = { + .spatial_layer_id = sid, + .temporal_layer_id = settings.temporal_id, + }; + SET_OR_RETURN(AV1E_SET_SVC_LAYER_ID, &layer_id); + aom_svc_ref_frame_config_t ref_config = GetSvcRefFrameConfig(settings); + SET_OR_RETURN(AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_config); + + // TD: Duration can't be zero, what does it matter when the layer is + // not being encoded? + TimeDelta duration = TimeDelta::Millis(1); + if (layer_enabled) { + if (const Cbr* cbr = std::get_if(&settings.rate_options)) { + duration = cbr->duration; + } else { + // TD: What should duration be when Cqp is used? + duration = TimeDelta::Millis(1); + } + + if (settings.effort_level != current_effort_level_[settings.spatial_id]) { + // For RTC we use speed level 6 to 10, with 8 being the default. Note + // that low effort means higher speed. + SET_OR_RETURN(AOME_SET_CPUUSED, 8 - settings.effort_level); + current_effort_level_[settings.spatial_id] = settings.effort_level; + } + } + + RTC_LOG(LS_WARNING) + << __FUNCTION__ << " timestamp=" + << (tu_settings.presentation_timestamp.ms() * kRtpTicksPerSecond / 1000) + << " duration=" << (duration.ms() * kRtpTicksPerSecond / 1000) + << " type=" + << (settings.frame_type == VideoEncoderInterface::FrameType::kKeyframe + ? "key" + : "delta"); + aom_codec_err_t ret = aom_codec_encode( + &ctx_, &*image_to_encode_, tu_settings.presentation_timestamp.ms() * 90, + duration.ms() * 90, + settings.frame_type == VideoEncoderInterface::FrameType::kKeyframe + ? AOM_EFLAG_FORCE_KF + : 0); + if (ret != AOM_CODEC_OK) { + RTC_LOG(LS_WARNING) << "aom_codec_encode returned " << ret; + return; + } + + if (!layer_enabled) { + continue; + } + + if (settings.frame_type == VideoEncoderInterface::FrameType::kKeyframe) { + last_resolution_in_buffer_ = {}; + } + + if (settings.update_buffer) { + last_resolution_in_buffer_[*settings.update_buffer] = settings.resolution; + } + + VideoEncoderInterface::EncodedData result; + aom_codec_iter_t iter = nullptr; + bool bitstream_produced = false; + while (const aom_codec_cx_pkt_t* pkt = + aom_codec_get_cx_data(&ctx_, &iter)) { + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT && pkt->data.frame.sz > 0) { + SET_OR_RETURN(AOME_GET_LAST_QUANTIZER_64, &result.encoded_qp); + result.frame_type = pkt->data.frame.flags & AOM_FRAME_IS_KEY + ? VideoEncoderInterface::FrameType::kKeyframe + : VideoEncoderInterface::FrameType::kDeltaFrame; + std::span output_buffer = + settings.frame_output->GetBitstreamOutputBuffer( + DataSize::Bytes(pkt->data.frame.sz)); + if (output_buffer.size() != pkt->data.frame.sz) { + return; + } + memcpy(output_buffer.data(), pkt->data.frame.buf, pkt->data.frame.sz); + bitstream_produced = true; + break; + } + } + + if (!bitstream_produced) { + return; + } else { + RTC_CHECK(settings.frame_output); + settings.frame_output->EncodeComplete(result); + settings.frame_output = nullptr; + } + } +} + +} // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.h b/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.h new file mode 100644 index 00000000000..75f75bcbe32 --- /dev/null +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_v2.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_V2_H_ +#define MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_V2_H_ + +#include +#include +#include +#include +#include +#include + +#include "api/scoped_refptr.h" +#include "api/video/resolution.h" +#include "api/video/video_codec_constants.h" +#include "api/video/video_frame_buffer.h" +#include "api/video_codecs/video_codec.h" +#include "api/video_codecs/video_encoder_factory_interface.h" +#include "api/video_codecs/video_encoder_interface.h" +#include "third_party/libaom/source/libaom/aom/aom_codec.h" +#include "third_party/libaom/source/libaom/aom/aom_encoder.h" +#include "third_party/libaom/source/libaom/aom/aom_image.h" + +namespace webrtc { + +class LibaomAv1EncoderV2 : public VideoEncoderInterface { + public: + LibaomAv1EncoderV2() = default; + ~LibaomAv1EncoderV2() override; + + bool InitEncode( + const VideoEncoderFactoryInterface::StaticEncoderSettings& settings, + const std::map& encoder_specific_settings); + + void Encode(scoped_refptr frame_buffer, + const TemporalUnitSettings& tu_settings, + std::vector frame_settings) override; + + private: + using aom_img_ptr = std::unique_ptr; + + aom_img_ptr image_to_encode_ = aom_img_ptr(nullptr, aom_img_free); + aom_codec_ctx_t ctx_; + aom_codec_enc_cfg_t cfg_; + + std::optional current_content_type_; + std::array, kMaxSpatialLayers> current_effort_level_; + int max_number_of_threads_; + std::array, 8> last_resolution_in_buffer_; +}; + +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_V2_H_ diff --git a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc index 58c15f801ad..df6f57216e0 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc @@ -22,13 +22,18 @@ #include "api/environment/environment.h" #include "api/environment/environment_factory.h" #include "api/field_trials.h" +#include "api/make_ref_counted.h" +#include "api/scoped_refptr.h" +#include "api/test/mock_video_encoder.h" #include "api/transport/rtp/dependency_descriptor.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/video/encoded_image.h" +#include "api/video/i420_buffer.h" #include "api/video/video_bitrate_allocation.h" #include "api/video/video_frame.h" +#include "api/video/video_frame_buffer.h" #include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_decoder.h" @@ -234,6 +239,80 @@ TEST(LibaomAv1Test, InitReleaseRepeatedly) { } } +TEST(LibaomAv1Test, RejectsNativeFramesWithUnequalChromaStrides) { + const Environment env = CreateEnvironment(); + std::unique_ptr encoder = CreateLibaomAv1Encoder(env); + VideoCodec codec_settings = DefaultCodecSettings(); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 300000); + encoder->SetRates(VideoEncoder::RateControlParameters( + allocation, codec_settings.maxFramerate)); + + MockEncodedImageCallback callback; + encoder->RegisterEncodeCompleteCallback(&callback); + + class FakeNativeBuffer : public VideoFrameBuffer { + public: + FakeNativeBuffer(int width, int height) : width_(width), height_(height) {} + Type type() const override { return Type::kNative; } + int width() const override { return width_; } + int height() const override { return height_; } + scoped_refptr ToI420() override { + return I420Buffer::Create(width_, height_, width_, (width_ + 1) / 2, + (width_ + 1) / 2 + 1); + } + + private: + int width_; + int height_; + }; + + auto buffer = make_ref_counted(codec_settings.width, + codec_settings.height); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ENCODER_FAILURE, + encoder->Encode(frame, nullptr)); +} + +TEST(LibaomAv1Test, RejectsI420FramesWithUnequalChromaStrides) { + const Environment env = CreateEnvironment(); + std::unique_ptr encoder = CreateLibaomAv1Encoder(env); + VideoCodec codec_settings = DefaultCodecSettings(); + ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), + WEBRTC_VIDEO_CODEC_OK); + + VideoBitrateAllocation allocation; + allocation.SetBitrate(0, 0, 300000); + encoder->SetRates(VideoEncoder::RateControlParameters( + allocation, codec_settings.maxFramerate)); + + MockEncodedImageCallback callback; + encoder->RegisterEncodeCompleteCallback(&callback); + + auto buffer = I420Buffer::Create( + /*width=*/codec_settings.width, + /*height=*/codec_settings.height, + /*stride_y=*/codec_settings.width, + /*stride_u=*/(codec_settings.width + 1) / 2, + /*stride_v=*/(codec_settings.width + 1) / 2 + 1); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ENCODER_FAILURE, + encoder->Encode(frame, nullptr)); +} + struct LayerId { friend bool operator==(const LayerId& lhs, const LayerId& rhs) { return std::tie(lhs.spatial_id, lhs.temporal_id) == diff --git a/modules/video_coding/codecs/av1/libaom_speed_config_factory.cc b/modules/video_coding/codecs/av1/libaom_speed_config_factory.cc index 71942629de3..00d25836e34 100644 --- a/modules/video_coding/codecs/av1/libaom_speed_config_factory.cc +++ b/modules/video_coding/codecs/av1/libaom_speed_config_factory.cc @@ -11,19 +11,29 @@ #include #include +#include "api/field_trials_view.h" +#include "api/units/time_delta.h" #include "api/video_codecs/encoder_speed_controller.h" #include "api/video_codecs/video_codec.h" +#include "rtc_base/experiments/psnr_experiment.h" namespace webrtc { namespace { +using SpeedLevel = EncoderSpeedController::Config::SpeedLevel; +using PsnrGain = EncoderSpeedController::Config::SpeedLevel::PsnrComparison; + constexpr int kNumLevels = 15; -EncoderSpeedController::Config::SpeedLevel kAllLevels[kNumLevels] = { - {.speeds = {5, 5, 6, 6}, .min_qp = 31}, +SpeedLevel kAllLevels[kNumLevels] = { + {.speeds = {5, 5, 6, 6}, + .min_qp = 31, + .min_psnr_gain = PsnrGain{.baseline_speed = 6, .psnr_threshold = 0.2}}, {.speeds = {5, 6, 7, 7}, .min_qp = 30}, {.speeds = {5, 6, 8, 10}, .min_qp = 30}, - {.speeds = {5, 6, 9, 11}, .min_qp = 29}, + {.speeds = {5, 6, 9, 11}, + .min_qp = 29, + .min_psnr_gain = PsnrGain{.baseline_speed = 7, .psnr_threshold = 0.25}}, {.speeds = {5, 7, 7, 7}, .min_qp = 29}, {.speeds = {7, 7, 8, 8}, .min_qp = 28}, {.speeds = {7, 7, 8, 9}, .min_qp = 28}, @@ -36,8 +46,8 @@ EncoderSpeedController::Config::SpeedLevel kAllLevels[kNumLevels] = { {.speeds = {9, 10, 11, 11}, .min_qp = std::nullopt}, {.speeds = {10, 11, 11, 11}, .min_qp = std::nullopt}}; -bool HasSameSpeeds(const EncoderSpeedController::Config::SpeedLevel& a, - const EncoderSpeedController::Config::SpeedLevel& b, +bool HasSameSpeeds(const SpeedLevel& a, + const SpeedLevel& b, int num_temporal_layers) { if (a.speeds[0] != b.speeds[0] || a.speeds[1] != b.speeds[1]) { // Keyframe or base layer speed differs. @@ -79,7 +89,8 @@ LibaomSpeedConfigFactory::LibaomSpeedConfigFactory( EncoderSpeedController::Config LibaomSpeedConfigFactory::GetSpeedConfig( int width, int height, - int num_temporal_layers) const { + int num_temporal_layers, + const FieldTrialsView& field_trials) { EncoderSpeedController::Config config; int num_levels = 0; switch (complexity_) { @@ -128,6 +139,21 @@ EncoderSpeedController::Config LibaomSpeedConfigFactory::GetSpeedConfig( config.start_speed_index = std::max(available_speed_levels - 1, 0); } + PsnrExperiment psnr_experiment(field_trials); + if (psnr_experiment.IsEnabled()) { + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kRegularBaseLayerSampling, + .sampling_interval = psnr_experiment.SamplingInterval(), + .average_base_layer_ratio = 1.0 / num_temporal_layers}; + } else { + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kOnlyWhenProbing, + .sampling_interval = TimeDelta::Seconds(1), + .average_base_layer_ratio = 1.0 / num_temporal_layers}; + } + return config; } diff --git a/modules/video_coding/codecs/av1/libaom_speed_config_factory.h b/modules/video_coding/codecs/av1/libaom_speed_config_factory.h index 6631da7a3b6..8799d589ce7 100644 --- a/modules/video_coding/codecs/av1/libaom_speed_config_factory.h +++ b/modules/video_coding/codecs/av1/libaom_speed_config_factory.h @@ -9,6 +9,7 @@ #ifndef MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_SPEED_CONFIG_FACTORY_H_ #define MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_SPEED_CONFIG_FACTORY_H_ +#include "api/field_trials_view.h" #include "api/video_codecs/encoder_speed_controller.h" #include "api/video_codecs/video_codec.h" @@ -19,9 +20,11 @@ class LibaomSpeedConfigFactory { LibaomSpeedConfigFactory(VideoCodecComplexity complexity, VideoCodecMode mode); - EncoderSpeedController::Config GetSpeedConfig(int width, - int height, - int num_temporal_layers) const; + EncoderSpeedController::Config GetSpeedConfig( + int width, + int height, + int num_temporal_layers, + const FieldTrialsView& field_trials); private: const VideoCodecComplexity complexity_; diff --git a/modules/video_coding/codecs/av1/libaom_speed_config_factory_unittest.cc b/modules/video_coding/codecs/av1/libaom_speed_config_factory_unittest.cc index ae4305e8838..3e9beeedbee 100644 --- a/modules/video_coding/codecs/av1/libaom_speed_config_factory_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_speed_config_factory_unittest.cc @@ -11,10 +11,15 @@ #include #include #include +#include +#include "api/environment/environment.h" +#include "api/field_trials.h" +#include "api/units/time_delta.h" #include "api/video_codecs/encoder_speed_controller.h" #include "api/video_codecs/video_codec.h" #include "rtc_base/checks.h" +#include "test/create_test_environment.h" #include "test/gtest.h" namespace webrtc { @@ -34,30 +39,32 @@ using ::testing::Values; // Test that the number of speed levels increases with complexity. TEST(LibaomSpeedConfigFactoryTest, NumLevelsIncreaseWithComplexity) { + FieldTrials empty_trial(""); LibaomSpeedConfigFactory factory_low(VideoCodecComplexity::kComplexityLow, VideoCodecMode::kRealtimeVideo); EncoderSpeedController::Config config_low = - factory_low.GetSpeedConfig(640, 360, 3); + factory_low.GetSpeedConfig(640, 360, 3, empty_trial); LibaomSpeedConfigFactory factory_normal( VideoCodecComplexity::kComplexityNormal, VideoCodecMode::kRealtimeVideo); EncoderSpeedController::Config config_normal = - factory_normal.GetSpeedConfig(640, 360, 3); + factory_normal.GetSpeedConfig(640, 360, 3, empty_trial); LibaomSpeedConfigFactory factory_high(VideoCodecComplexity::kComplexityHigh, VideoCodecMode::kRealtimeVideo); EncoderSpeedController::Config config_high = - factory_high.GetSpeedConfig(640, 360, 3); + factory_high.GetSpeedConfig(640, 360, 3, empty_trial); LibaomSpeedConfigFactory factory_higher( VideoCodecComplexity::kComplexityHigher, VideoCodecMode::kRealtimeVideo); EncoderSpeedController::Config config_higher = - factory_higher.GetSpeedConfig(640, 360, 3); + factory_higher.GetSpeedConfig(640, 360, 3, empty_trial); LibaomSpeedConfigFactory factory_max(VideoCodecComplexity::kComplexityMax, VideoCodecMode::kRealtimeVideo); + EncoderSpeedController::Config config_max = - factory_max.GetSpeedConfig(640, 360, 3); + factory_max.GetSpeedConfig(640, 360, 3, empty_trial); EXPECT_GE(config_normal.speed_levels.size(), config_low.speed_levels.size()); EXPECT_GE(config_high.speed_levels.size(), config_normal.speed_levels.size()); @@ -67,9 +74,11 @@ TEST(LibaomSpeedConfigFactoryTest, NumLevelsIncreaseWithComplexity) { // Test that speeds within each level are monotonic. TEST(LibaomSpeedConfigFactoryTest, SpeedsAreMonotonic) { + FieldTrials empty_trial(""); LibaomSpeedConfigFactory factory(VideoCodecComplexity::kComplexityMax, VideoCodecMode::kRealtimeVideo); - EncoderSpeedController::Config config = factory.GetSpeedConfig(1280, 720, 3); + EncoderSpeedController::Config config = + factory.GetSpeedConfig(1280, 720, 3, empty_trial); for (const auto& level : config.speed_levels) { // Lower reference class index means more important, so speed should be @@ -91,9 +100,11 @@ TEST(LibaomSpeedConfigFactoryTest, SpeedsAreMonotonic) { // Test that keyframe and base layer speeds between levels are monotonic. TEST(LibaomSpeedConfigFactoryTest, KeyAndMainSpeedsIncreaseBetweenLevels) { + FieldTrials empty_trial(""); LibaomSpeedConfigFactory factory(VideoCodecComplexity::kComplexityMax, VideoCodecMode::kRealtimeVideo); - EncoderSpeedController::Config config = factory.GetSpeedConfig(1280, 720, 3); + EncoderSpeedController::Config config = + factory.GetSpeedConfig(1280, 720, 3, empty_trial); for (size_t i = 0; i < config.speed_levels.size() - 1; ++i) { const auto& current_level = config.speed_levels[i]; @@ -130,21 +141,23 @@ TEST_P(LibaomSpeedConfigFactoryResolutionTest, GetSpeedConfigStartSpeedIndex) { const ResolutionParams& params = GetParam(); LibaomSpeedConfigFactory factory(VideoCodecComplexity::kComplexityMax, VideoCodecMode::kRealtimeVideo); + FieldTrials empty_trial(""); EncoderSpeedController::Config config = - factory.GetSpeedConfig(params.width, params.height, 3); + factory.GetSpeedConfig(params.width, params.height, 3, empty_trial); int expected_index = std::max(0, static_cast(config.speed_levels.size()) - params.expected_start_index_offset); EXPECT_EQ(config.start_speed_index, expected_index); } -void CheckDistinctConfigs(const LibaomSpeedConfigFactory& factory, +void CheckDistinctConfigs(LibaomSpeedConfigFactory& factory, int num_temporal_layers) { RTC_DCHECK_GT(num_temporal_layers, 0); RTC_DCHECK_LE(num_temporal_layers, 3); + FieldTrials empty_trial(""); EncoderSpeedController::Config config = - factory.GetSpeedConfig(640, 360, num_temporal_layers); + factory.GetSpeedConfig(640, 360, num_temporal_layers, empty_trial); std::set unique_configs( config.speed_levels.begin(), config.speed_levels.end()); @@ -169,5 +182,24 @@ TEST(LibaomSpeedConfigFactoryTest, DistinctConfigs3Tl) { CheckDistinctConfigs(factory, 3); } +TEST(LibaomSpeedConfigFactoryTest, PropagatesPsnrExperimentSettings) { + const std::string kFieldTrials = + "WebRTC-Video-CalculatePsnr/Enabled,sampling_interval:3000ms/"; + Environment env = CreateTestEnvironment({.field_trials = kFieldTrials}); + + LibaomSpeedConfigFactory factory(VideoCodecComplexity::kComplexityMax, + VideoCodecMode::kRealtimeVideo); + EncoderSpeedController::Config config = + factory.GetSpeedConfig(1280, 720, 2, env.field_trials()); + + ASSERT_TRUE(config.psnr_probing_settings.has_value()); + EXPECT_EQ(config.psnr_probing_settings->mode, + EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kRegularBaseLayerSampling); + EXPECT_EQ(config.psnr_probing_settings->sampling_interval, + TimeDelta::Seconds(3)); + EXPECT_EQ(config.psnr_probing_settings->average_base_layer_ratio, 0.5); +} + } // namespace } // namespace webrtc diff --git a/modules/video_coding/codecs/h264/h264_encoder_impl.cc b/modules/video_coding/codecs/h264/h264_encoder_impl.cc index d75c132a78f..b682013e626 100644 --- a/modules/video_coding/codecs/h264/h264_encoder_impl.cc +++ b/modules/video_coding/codecs/h264/h264_encoder_impl.cc @@ -450,6 +450,11 @@ int32_t H264EncoderImpl::Encode( << " image to I420. Can't encode frame."; return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; } + if (frame_buffer->StrideU() != frame_buffer->StrideV()) { + // TODO: crbug.com/chromium:491655161 - Remove once the root cause is fixed. + RTC_LOG(LS_ERROR) << "OpenH264 requires the U and V strides to be equal."; + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } RTC_CHECK(frame_buffer->type() == VideoFrameBuffer::Type::kI420 || frame_buffer->type() == VideoFrameBuffer::Type::kI420A); @@ -472,6 +477,25 @@ int32_t H264EncoderImpl::Encode( psnr_frame_sampler_.ShouldBeSampled(input_frame); #endif + int num_layers_to_send = 0; + std::vector frame_types_to_send( + configurations_.size(), VideoFrameType::kVideoFrameDelta); + for (size_t i = 0; i < encoders_.size(); ++i) { + if (!configurations_[i].sending) { + frame_types_to_send[i] = VideoFrameType::kEmptyFrame; + continue; + } + + const size_t simulcast_idx = + static_cast(configurations_[i].simulcast_idx); + if (frame_types != nullptr && simulcast_idx < frame_types->size()) { + frame_types_to_send[i] = (*frame_types)[simulcast_idx]; + } + if (frame_types_to_send[i] != VideoFrameType::kEmptyFrame) { + ++num_layers_to_send; + } + } + // Encode image for each layer. for (size_t i = 0; i < encoders_.size(); ++i) { // EncodeFrame input. @@ -515,23 +539,15 @@ int32_t H264EncoderImpl::Encode( configurations_[i].height, libyuv::kFilterBox); } - if (!configurations_[i].sending) { + if (frame_types_to_send[i] == VideoFrameType::kEmptyFrame) { continue; } - if (frame_types != nullptr && i < frame_types->size()) { - // Skip frame? - if ((*frame_types)[i] == VideoFrameType::kEmptyFrame) { - continue; - } - } + // Send a key frame either when this layer is configured to require one // or we have explicitly been asked to. - const size_t simulcast_idx = - static_cast(configurations_[i].simulcast_idx); bool send_key_frame = is_keyframe_needed || - (frame_types && simulcast_idx < frame_types->size() && - (*frame_types)[simulcast_idx] == VideoFrameType::kVideoFrameKey); + frame_types_to_send[i] == VideoFrameType::kVideoFrameKey; if (send_key_frame) { // API doc says ForceIntraFrame(false) does nothing, but calling this // function forces a key frame regardless of the `bIDR` argument's value. @@ -559,33 +575,36 @@ int32_t H264EncoderImpl::Encode( return WEBRTC_VIDEO_CODEC_ERROR; } - encoded_images_[i]._encodedWidth = configurations_[i].width; - encoded_images_[i]._encodedHeight = configurations_[i].height; - encoded_images_[i].SetRtpTimestamp(input_frame.rtp_timestamp()); - encoded_images_[i].SetColorSpace(input_frame.color_space()); - encoded_images_[i]._frameType = ConvertToVideoFrameType(info.eFrameType); - encoded_images_[i].SetSimulcastIndex(configurations_[i].simulcast_idx); + EncodedImage& encoded_image = encoded_images_[i]; + + encoded_image._encodedWidth = configurations_[i].width; + encoded_image._encodedHeight = configurations_[i].height; + encoded_image.SetRtpTimestamp(input_frame.rtp_timestamp()); + encoded_image.SetColorSpace(input_frame.color_space()); + encoded_image.set_frame_type(ConvertToVideoFrameType(info.eFrameType)); + encoded_image.SetSimulcastIndex(configurations_[i].simulcast_idx); + --num_layers_to_send; + encoded_image.set_end_of_temporal_unit(num_layers_to_send == 0); // Split encoded image up into fragments. This also updates // `encoded_image_`. - RtpFragmentize(&encoded_images_[i], &info); + RtpFragmentize(&encoded_image, &info); // Encoder can skip frames to save bandwidth in which case - // `encoded_images_[i]._length` == 0. - if (encoded_images_[i].size() > 0) { + // `encoded_image._length` == 0. + if (encoded_image.size() > 0) { // Parse QP. - h264_bitstream_parser_.ParseBitstream(encoded_images_[i]); - encoded_images_[i].qp_ = - h264_bitstream_parser_.GetLastSliceQp().value_or(-1); + h264_bitstream_parser_.ParseBitstream(encoded_image); + encoded_image.qp_ = h264_bitstream_parser_.GetLastSliceQp().value_or(-1); #ifdef WEBRTC_ENCODER_PSNR_STATS if (calculate_psnr) { - encoded_images_[i].set_psnr(EncodedImage::Psnr({ + encoded_image.set_psnr(EncodedImage::Psnr({ .y = info.sLayerInfo[info.iLayerNum - 1].rPsnr[0], .u = info.sLayerInfo[info.iLayerNum - 1].rPsnr[1], .v = info.sLayerInfo[info.iLayerNum - 1].rPsnr[2], })); } else { - encoded_images_[i].set_psnr(std::nullopt); + encoded_image.set_psnr(std::nullopt); } #endif @@ -604,7 +623,7 @@ int32_t H264EncoderImpl::Encode( codec_specific.codecSpecific.H264.base_layer_sync = tid > 0 && tid < tl0sync_limit_[i]; if (svc_controllers_[i]) { - if (encoded_images_[i]._frameType == VideoFrameType::kVideoFrameKey) { + if (encoded_images_[i].IsKey()) { // Reset the ScalableVideoController on key frame // to reset the expected dependency structure. layer_frames = @@ -620,7 +639,7 @@ int32_t H264EncoderImpl::Encode( << ", expected " << layer_frames[0].TemporalId() << "."; continue; } - encoded_images_[i].SetTemporalIndex(tid); + encoded_image.SetTemporalIndex(tid); } if (codec_specific.codecSpecific.H264.base_layer_sync) { tl0sync_limit_[i] = tid; @@ -632,17 +651,21 @@ int32_t H264EncoderImpl::Encode( if (svc_controllers_[i]) { codec_specific.generic_frame_info = svc_controllers_[i]->OnEncodeDone(layer_frames[0]); - if (encoded_images_[i]._frameType == VideoFrameType::kVideoFrameKey && + if (encoded_images_[i].IsKey() && codec_specific.generic_frame_info.has_value()) { codec_specific.template_structure = svc_controllers_[i]->DependencyStructure(); } codec_specific.scalability_mode = scalability_modes_[i]; } - encoded_image_callback_->OnEncodedImage(encoded_images_[i], - &codec_specific); + encoded_image_callback_->OnEncodedImage(encoded_image, &codec_specific); + } else { + encoded_image_callback_->OnFrameDropped( + encoded_image.RtpTimestamp(), *encoded_image.SimulcastIndex(), + *encoded_image.is_end_of_temporal_unit()); } } + RTC_DCHECK_EQ(num_layers_to_send, 0); return WEBRTC_VIDEO_CODEC_OK; } diff --git a/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc b/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc index ae9ed9b8518..c4f0dad975b 100644 --- a/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc +++ b/modules/video_coding/codecs/h264/h264_encoder_impl_unittest.cc @@ -11,14 +11,30 @@ #include "modules/video_coding/codecs/h264/h264_encoder_impl.h" +#include +#include + #include "api/environment/environment_factory.h" +#include "api/make_ref_counted.h" +#include "api/scoped_refptr.h" +#include "api/test/create_frame_generator.h" +#include "api/test/frame_generator_interface.h" +#include "api/test/mock_video_encoder.h" +#include "api/video/i420_buffer.h" #include "api/video/video_codec_type.h" +#include "api/video/video_frame.h" +#include "api/video/video_frame_buffer.h" #include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_encoder.h" #include "modules/video_coding/codecs/h264/include/h264_globals.h" #include "modules/video_coding/include/video_error_codes.h" +#include "test/gmock.h" #include "test/gtest.h" +using ::testing::AnyNumber; +using ::testing::AtLeast; +using ::testing::Return; + namespace webrtc { namespace { @@ -77,6 +93,116 @@ TEST(H264EncoderImplTest, CanInitializeWithSingleNalUnitModeExplicitly) { encoder.PacketizationModeForTesting()); } +TEST(H264EncoderImplTest, OnFrameDropped) { + H264EncoderImpl encoder(CreateEnvironment(), {}); + VideoCodec codec_settings; + SetDefaultSettings(&codec_settings); + // Set a very low bitrate to force frame drops. + codec_settings.startBitrate = 1; + codec_settings.maxBitrate = 1; + + MockEncodedImageCallback callback; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.InitEncode(&codec_settings, kSettings)); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.RegisterEncodeCompleteCallback(&callback)); + + auto frame_generator = test::CreateSquareFrameGenerator( + codec_settings.width, codec_settings.height, + test::FrameGeneratorInterface::OutputType::kI420, + /*num_squares=*/std::nullopt); + + // We need to encode enough frames to trigger rate control dropping. + // The exact number might vary, but a loop should catch it. + const int kNumFramesToEncode = 30; + + EXPECT_CALL(callback, OnEncodedImage) + .Times(AnyNumber()) + .WillRepeatedly(Return( + EncodedImageCallback::Result(EncodedImageCallback::Result::OK))); + + EXPECT_CALL(callback, OnFrameDropped) + .Times(AtLeast(1)) + .WillRepeatedly([&](uint32_t rtp_timestamp, int spatial_id, + bool is_end_of_temporal_unit) { + // Verify arguments match what we expect for a single layer drop. + EXPECT_EQ(spatial_id, 0); // H264 encoder usually uses simlucast index + // as spatial_id, or just 0 for single layer. + EXPECT_TRUE(is_end_of_temporal_unit); + }); + + for (int i = 0; i < kNumFramesToEncode; ++i) { + VideoFrame frame = + VideoFrame::Builder() + .set_video_frame_buffer(frame_generator->NextFrame().buffer) + .set_rtp_timestamp(i * 90000 / codec_settings.maxFramerate) + .build(); + encoder.Encode(frame, nullptr); + } +} + +TEST(H264EncoderImplTest, RejectsI420FramesWithUnequalChromaStrides) { + H264EncoderImpl encoder(CreateEnvironment(), {}); + VideoCodec codec_settings; + SetDefaultSettings(&codec_settings); + MockEncodedImageCallback callback; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.InitEncode(&codec_settings, kSettings)); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.RegisterEncodeCompleteCallback(&callback)); + // Create a VideoFrame where the U and V strides are different. + auto buffer = I420Buffer::Create( + /*width=*/codec_settings.width, + /*height=*/codec_settings.height, + /*stride_y=*/codec_settings.width, + /*stride_u=*/(codec_settings.width + 1) / 2, + /*stride_v=*/(codec_settings.width + 1) / 2 + 1); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ENCODER_FAILURE, encoder.Encode(frame, nullptr)); +} + +TEST(H264EncoderImplTest, RejectsNativeFramesWithUnequalChromaStrides) { + H264EncoderImpl encoder(CreateEnvironment(), {}); + VideoCodec codec_settings; + SetDefaultSettings(&codec_settings); + MockEncodedImageCallback callback; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.InitEncode(&codec_settings, kSettings)); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder.RegisterEncodeCompleteCallback(&callback)); + + class FakeNativeBuffer : public VideoFrameBuffer { + public: + FakeNativeBuffer(int width, int height) : width_(width), height_(height) {} + Type type() const override { return Type::kNative; } + int width() const override { return width_; } + int height() const override { return height_; } + scoped_refptr ToI420() override { + return I420Buffer::Create(width_, height_, width_, (width_ + 1) / 2, + (width_ + 1) / 2 + 1); + } + + private: + int width_; + int height_; + }; + + auto buffer = make_ref_counted(codec_settings.width, + codec_settings.height); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ENCODER_FAILURE, encoder.Encode(frame, nullptr)); +} + } // anonymous namespace } // namespace webrtc diff --git a/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc b/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc index 3d2dd3dbc72..598e73c2067 100644 --- a/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc +++ b/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc @@ -60,7 +60,7 @@ TEST_F(TestH264Impl, MAYBE_EncodeDecode) { CodecSpecificInfo codec_specific_info; ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info)); // First frame should be a key frame. - encoded_frame._frameType = VideoFrameType::kVideoFrameKey; + encoded_frame.set_frame_type(VideoFrameType::kVideoFrameKey); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, 0)); std::unique_ptr decoded_frame; std::optional decoded_qp; @@ -85,7 +85,7 @@ TEST_F(TestH264Impl, MAYBE_DecodedQpEqualsEncodedQp) { CodecSpecificInfo codec_specific_info; ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info)); // First frame should be a key frame. - encoded_frame._frameType = VideoFrameType::kVideoFrameKey; + encoded_frame.set_frame_type(VideoFrameType::kVideoFrameKey); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, 0)); std::unique_ptr decoded_frame; std::optional decoded_qp; diff --git a/modules/video_coding/codecs/test/encoded_video_frame_producer.cc b/modules/video_coding/codecs/test/encoded_video_frame_producer.cc index 312605c1dc1..289b61a792f 100644 --- a/modules/video_coding/codecs/test/encoded_video_frame_producer.cc +++ b/modules/video_coding/codecs/test/encoded_video_frame_producer.cc @@ -42,6 +42,10 @@ class EncoderCallback : public EncodedImageCallback { return Result(Result::Error::OK); } + void OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) override {} + std::vector& output_frames_; }; diff --git a/modules/video_coding/codecs/test/video_codec_test.cc b/modules/video_coding/codecs/test/video_codec_test.cc index aee9bd2300a..9cbfd71df7b 100644 --- a/modules/video_coding/codecs/test/video_codec_test.cc +++ b/modules/video_coding/codecs/test/video_codec_test.cc @@ -292,6 +292,7 @@ std::unique_ptr RunEncodeTest( if (absl::GetFlag(FLAGS_dump_encoder_output)) { encoder_settings.encoder_output_base_path = output_path + "_enc_output"; } + encoder_settings.num_cores = absl::GetFlag(FLAGS_num_cores); return VideoCodecTester::RunEncodeTest(env, source_settings, encoder_factory.get(), diff --git a/modules/video_coding/codecs/test/video_codec_unittest.h b/modules/video_coding/codecs/test/video_codec_unittest.h index fea4ba0d729..e24ecf0046e 100644 --- a/modules/video_coding/codecs/test/video_codec_unittest.h +++ b/modules/video_coding/codecs/test/video_codec_unittest.h @@ -56,6 +56,10 @@ class VideoCodecUnitTest : public ::testing::Test { const EncodedImage& frame, const CodecSpecificInfo* codec_specific_info) override; + void OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) override {} + private: VideoCodecUnitTest* const test_; }; diff --git a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc index 66e0c2f8de1..5c60bdbb461 100644 --- a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc +++ b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc @@ -24,7 +24,6 @@ #include "absl/strings/match.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment_factory.h" #include "api/field_trials_view.h" #include "api/make_ref_counted.h" @@ -37,7 +36,6 @@ #include "api/video/resolution.h" #include "api/video/video_codec_constants.h" #include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" #include "api/video_codecs/h264_profile_level_id.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/simulcast_stream.h" @@ -68,7 +66,6 @@ #include "rtc_base/cpu_time.h" #include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" -#include "rtc_base/system/file_wrapper.h" #include "rtc_base/system_time.h" #include "rtc_base/task_queue_for_test.h" #include "rtc_base/thread.h" @@ -118,15 +115,14 @@ void ConfigureSimulcast(const FieldTrialsView& trials, } } -void ConfigureSvc(VideoCodec* codec_settings) { - RTC_CHECK_EQ(kVideoCodecVP9, codec_settings->codecType); - +void ConfigureSvc(VideoCodec* codec_settings, + size_t num_spatial_layers, + size_t num_temporal_layers) { const std::vector layers = GetSvcConfig( codec_settings->width, codec_settings->height, kDefaultMaxFramerateFps, - /*first_active_layer=*/0, codec_settings->VP9()->numberOfSpatialLayers, - codec_settings->VP9()->numberOfTemporalLayers, + /*first_active_layer=*/0, num_spatial_layers, num_temporal_layers, /* is_screen_sharing = */ false); - ASSERT_EQ(codec_settings->VP9()->numberOfSpatialLayers, layers.size()) + ASSERT_EQ(num_spatial_layers, layers.size()) << "GetSvcConfig returned fewer spatial layers than configured."; for (size_t i = 0; i < layers.size(); ++i) { @@ -290,9 +286,8 @@ void VideoCodecTestFixtureImpl::Config::SetCodecSettings( if (codec_settings.numberOfSimulcastStreams > 1) { ConfigureSimulcast(field_trials, &codec_settings); - } else if (codec_settings.codecType == kVideoCodecVP9 && - codec_settings.VP9()->numberOfSpatialLayers > 1) { - ConfigureSvc(&codec_settings); + } else if (num_spatial_layers > 1 || num_temporal_layers > 1) { + ConfigureSvc(&codec_settings, num_spatial_layers, num_temporal_layers); } } @@ -410,11 +405,11 @@ void VideoCodecTestFixtureImpl::H264KeyframeChecker::CheckEncodedFrame( contains_idr = true; } } - if (encoded_frame._frameType == VideoFrameType::kVideoFrameKey) { + if (encoded_frame.IsKey()) { EXPECT_TRUE(contains_sps) << "Keyframe should contain SPS."; EXPECT_TRUE(contains_pps) << "Keyframe should contain PPS."; EXPECT_TRUE(contains_idr) << "Keyframe should contain IDR."; - } else if (encoded_frame._frameType == VideoFrameType::kVideoFrameDelta) { + } else if (encoded_frame.IsDelta()) { EXPECT_FALSE(contains_sps) << "Delta frame should not contain SPS."; EXPECT_FALSE(contains_pps) << "Delta frame should not contain PPS."; EXPECT_FALSE(contains_idr) << "Delta frame should not contain IDR."; @@ -820,12 +815,10 @@ bool VideoCodecTestFixtureImpl::SetUpAndInitObjects( const std::string output_file_path = output_filename_base + "tl" + std::to_string(temporal_idx) + ".ivf"; - FileWrapper ivf_file = FileWrapper::OpenWriteOnly(output_file_path); - const VideoProcessor::LayerKey layer_key(simulcast_svc_idx, temporal_idx); encoded_frame_writers_[layer_key] = - IvfFileWriter::Wrap(std::move(ivf_file), /*byte_limit=*/0); + IvfFileWriter::Wrap(output_file_path, /*byte_limit=*/0); } } diff --git a/modules/video_coding/codecs/test/videocodec_test_libvpx.cc b/modules/video_coding/codecs/test/videocodec_test_libvpx.cc index cf67e5d3b79..3ed183cf7d8 100644 --- a/modules/video_coding/codecs/test/videocodec_test_libvpx.cc +++ b/modules/video_coding/codecs/test/videocodec_test_libvpx.cc @@ -304,8 +304,28 @@ TEST(VideoCodecTestLibvpx, VeryLowBitrateVP9) { fixture->RunTest(rate_profiles, &rc_thresholds, &quality_thresholds, nullptr); } -// TODO(marpan): Add temporal layer test for VP9, once changes are in -// vp9 wrapper for this. +TEST(VideoCodecTestLibvpx, Vp9L1T3) { + auto config = CreateConfig(); + config.SetCodecSettings(kVp9CodecName, 1, 1, /*num_temporal_layers=*/3, true, + true, false, kCifWidth, kCifHeight); + + auto fixture = CreateVideoCodecTestFixture(config); + + std::vector rc_thresholds = { + {.max_avg_bitrate_mismatch_percent = 60, + .max_time_to_reach_target_bitrate_sec = 3, + .max_avg_framerate_mismatch_percent = 75, + .max_avg_buffer_level_sec = 1, + .max_max_key_frame_delay_sec = 0.5, + .max_max_delta_frame_delay_sec = 0.4, + .max_num_spatial_resizes = 2, + .max_num_key_frames = 1}}; + + std::vector rate_profiles = { + {.target_kbps = 500, .input_fps = 30, .frame_num = 0}}; + + fixture->RunTest(rate_profiles, &rc_thresholds, nullptr, nullptr); +} #endif // defined(RTC_ENABLE_VP9) diff --git a/modules/video_coding/codecs/test/videoprocessor.cc b/modules/video_coding/codecs/test/videoprocessor.cc index 5a89cad7694..c6c309b572e 100644 --- a/modules/video_coding/codecs/test/videoprocessor.cc +++ b/modules/video_coding/codecs/test/videoprocessor.cc @@ -440,7 +440,7 @@ void VideoProcessor::FrameEncoded(const EncodedImage& encoded_image, bitrate_allocation.GetTemporalLayerSum(stream_idx, temporal_idx) / 1000; frame_stat->target_framerate_fps = target_rate.input_fps; frame_stat->length_bytes = encoded_image.size(); - frame_stat->frame_type = encoded_image._frameType; + frame_stat->frame_type = encoded_image.frame_type(); frame_stat->temporal_idx = temporal_idx; frame_stat->max_nalu_size_bytes = GetMaxNaluSizeBytes(encoded_image, config_); frame_stat->qp = encoded_image.qp_; @@ -695,7 +695,7 @@ const EncodedImage* VideoProcessor::BuildAndStoreSuperframe( EncodedImage copied_image = encoded_image; copied_image.SetEncodedData(buffer); if (base_image.size()) - copied_image._frameType = base_image._frameType; + copied_image.set_frame_type(base_image.frame_type()); // Replace previous EncodedImage for this spatial layer. merged_encoded_frames_.at(spatial_idx) = std::move(copied_image); diff --git a/modules/video_coding/codecs/test/videoprocessor.h b/modules/video_coding/codecs/test/videoprocessor.h index 200d5bb1543..ed64172008a 100644 --- a/modules/video_coding/codecs/test/videoprocessor.h +++ b/modules/video_coding/codecs/test/videoprocessor.h @@ -120,6 +120,10 @@ class VideoProcessor { return Result(Result::OK, 0); } + void OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) override {} + private: VideoProcessor* const video_processor_; TaskQueueBase* const task_queue_; diff --git a/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc b/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc index b50db65f96f..b65be28140b 100644 --- a/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc +++ b/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc @@ -441,7 +441,7 @@ class TemporalLayersReferenceTest : public TemporalLayersTest, INSTANTIATE_TEST_SUITE_P(DefaultTemporalLayersTest, TemporalLayersReferenceTest, - ::testing::Range(1, kMaxTemporalStreams + 1)); + ::testing::Range(1, int{kMaxTemporalStreams} + 1)); TEST_P(TemporalLayersReferenceTest, ValidFrameConfigs) { const int num_layers = GetParam(); diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc b/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc index 5e51c316cc7..ee52e72680f 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_decoder.cc @@ -27,7 +27,6 @@ #include "api/video/i420_buffer.h" #include "api/video/video_frame.h" #include "api/video/video_frame_buffer.h" -#include "api/video/video_frame_type.h" #include "api/video_codecs/video_decoder.h" #include "modules/video_coding/codecs/vp8/include/vp8.h" #include "modules/video_coding/include/video_error_codes.h" @@ -240,8 +239,9 @@ int LibvpxVp8Decoder::Decode(const EncodedImage& input_image, // Always start with a complete key frame. if (key_frame_required_) { - if (input_image._frameType != VideoFrameType::kVideoFrameKey) + if (!input_image.IsKey()) { return WEBRTC_VIDEO_CODEC_ERROR; + } key_frame_required_ = false; } diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc index 19c463cf3b6..8fb8c972f2d 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc @@ -1248,7 +1248,8 @@ int LibvpxVp8Encoder::GetEncodedPartitions(const VideoFrame& input_image, vpx_codec_iter_t iter = nullptr; encoded_images_[encoder_idx].set_size(0); encoded_images_[encoder_idx].set_psnr(std::nullopt); - encoded_images_[encoder_idx]._frameType = VideoFrameType::kVideoFrameDelta; + encoded_images_[encoder_idx].set_frame_type( + VideoFrameType::kVideoFrameDelta); CodecSpecificInfo codec_specific; const vpx_codec_cx_pkt_t* pkt = nullptr; @@ -1289,8 +1290,8 @@ int LibvpxVp8Encoder::GetEncodedPartitions(const VideoFrame& input_image, (pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) { // check if encoded frame is a key frame if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { - encoded_images_[encoder_idx]._frameType = - VideoFrameType::kVideoFrameKey; + encoded_images_[encoder_idx].set_frame_type( + VideoFrameType::kVideoFrameKey); } encoded_images_[encoder_idx].SetEncodedData(buffer); encoded_images_[encoder_idx].set_size(encoded_pos); @@ -1328,8 +1329,8 @@ int LibvpxVp8Encoder::GetEncodedPartitions(const VideoFrame& input_image, encoded_images_[encoder_idx].set_corruption_detection_filter_settings( corruption_detection_settings_generator_->OnFrame( - encoded_images_[encoder_idx].FrameType() == - VideoFrameType::kVideoFrameKey, + encoded_images_[encoder_idx].IsKey(), + qp_128)); encoded_complete_callback_->OnEncodedImage(encoded_images_[encoder_idx], @@ -1492,6 +1493,13 @@ std::vector> LibvpxVp8Encoder::PrepareBuffers( << " image to I420. Can't encode frame."; return {}; } + + // TODO: crbug.com/492213293 - Remove once the root cause is fixed. + if (converted_buffer->StrideU() != converted_buffer->StrideV()) { + RTC_LOG(LS_ERROR) << "Libvpx requires the U and V strides to be equal."; + return {}; + } + RTC_CHECK(converted_buffer->type() == VideoFrameBuffer::Type::kI420 || converted_buffer->type() == VideoFrameBuffer::Type::kI420A); @@ -1563,6 +1571,21 @@ std::vector> LibvpxVp8Encoder::PrepareBuffers( SetRawImagePlanes(&raw_images_[i], scaled_buffer.get()); prepared_buffers.push_back(scaled_buffer); } + + // TODO: crbug.com/492213293 - Remove once the root cause is fixed. + for (const scoped_refptr& prepared_buffer : + prepared_buffers) { + if (prepared_buffer->type() == VideoFrameBuffer::Type::kI420 || + prepared_buffer->type() == VideoFrameBuffer::Type::kI420A) { + auto i420_buffer = prepared_buffer->GetI420(); + RTC_DCHECK(i420_buffer); + if (i420_buffer->StrideU() != i420_buffer->StrideV()) { + RTC_LOG(LS_ERROR) << "Libvpx requires the U and V strides to be equal."; + return {}; + } + } + } + return prepared_buffers; } diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc index 0b3cc6cea7d..7033b4ecb4b 100644 --- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc +++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc @@ -22,6 +22,7 @@ #include "absl/memory/memory.h" #include "api/environment/environment_factory.h" #include "api/field_trials.h" +#include "api/make_ref_counted.h" #include "api/scoped_refptr.h" #include "api/test/create_frame_generator.h" #include "api/test/frame_generator_interface.h" @@ -30,6 +31,7 @@ #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "api/video/encoded_image.h" +#include "api/video/i420_buffer.h" #include "api/video/render_resolution.h" #include "api/video/video_bitrate_allocation.h" #include "api/video/video_codec_constants.h" @@ -229,6 +231,55 @@ TEST_F(TestVp8Impl, EncodeFrameAndRelease) { encoder_->Encode(NextInputFrame(), nullptr)); } +TEST_F(TestVp8Impl, RejectsI420FramesWithUnequalChromaStrides) { + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kSettings)); + + auto buffer = I420Buffer::Create( + /*width=*/kWidth, + /*height=*/kHeight, + /*stride_y=*/kWidth, + /*stride_u=*/(kWidth + 1) / 2, + /*stride_v=*/(kWidth + 1) / 2 + 1); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, encoder_->Encode(frame, nullptr)); +} + +TEST_F(TestVp8Impl, RejectsNativeFramesWithUnequalChromaStrides) { + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kSettings)); + + class FakeNativeBuffer : public VideoFrameBuffer { + public: + FakeNativeBuffer(int width, int height) : width_(width), height_(height) {} + Type type() const override { return Type::kNative; } + int width() const override { return width_; } + int height() const override { return height_; } + scoped_refptr ToI420() override { + return I420Buffer::Create(width_, height_, width_, (width_ + 1) / 2, + (width_ + 1) / 2 + 1); + } + + private: + int width_; + int height_; + }; + + auto buffer = make_ref_counted(kWidth, kHeight); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, encoder_->Encode(frame, nullptr)); +} + TEST_F(TestVp8Impl, EncodeNv12FrameSimulcast) { EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release()); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, @@ -308,7 +359,7 @@ TEST_F(TestVp8Impl, DecodedQpEqualsEncodedQp) { EncodeAndWaitForFrame(input_frame, &encoded_frame, &codec_specific_info); // First frame should be a key frame. - encoded_frame._frameType = VideoFrameType::kVideoFrameKey; + encoded_frame.set_frame_type(VideoFrameType::kVideoFrameKey); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, -1)); std::unique_ptr decoded_frame; std::optional decoded_qp; @@ -521,7 +572,7 @@ TEST_F(TestVp8Impl, MAYBE_AlignedStrideEncodeDecode) { EncodeAndWaitForFrame(input_frame, &encoded_frame, &codec_specific_info); // First frame should be a key frame. - encoded_frame._frameType = VideoFrameType::kVideoFrameKey; + encoded_frame.set_frame_type(VideoFrameType::kVideoFrameKey); encoded_frame.ntp_time_ms_ = kTestNtpTimeMs; EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, -1)); @@ -949,6 +1000,10 @@ TEST_P(TestVp8ImplWithMaxFrameDropTrial, EnforcesMaxFrameDropInterval) { return Result(Result::Error::OK); } + void OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) override {} + private: std::vector callback_deltas_; Timestamp last_callback_; diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc index 2dce0d1033c..0dafd3e2019 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc @@ -17,15 +17,14 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/video/color_space.h" #include "api/video/encoded_image.h" #include "api/video/render_resolution.h" #include "api/video/video_frame.h" #include "api/video/video_frame_buffer.h" -#include "api/video/video_frame_type.h" #include "api/video_codecs/video_decoder.h" #include "common_video/include/video_frame_buffer.h" #include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h" @@ -207,10 +206,10 @@ int LibvpxVp9Decoder::Decode(const EncodedImage& input_image, return WEBRTC_VIDEO_CODEC_UNINITIALIZED; } - if (input_image._frameType == VideoFrameType::kVideoFrameKey) { + if (input_image.IsKey()) { std::optional frame_info = ParseUncompressedVp9Header( - MakeArrayView(input_image.data(), input_image.size())); + std::span(input_image.data(), input_image.size())); if (frame_info) { RenderResolution frame_resolution(frame_info->frame_width, frame_info->frame_height); @@ -231,8 +230,9 @@ int LibvpxVp9Decoder::Decode(const EncodedImage& input_image, // Always start with a complete key frame. if (key_frame_required_) { - if (input_image._frameType != VideoFrameType::kVideoFrameKey) + if (!input_image.IsKey()) { return WEBRTC_VIDEO_CODEC_ERROR; + } key_frame_required_ = false; } vpx_codec_iter_t iter = nullptr; diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc index 7c7ca9fd1ed..d757f1aa11f 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc @@ -21,12 +21,12 @@ #include #include #include +#include #include #include #include "absl/algorithm/container.h" #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/fec_controller_override.h" #include "api/field_trials_view.h" @@ -203,7 +203,7 @@ std::unique_ptr CreateVp9ScalabilityStructure( } vpx_svc_ref_frame_config_t Vp9References( - ArrayView layers) { + std::span layers) { vpx_svc_ref_frame_config_t ref_config = {}; for (const ScalableVideoController::LayerFrameConfig& layer_frame : layers) { const auto& buffers = layer_frame.Buffers(); @@ -1215,6 +1215,13 @@ int LibvpxVp9Encoder::Encode(const VideoFrame& input_image, i010_buffer = i010_copy.get(); } } + + // TODO: crbug.com/492213293 - Remove once the root cause is fixed. + if (i010_buffer->StrideU() != i010_buffer->StrideV()) { + RTC_LOG(LS_ERROR) << "Libvpx requires the U and V strides to be equal."; + return WEBRTC_VIDEO_CODEC_ERROR; + } + MaybeRewrapRawWithFormat(VPX_IMG_FMT_I42016, i010_buffer->width(), i010_buffer->height()); raw_->planes[VPX_PLANE_Y] = const_cast( @@ -1734,14 +1741,29 @@ vpx_svc_ref_frame_config_t LibvpxVp9Encoder::SetReferences( void LibvpxVp9Encoder::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { RTC_DCHECK_EQ(pkt->kind, VPX_CODEC_CX_FRAME_PKT); + vpx_svc_layer_id_t layer_id = {.spatial_layer_id = 0}; + libvpx_->codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &layer_id); + + // This encoder doesn't mark the last encoded frame with end_of_picture - + // meaning that if per-layer frame dropping is enabled and the last layer + // drops the frame, there will be no encoded image with end_of_picture set. + // In those cases the receiver will have to figure that out based on the + // absence of a picture when the next frame arrives. + // We should consider changing this behavior - but that necessitates buffering + // and so introduces latency. If FULL_SUPERFRAME_DROP is used, this is a non- + // issue. + // Due to this behavior, end_of_temporal_unit is the same thing as + // end_of_picture. + const bool end_of_picture = + layer_id.spatial_layer_id + 1 == num_active_spatial_layers_; + if (pkt->data.frame.sz == 0) { - // Ignore dropped frame. + encoded_complete_callback_->OnFrameDropped(input_image_->rtp_timestamp(), + layer_id.spatial_layer_id, + end_of_picture); return; } - vpx_svc_layer_id_t layer_id = {.spatial_layer_id = 0}; - libvpx_->codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &layer_id); - encoded_image_.SetEncodedData(EncodedImageBuffer::Create( static_cast(pkt->data.frame.buf), pkt->data.frame.sz)); @@ -1765,9 +1787,9 @@ void LibvpxVp9Encoder::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { RTC_DCHECK(is_key_frame || !force_key_frame_); // Check if encoded frame is a key frame. - encoded_image_._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image_.set_frame_type(VideoFrameType::kVideoFrameDelta); if (is_key_frame) { - encoded_image_._frameType = VideoFrameType::kVideoFrameKey; + encoded_image_.set_frame_type(VideoFrameType::kVideoFrameKey); force_key_frame_ = false; } @@ -1802,8 +1824,7 @@ void LibvpxVp9Encoder::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { } } - const bool end_of_picture = encoded_image_.SpatialIndex().value_or(0) + 1 == - num_active_spatial_layers_; + encoded_image_.set_end_of_temporal_unit(end_of_picture); DeliverBufferedFrame(end_of_picture); } @@ -2145,6 +2166,13 @@ scoped_refptr LibvpxVp9Encoder::PrepareBufferForProfile0( mapped_buffer->height()); const I420BufferInterface* i420_buffer = mapped_buffer->GetI420(); RTC_DCHECK(i420_buffer); + + // TODO: crbug.com/492213293 - Remove once the root cause is fixed. + if (i420_buffer->StrideU() != i420_buffer->StrideV()) { + RTC_LOG(LS_ERROR) << "Libvpx requires the U and V strides to be equal."; + return {}; + } + raw_->planes[VPX_PLANE_Y] = const_cast(i420_buffer->DataY()); raw_->planes[VPX_PLANE_U] = const_cast(i420_buffer->DataU()); raw_->planes[VPX_PLANE_V] = const_cast(i420_buffer->DataV()); diff --git a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc index fc392cfd009..30884f44e4e 100644 --- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc +++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc @@ -14,15 +14,16 @@ #include #include #include +#include #include #include #include #include "absl/container/inlined_vector.h" #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/environment/environment_factory.h" #include "api/field_trials.h" +#include "api/make_ref_counted.h" #include "api/scoped_refptr.h" #include "api/test/create_frame_generator.h" #include "api/test/frame_generator_interface.h" @@ -31,6 +32,7 @@ #include "api/units/timestamp.h" #include "api/video/color_space.h" #include "api/video/encoded_image.h" +#include "api/video/i420_buffer.h" #include "api/video/video_bitrate_allocation.h" #include "api/video/video_codec_constants.h" #include "api/video/video_codec_type.h" @@ -77,12 +79,14 @@ using ::testing::AllOf; using ::testing::An; using ::testing::AnyNumber; using ::testing::ByRef; +using ::testing::Combine; using ::testing::DoAll; using ::testing::Each; using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::Field; using ::testing::IsEmpty; +using ::testing::Matcher; using ::testing::Mock; using ::testing::NiceMock; using ::testing::Return; @@ -92,6 +96,7 @@ using ::testing::SetArgPointee; using ::testing::SizeIs; using ::testing::TypedEq; using ::testing::UnorderedElementsAreArray; +using ::testing::Values; using ::testing::WithArg; using EncoderInfo = VideoEncoder::EncoderInfo; using FramerateFractions = absl::InlinedVector; @@ -175,7 +180,7 @@ TEST_P(TestVp9ImplForPixelFormat, EncodeDecode) { CodecSpecificInfo codec_specific_info; ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info)); // First frame should be a key frame. - encoded_frame._frameType = VideoFrameType::kVideoFrameKey; + encoded_frame.set_frame_type(VideoFrameType::kVideoFrameKey); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, 0)); std::unique_ptr decoded_frame; std::optional decoded_qp; @@ -243,7 +248,7 @@ TEST_P(TestVp9ImplForPixelFormat, DecodedQpEqualsEncodedQp) { CodecSpecificInfo codec_specific_info; ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info)); // First frame should be a key frame. - encoded_frame._frameType = VideoFrameType::kVideoFrameKey; + encoded_frame.set_frame_type(VideoFrameType::kVideoFrameKey); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, 0)); std::unique_ptr decoded_frame; std::optional decoded_qp; @@ -266,6 +271,55 @@ TEST_P(TestVp9ImplForPixelFormat, CheckPresentationTimestamp) { encoded_frame.PresentationTimestamp()->us()); } +TEST_F(TestVp9Impl, RejectsI420FramesWithUnequalChromaStrides) { + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kSettings)); + + auto buffer = I420Buffer::Create( + /*width=*/kWidth, + /*height=*/kHeight, + /*stride_y=*/kWidth, + /*stride_u=*/(kWidth + 1) / 2, + /*stride_v=*/(kWidth + 1) / 2 + 1); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, encoder_->Encode(frame, nullptr)); +} + +TEST_F(TestVp9Impl, RejectsNativeFramesWithUnequalChromaStrides) { + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kSettings)); + + class FakeNativeBuffer : public VideoFrameBuffer { + public: + FakeNativeBuffer(int width, int height) : width_(width), height_(height) {} + Type type() const override { return Type::kNative; } + int width() const override { return width_; } + int height() const override { return height_; } + scoped_refptr ToI420() override { + return I420Buffer::Create(width_, height_, width_, (width_ + 1) / 2, + (width_ + 1) / 2 + 1); + } + + private: + int width_; + int height_; + }; + + auto buffer = make_ref_counted(kWidth, kHeight); + + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp(0) + .build(); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, encoder_->Encode(frame, nullptr)); +} + TEST_F(TestVp9Impl, SwitchInputPixelFormatsWithoutReconfigure) { EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr)); EncodedImage encoded_frame; @@ -833,7 +887,8 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrame) { codec_specific_info[0].codecSpecific.VP9.ss_data_available; } // No key-frames generated for disabling layers. - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); + EXPECT_EQ(encoded_frame[0].SpatialIndex().value_or(-1), 2); } EXPECT_TRUE(seen_ss_data); @@ -848,7 +903,8 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrame) { std::vector codec_specific_info; ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); // Key-frame should be produced. - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameKey); + EXPECT_TRUE(encoded_frame[0].IsKey()); + EXPECT_EQ(encoded_frame[0].SpatialIndex().value_or(-1), 2); } @@ -860,7 +916,8 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrame) { std::vector encoded_frame; std::vector codec_specific_info; ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); + EXPECT_EQ(encoded_frame[0].SpatialIndex().value_or(-1), 2); } @@ -888,7 +945,7 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrame) { const VideoFrameType expected_type = frame_num == 0 ? VideoFrameType::kVideoFrameKey : VideoFrameType::kVideoFrameDelta; - EXPECT_EQ(encoded_frame[0]._frameType, expected_type); + EXPECT_EQ(encoded_frame[0].frame_type(), expected_type); EXPECT_EQ(encoded_frame[0].SpatialIndex().value_or(-1), 1); EXPECT_EQ(encoded_frame[1].SpatialIndex().value_or(-1), 2); } @@ -917,7 +974,7 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrame) { const VideoFrameType expected_type = frame_num == 0 ? VideoFrameType::kVideoFrameKey : VideoFrameType::kVideoFrameDelta; - EXPECT_EQ(encoded_frame[0]._frameType, expected_type); + EXPECT_EQ(encoded_frame[0].frame_type(), expected_type); } } @@ -980,7 +1037,8 @@ TEST(Vp9ImplTest, DisableEnableBaseLayerWithSvcControllerTriggersKeyFrame) { frames = producer.ForceKeyFrame().SetNumInputFrames(1).Encode(); ASSERT_THAT(frames, SizeIs(1)); // Key-frame should be produced. - EXPECT_EQ(frames[0].encoded_image._frameType, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(frames[0].encoded_image.frame_type(), + VideoFrameType::kVideoFrameKey); ASSERT_TRUE(frames[0].codec_specific_info.template_structure); ASSERT_TRUE(frames[0].codec_specific_info.generic_frame_info); EXPECT_EQ(frames[0].codec_specific_info.generic_frame_info->spatial_id, 2); @@ -988,7 +1046,8 @@ TEST(Vp9ImplTest, DisableEnableBaseLayerWithSvcControllerTriggersKeyFrame) { frames = producer.SetNumInputFrames(num_frames_to_encode).Encode(); ASSERT_THAT(frames, SizeIs(num_frames_to_encode)); for (const auto& frame : frames) { - EXPECT_EQ(frame.encoded_image._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(frame.encoded_image.IsDelta()); + EXPECT_FALSE(frame.codec_specific_info.template_structure); ASSERT_TRUE(frame.codec_specific_info.generic_frame_info); EXPECT_EQ(frame.codec_specific_info.generic_frame_info->spatial_id, 2); @@ -1005,12 +1064,13 @@ TEST(Vp9ImplTest, DisableEnableBaseLayerWithSvcControllerTriggersKeyFrame) { frames = producer.SetNumInputFrames(num_frames_to_encode).Encode(); ASSERT_THAT(frames, SizeIs(num_frames_to_encode * 2)); - EXPECT_EQ(frames[0].encoded_image._frameType, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(frames[0].encoded_image.frame_type(), + VideoFrameType::kVideoFrameKey); EXPECT_TRUE(frames[0].codec_specific_info.template_structure); ASSERT_TRUE(frames[0].codec_specific_info.generic_frame_info); EXPECT_EQ(frames[0].codec_specific_info.generic_frame_info->spatial_id, 1); for (size_t i = 1; i < frames.size(); ++i) { - EXPECT_EQ(frames[i].encoded_image._frameType, + EXPECT_EQ(frames[i].encoded_image.frame_type(), VideoFrameType::kVideoFrameDelta); EXPECT_FALSE(frames[i].codec_specific_info.template_structure); ASSERT_TRUE(frames[i].codec_specific_info.generic_frame_info); @@ -1094,7 +1154,8 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrameForScreenshare) { EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.ss_data_available, frame_num == 0); // No key-frames generated for disabling layers. - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); + EXPECT_EQ(encoded_frame[0].SpatialIndex().value_or(-1), 2); } @@ -1108,7 +1169,7 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrameForScreenshare) { std::vector codec_specific_info; ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); // Key-frame should be produced. - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameKey); + EXPECT_TRUE(encoded_frame[0].IsKey()); // Enable the second layer back. // Allocate high bit rate to avoid frame dropping due to rate control. @@ -1133,7 +1194,7 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrameForScreenshare) { const VideoFrameType expected_type = frame_num == 0 ? VideoFrameType::kVideoFrameKey : VideoFrameType::kVideoFrameDelta; - EXPECT_EQ(encoded_frame[0]._frameType, expected_type); + EXPECT_EQ(encoded_frame[0].frame_type(), expected_type); EXPECT_EQ(encoded_frame[0].SpatialIndex().value_or(-1), 1); EXPECT_EQ(encoded_frame[1].SpatialIndex().value_or(-1), 2); } @@ -1160,7 +1221,7 @@ TEST_F(TestVp9Impl, DisableEnableBaseLayerTriggersKeyFrameForScreenshare) { const VideoFrameType expected_type = frame_num == 0 ? VideoFrameType::kVideoFrameKey : VideoFrameType::kVideoFrameDelta; - EXPECT_EQ(encoded_frame[0]._frameType, expected_type); + EXPECT_EQ(encoded_frame[0].frame_type(), expected_type); } } @@ -1314,19 +1375,17 @@ TEST_F(TestVp9Impl, const bool is_first_upper_layer_frame = (sl_idx > 0 && frame_num == 0); if (is_first_upper_layer_frame) { if (inter_layer_pred == InterLayerPredMode::kOn) { - EXPECT_EQ(encoded_frame[0]._frameType, - VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); } else { - EXPECT_EQ(encoded_frame[0]._frameType, - VideoFrameType::kVideoFrameKey); + EXPECT_TRUE(encoded_frame[0].IsKey()); } + } else if (sl_idx == 0 && frame_num == 0) { - EXPECT_EQ(encoded_frame[0]._frameType, - VideoFrameType::kVideoFrameKey); + EXPECT_TRUE(encoded_frame[0].IsKey()); + } else { for (size_t i = 0; i <= sl_idx; ++i) { - EXPECT_EQ(encoded_frame[i]._frameType, - VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[i].IsDelta()); } } } @@ -1372,8 +1431,7 @@ TEST_F(TestVp9Impl, ASSERT_EQ(codec_specific_info.size(), sl_idx + 1); for (size_t i = 0; i <= sl_idx; ++i) { - const bool is_keyframe = - encoded_frame[0]._frameType == VideoFrameType::kVideoFrameKey; + const bool is_keyframe = encoded_frame[0].IsKey(); const bool is_first_upper_layer_frame = (i == sl_idx && frame_num == 0); // Interframe references are there, unless it's a keyframe, @@ -1439,7 +1497,8 @@ TEST_F(TestVp9Impl, EnablingDisablingUpperLayerInTheSameGof) { EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr)); ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); ASSERT_EQ(codec_specific_info.size(), 1u); - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); + EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.temporal_idx, 1); EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.inter_pic_predicted, true); @@ -1456,7 +1515,8 @@ TEST_F(TestVp9Impl, EnablingDisablingUpperLayerInTheSameGof) { EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr)); ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); ASSERT_EQ(codec_specific_info.size(), 2u); - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); + EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.temporal_idx, 0); EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.inter_pic_predicted, true); EXPECT_EQ(codec_specific_info[1].codecSpecific.VP9.inter_pic_predicted, true); @@ -1513,7 +1573,8 @@ TEST_F(TestVp9Impl, EnablingDisablingUpperLayerAccrossGof) { encoder_->Encode(NextInputFrame(), nullptr)); ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); ASSERT_EQ(codec_specific_info.size(), 1u); - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); + EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.temporal_idx, 1 - i % 2); EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.inter_pic_predicted, true); @@ -1532,7 +1593,8 @@ TEST_F(TestVp9Impl, EnablingDisablingUpperLayerAccrossGof) { EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr)); ASSERT_TRUE(WaitForEncodedFrames(&encoded_frame, &codec_specific_info)); ASSERT_EQ(codec_specific_info.size(), 2u); - EXPECT_EQ(encoded_frame[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frame[0].IsDelta()); + EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.temporal_idx, 0); EXPECT_EQ(codec_specific_info[0].codecSpecific.VP9.inter_pic_predicted, true); EXPECT_EQ(codec_specific_info[1].codecSpecific.VP9.inter_pic_predicted, @@ -1896,7 +1958,7 @@ TEST_F(TestVp9Impl, EncoderInfoWithBitrateLimitsFromFieldTrial) { EXPECT_THAT( encoder_->GetEncoderInfo().resolution_bitrate_limits, - ::testing::ElementsAre( + ElementsAre( VideoEncoder::ResolutionBitrateLimits{123, 11000, 44000, 77000}, VideoEncoder::ResolutionBitrateLimits{456, 22000, 55000, 88000}, VideoEncoder::ResolutionBitrateLimits{789, 33000, 66000, 99000})); @@ -1976,18 +2038,18 @@ TEST_F(TestVp9Impl, EncoderInfoFpsAllocationFlexibleMode) { expected_fps_allocation[1].push_back(EncoderInfo::kMaxFramerateFraction / 2); expected_fps_allocation[2].push_back(EncoderInfo::kMaxFramerateFraction); EXPECT_THAT(encoder_->GetEncoderInfo().fps_allocation, - ::testing::ElementsAreArray(expected_fps_allocation)); + ElementsAreArray(expected_fps_allocation)); // SetRates with current fps does not alter outcome. encoder_->SetRates(rate_params); EXPECT_THAT(encoder_->GetEncoderInfo().fps_allocation, - ::testing::ElementsAreArray(expected_fps_allocation)); + ElementsAreArray(expected_fps_allocation)); // Higher fps than the codec wants, should still not affect outcome. rate_params.framerate_fps *= 2; encoder_->SetRates(rate_params); EXPECT_THAT(encoder_->GetEncoderInfo().fps_allocation, - ::testing::ElementsAreArray(expected_fps_allocation)); + ElementsAreArray(expected_fps_allocation)); } class Vp9ImplWithLayeringTest @@ -2054,7 +2116,7 @@ TEST_P(Vp9ImplWithLayeringTest, FlexibleMode) { if (picture_idx == 0) { EXPECT_EQ(vp9.num_ref_pics, 0) << "Frame " << i; } else { - EXPECT_THAT(MakeArrayView(vp9.p_diff, vp9.num_ref_pics), + EXPECT_THAT(std::span(vp9.p_diff, vp9.num_ref_pics), UnorderedElementsAreArray(gof.pid_diff[gof_idx], gof.num_ref_pics[gof_idx])) << "Frame " << i; @@ -2064,8 +2126,7 @@ TEST_P(Vp9ImplWithLayeringTest, FlexibleMode) { INSTANTIATE_TEST_SUITE_P(All, Vp9ImplWithLayeringTest, - ::testing::Combine(::testing::Values(1, 2, 3), - ::testing::Values(1, 2, 3))); + Combine(Values(1, 2, 3), Values(1, 2, 3))); class TestVp9ImplFrameDropping : public TestVp9Impl { protected: @@ -2211,7 +2272,7 @@ TEST_F(TestVp9ImplProfile2, EncodeDecode) { CodecSpecificInfo codec_specific_info; ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info)); // First frame should be a key frame. - encoded_frame._frameType = VideoFrameType::kVideoFrameKey; + encoded_frame.set_frame_type(VideoFrameType::kVideoFrameKey); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Decode(encoded_frame, 0)); std::unique_ptr decoded_frame; std::optional decoded_qp; @@ -2304,7 +2365,7 @@ TEST_F(TestVp9Impl, ReenablingUpperLayerAfterKFWithInterlayerPredIsEnabled) { encoder_->Encode(NextInputFrame(), &frame_types)); ASSERT_TRUE(WaitForEncodedFrames(&encoded_frames, &codec_specific)); EXPECT_EQ(encoded_frames.size(), num_spatial_layers - 1); - ASSERT_EQ(encoded_frames[0]._frameType, VideoFrameType::kVideoFrameKey); + ASSERT_TRUE(encoded_frames[0].IsKey()); // Re-enable the last layer. bitrate_allocation.SetBitrate( @@ -2318,7 +2379,7 @@ TEST_F(TestVp9Impl, ReenablingUpperLayerAfterKFWithInterlayerPredIsEnabled) { EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Encode(NextInputFrame(), nullptr)); ASSERT_TRUE(WaitForEncodedFrames(&encoded_frames, &codec_specific)); EXPECT_EQ(encoded_frames.size(), num_spatial_layers); - EXPECT_EQ(encoded_frames[0]._frameType, VideoFrameType::kVideoFrameDelta); + EXPECT_TRUE(encoded_frames[0].IsDelta()); } TEST_F(TestVp9Impl, HandlesEmptyDecoderConfigure) { @@ -2331,8 +2392,8 @@ TEST_F(TestVp9Impl, HandlesEmptyDecoderConfigure) { INSTANTIATE_TEST_SUITE_P( TestVp9ImplForPixelFormat, TestVp9ImplForPixelFormat, - ::testing::Values(test::FrameGeneratorInterface::OutputType::kI420, - test::FrameGeneratorInterface::OutputType::kNV12), + Values(test::FrameGeneratorInterface::OutputType::kI420, + test::FrameGeneratorInterface::OutputType::kNV12), [](const auto& info) { return test::FrameGeneratorInterface::OutputTypeToString(info.param); }); @@ -2455,6 +2516,235 @@ TEST_F(TestVp9Impl, ScalesInputToActiveResolution) { settings.maxFramerate)); } +TEST_F(TestVp9Impl, ReportFrameDroppedOnZeroSizePacket) { + // Keep a raw pointer for EXPECT calls and the like. Ownership is otherwise + // passed on to LibvpxVp9Encoder. + auto* const vpx = new NiceMock(); + LibvpxVp9Encoder encoder(CreateEnvironment(), {}, + absl::WrapUnique(vpx)); + + VideoCodec settings = DefaultCodecSettings(); + constexpr int kNumSpatialLayers = 1; + constexpr int kNumTemporalLayers = 1; + ConfigureSvc(settings, kNumSpatialLayers, kNumTemporalLayers); + + // Set up the vpx to return a packet with size 0 (dropped frame). + vpx_image_t img; + ON_CALL(*vpx, img_wrap).WillByDefault(GetWrapImageFunction(&img)); + ON_CALL(*vpx, codec_enc_init).WillByDefault(Return(VPX_CODEC_OK)); + ON_CALL(*vpx, codec_enc_config_default).WillByDefault(Return(VPX_CODEC_OK)); + // Capture the callback and handle Layer ID query. + vpx_codec_priv_output_cx_pkt_cb_pair_t callback_pointer = {}; + EXPECT_CALL(*vpx, codec_control(_, _, Matcher(_))) + .WillRepeatedly([&](vpx_codec_ctx_t*, vp8e_enc_control_id id, + void* param) { + if (id == VP9E_GET_SVC_LAYER_ID) { + vpx_svc_layer_id_t* layer_id = + static_cast(param); + layer_id->spatial_layer_id = 0; + return VPX_CODEC_OK; + } + if (id == VP9E_REGISTER_CX_CALLBACK) { + callback_pointer = + *reinterpret_cast(param); + return VPX_CODEC_OK; + } + return VPX_CODEC_OK; + }); + + // Frame 1: Valid Keyframe. + // Frame 2: Dropped Frame (sz=0). + EXPECT_CALL(*vpx, codec_encode) + .Times(2) + .WillOnce([&](vpx_codec_ctx_t*, const vpx_image_t*, vpx_codec_pts_t, + uint64_t, vpx_enc_frame_flags_t, uint64_t) { + if (callback_pointer.output_cx_pkt) { + vpx_codec_cx_pkt pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.kind = VPX_CODEC_CX_FRAME_PKT; + std::vector data(1000); // Dummy data + pkt.data.frame.buf = data.data(); + pkt.data.frame.sz = data.size(); + pkt.data.frame.flags = VPX_FRAME_IS_KEY; + pkt.data.frame.width[0] = 1280; + pkt.data.frame.height[0] = 720; + callback_pointer.output_cx_pkt(&pkt, callback_pointer.user_priv); + } + return VPX_CODEC_OK; + }) + .WillOnce([&](vpx_codec_ctx_t*, const vpx_image_t*, vpx_codec_pts_t, + uint64_t, vpx_enc_frame_flags_t, uint64_t) { + if (callback_pointer.output_cx_pkt) { + vpx_codec_cx_pkt pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.kind = VPX_CODEC_CX_FRAME_PKT; + pkt.data.frame.sz = 0; // Size 0 implies drop. + callback_pointer.output_cx_pkt(&pkt, callback_pointer.user_priv); + } + return VPX_CODEC_OK; + }); + + MockEncodedImageCallback callback; + encoder.RegisterEncodeCompleteCallback(&callback); + ASSERT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings)); + EXPECT_NE(callback_pointer.output_cx_pkt, nullptr); + + // Expectation for valid frame. + EXPECT_CALL(callback, OnEncodedImage) + .WillOnce([&](const EncodedImage& encoded_image, + const CodecSpecificInfo* codec_specific_info) { + EXPECT_TRUE(codec_specific_info->end_of_picture); + return EncodedImageCallback::Result(EncodedImageCallback::Result::OK); + }); + + // Expectation for dropped frame. + // end_of_picture should be true because it's the only layer. + EXPECT_CALL(callback, OnFrameDropped(_, 0, /*is_end_of_temporal_unit=*/true)); + + // Encode Frame 1. + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.Encode(NextInputFrame(), nullptr)); + // Encode Frame 2. + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.Encode(NextInputFrame(), nullptr)); +} + +TEST_F(TestVp9Impl, ReportFrameDroppedOnLayerDrop) { + // Keep a raw pointer for EXPECT calls and the like. Ownership is otherwise + // passed on to LibvpxVp9Encoder. + auto* const vpx = new NiceMock(); + LibvpxVp9Encoder encoder(CreateEnvironment(), {}, + absl::WrapUnique(vpx)); + + VideoCodec settings = DefaultCodecSettings(); + constexpr int kNumSpatialLayers = 2; + constexpr int kNumTemporalLayers = 1; + ConfigureSvc(settings, kNumSpatialLayers, kNumTemporalLayers); + + // Set up the vpx to return a packet with size 0 (dropped frame). + vpx_image_t img; + ON_CALL(*vpx, img_wrap).WillByDefault(GetWrapImageFunction(&img)); + ON_CALL(*vpx, codec_enc_init).WillByDefault(Return(VPX_CODEC_OK)); + ON_CALL(*vpx, codec_enc_config_default).WillByDefault(Return(VPX_CODEC_OK)); + // Capture the callback and handle Layer ID query. + vpx_codec_priv_output_cx_pkt_cb_pair_t callback_pointer = {}; + // Shared state to synchronize layer ID between codec_encode and + // codec_control. + int current_adj_layer_id = 0; + + // Expectation for VP9E_GET_SVC_LAYER_ID (vpx_svc_layer_id_t* overload). + EXPECT_CALL(*vpx, codec_control(_, _, A())) + .WillRepeatedly([&](vpx_codec_ctx_t*, vp8e_enc_control_id, + vpx_svc_layer_id_t* layer_id) { + layer_id->spatial_layer_id = current_adj_layer_id; + return VPX_CODEC_OK; + }); + + // Expectation for VP9E_REGISTER_CX_CALLBACK (void* overload). + EXPECT_CALL(*vpx, + codec_control(_, VP9E_REGISTER_CX_CALLBACK, Matcher(_))) + .WillRepeatedly([&](vpx_codec_ctx_t*, vp8e_enc_control_id, void* param) { + callback_pointer = + *reinterpret_cast(param); + return VPX_CODEC_OK; + }); + + // Frame 1: SL0 dropped, SL1 encoded. + // Frame 2: SL0 encoded, SL1 dropped. + EXPECT_CALL(*vpx, codec_encode) + .Times(2) // 2 frames + .WillRepeatedly([&](vpx_codec_ctx_t*, const vpx_image_t*, vpx_codec_pts_t, + uint64_t, vpx_enc_frame_flags_t, uint64_t) { + static int frame_count = 0; + int frame_idx = frame_count++; + + if (callback_pointer.output_cx_pkt) { + // Layer 0 + { + current_adj_layer_id = 0; + vpx_codec_cx_pkt pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.kind = VPX_CODEC_CX_FRAME_PKT; + std::vector data(1000); // Shared dummy data + + if (frame_idx == 0) { // Frame 1, SL0: dropped. + pkt.data.frame.sz = 0; + } else { // Frame 2, SL0: encoded. + pkt.data.frame.buf = data.data(); + pkt.data.frame.sz = data.size(); + pkt.data.frame.flags = VPX_FRAME_IS_KEY; + pkt.data.frame.width[0] = 1280; + pkt.data.frame.height[0] = 720; + } + callback_pointer.output_cx_pkt(&pkt, callback_pointer.user_priv); + } + // Layer 1 + { + current_adj_layer_id = 1; + vpx_codec_cx_pkt pkt; + memset(&pkt, 0, sizeof(pkt)); + pkt.kind = VPX_CODEC_CX_FRAME_PKT; + std::vector data(1000); + + if (frame_idx == 0) { // Frame 1, SL1: encoded. + pkt.data.frame.buf = data.data(); + pkt.data.frame.sz = data.size(); + pkt.data.frame.flags = VPX_FRAME_IS_KEY; + pkt.data.frame.width[0] = 1280; + pkt.data.frame.height[0] = 720; + } else { // Frame 2, SL1: dropped. + pkt.data.frame.sz = 0; + } + callback_pointer.output_cx_pkt(&pkt, callback_pointer.user_priv); + } + } + return VPX_CODEC_OK; + }); + + MockEncodedImageCallback callback; + encoder.RegisterEncodeCompleteCallback(&callback); + ASSERT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.InitEncode(&settings, kSettings)); + EXPECT_NE(callback_pointer.output_cx_pkt, nullptr); + + // Expectations. + // Sequence of events: + // 1. Frame 1 SL0: dropped. + // 2. Frame 1 SL1: encoded. + // 3. Frame 2 SL0: encoded. + // 4. Frame 2 SL1: dropped. + + EXPECT_CALL(callback, OnFrameDropped) + .Times(2) + .WillOnce([&](uint32_t, int spatial_idx, bool is_end_of_temporal_unit) { + EXPECT_EQ(spatial_idx, 0); + EXPECT_FALSE(is_end_of_temporal_unit); + }) + .WillOnce([&](uint32_t, int spatial_idx, bool is_end_of_temporal_unit) { + EXPECT_EQ(spatial_idx, 1); + EXPECT_TRUE(is_end_of_temporal_unit); + }); + + EXPECT_CALL(callback, OnEncodedImage) + .Times(2) + .WillOnce([&](const EncodedImage& encoded_image, + const CodecSpecificInfo* codec_specific_info) { + // Frame 1 SL1 + EXPECT_EQ(encoded_image.SpatialIndex(), 1); + EXPECT_TRUE(codec_specific_info->end_of_picture); + return EncodedImageCallback::Result(EncodedImageCallback::Result::OK); + }) + .WillOnce([&](const EncodedImage& encoded_image, + const CodecSpecificInfo* codec_specific_info) { + // Frame 2 SL0 + EXPECT_EQ(encoded_image.SpatialIndex(), 0); + EXPECT_FALSE(codec_specific_info->end_of_picture); + return EncodedImageCallback::Result(EncodedImageCallback::Result::OK); + }); + + // Encode Frame 1. + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.Encode(NextInputFrame(), nullptr)); + // Encode Frame 2. + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder.Encode(NextInputFrame(), nullptr)); +} + TEST(Vp9SpeedSettingsTrialsTest, NoSvcUsesGlobalSpeedFromTl0InLayerConfig) { // TL0 speed 8 at >= 480x270, 5 if below that. FieldTrials trials = CreateTestFieldTrials( @@ -2754,7 +3044,7 @@ TEST_P(TestVp9ImplSvcFrameDropConfig, SvcFrameDropConfig) { INSTANTIATE_TEST_SUITE_P( All, TestVp9ImplSvcFrameDropConfig, - ::testing::Values( + Values( // Flexible mode is disabled, KSVC. Layer drop is not allowed. SvcFrameDropConfigTestParameters{ .flexible_mode = false, diff --git a/modules/video_coding/deprecated/BUILD.gn b/modules/video_coding/deprecated/BUILD.gn deleted file mode 100644 index 533e01798fa..00000000000 --- a/modules/video_coding/deprecated/BUILD.gn +++ /dev/null @@ -1,210 +0,0 @@ -# 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. - -import("../../../webrtc.gni") - -visibility = [ - ":*", - "../:video_coding_legacy", - "../:video_coding_unittests", -] - -rtc_library("deprecated_decoding_state") { - sources = [ - "decoding_state.cc", - "decoding_state.h", - ] - deps = [ - ":deprecated_frame_buffer", - ":deprecated_jitter_buffer_common", - ":deprecated_packet", - "..:codec_globals_headers", - "../../../api/video:video_frame", - "../../../api/video:video_frame_type", - "../../../common_video", - "../../../modules:module_api_public", - "../../../rtc_base:checks", - "../../../rtc_base:logging", - ] -} - -rtc_library("deprecated_event_wrapper") { - sources = [ - "event_wrapper.cc", - "event_wrapper.h", - ] - deps = [ - "../../../api/units:time_delta", - "../../../rtc_base:rtc_event", - ] -} - -rtc_library("deprecated_jitter_buffer_common") { - sources = [ "jitter_buffer_common.h" ] -} - -rtc_library("deprecated_jitter_buffer") { - sources = [ - "jitter_buffer.cc", - "jitter_buffer.h", - ] - deps = [ - ":deprecated_decoding_state", - ":deprecated_event_wrapper", - ":deprecated_frame_buffer", - ":deprecated_jitter_buffer_common", - ":deprecated_packet", - ":deprecated_session_info", - "../../../api:field_trials_view", - "../../../api/units:data_size", - "../../../api/units:timestamp", - "../../../api/video:video_frame_type", - "../../../modules:module_api", - "../../../modules:module_api_public", - "../../../modules/video_coding:video_codec_interface", - "../../../modules/video_coding/timing:inter_frame_delay_variation_calculator", - "../../../modules/video_coding/timing:jitter_estimator", - "../../../rtc_base:checks", - "../../../rtc_base:logging", - "../../../rtc_base:macromagic", - "../../../rtc_base/synchronization:mutex", - "../../../system_wrappers", - ] -} - -rtc_library("deprecated_frame_buffer") { - sources = [ - "frame_buffer.cc", - "frame_buffer.h", - ] - deps = [ - ":deprecated_jitter_buffer_common", - ":deprecated_packet", - ":deprecated_session_info", - "../../../api:scoped_refptr", - "../../../api/video:encoded_image", - "../../../api/video:video_frame_type", - "../../../api/video:video_rtp_headers", - "../../../modules/video_coding:codec_globals_headers", - "../../../modules/video_coding:encoded_frame", - "../../../rtc_base:checks", - "../../../rtc_base:event_tracer", - "../../../rtc_base:logging", - ] -} - -rtc_library("deprecated_packet") { - sources = [ - "packet.cc", - "packet.h", - ] - deps = [ - "../../../api:rtp_headers", - "../../../api:rtp_packet_info", - "../../../api/units:timestamp", - "../../../api/video:video_frame", - "../../../api/video:video_frame_type", - "../../../modules/rtp_rtcp:rtp_rtcp_format", - "../../../modules/rtp_rtcp:rtp_video_header", - ] -} - -rtc_library("deprecated_receiver") { - sources = [ - "receiver.cc", - "receiver.h", - ] - deps = [ - ":deprecated_event_wrapper", - ":deprecated_jitter_buffer", - ":deprecated_jitter_buffer_common", - ":deprecated_packet", - "../../../api:field_trials_view", - "../../../api/units:time_delta", - "../../../api/units:timestamp", - "../../../api/video:encoded_image", - "../../../api/video:video_rtp_headers", - "../../../modules/video_coding", - "../../../modules/video_coding:encoded_frame", - "../../../modules/video_coding:video_codec_interface", - "../../../modules/video_coding/timing:timing_module", - "../../../rtc_base:event_tracer", - "../../../rtc_base:logging", - "../../../rtc_base:safe_conversions", - "../../../system_wrappers", - "//third_party/abseil-cpp/absl/memory", - ] -} - -rtc_library("deprecated_session_info") { - deps = [ - ":deprecated_jitter_buffer_common", - ":deprecated_packet", - "../../../api/video:video_frame", - "../../../api/video:video_frame_type", - "../../../modules:module_api", - "../../../modules:module_api_public", - "../../../modules/video_coding:codec_globals_headers", - "../../../rtc_base:checks", - "../../../rtc_base:logging", - "//third_party/abseil-cpp/absl/algorithm:container", - ] - sources = [ - "session_info.cc", - "session_info.h", - ] -} - -rtc_library("deprecated_stream_generator") { - deps = [ - ":deprecated_packet", - "../../../api/video:video_frame_type", - "../../../rtc_base:checks", - ] - sources = [ - "stream_generator.cc", - "stream_generator.h", - ] -} - -rtc_library("deprecated_unittests") { - testonly = true - sources = [ - "decoding_state_unittest.cc", - "jitter_buffer_unittest.cc", - "receiver_unittest.cc", - "session_info_unittest.cc", - ] - visibility += [ "../../../modules/*" ] - deps = [ - ":deprecated_decoding_state", - ":deprecated_event_wrapper", - ":deprecated_frame_buffer", - ":deprecated_jitter_buffer", - ":deprecated_jitter_buffer_common", - ":deprecated_packet", - ":deprecated_receiver", - ":deprecated_session_info", - ":deprecated_stream_generator", - "../../../api:field_trials", - "../../../api:rtp_headers", - "../../../api/units:time_delta", - "../../../api/video:video_frame", - "../../../api/video:video_frame_type", - "../../../common_video", - "../../../modules/rtp_rtcp:rtp_video_header", - "../../../modules/video_coding:codec_globals_headers", - "../../../modules/video_coding:encoded_frame", - "../../../modules/video_coding/timing:timing_module", - "../../../rtc_base:checks", - "../../../system_wrappers", - "../../../test:create_test_field_trials", - "../../../test:test_support", - "//third_party/abseil-cpp/absl/memory", - ] -} diff --git a/modules/video_coding/deprecated/decoding_state.cc b/modules/video_coding/deprecated/decoding_state.cc deleted file mode 100644 index 6711cd5daf5..00000000000 --- a/modules/video_coding/deprecated/decoding_state.cc +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/decoding_state.h" - -#include -#include -#include -#include -#include - -#include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" -#include "common_video/h264/h264_common.h" -#include "modules/include/module_common_types_public.h" -#include "modules/video_coding/codecs/interface/common_constants.h" -#include "modules/video_coding/deprecated/frame_buffer.h" -#include "modules/video_coding/deprecated/packet.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" - -namespace webrtc { - -VCMDecodingState::VCMDecodingState() - : sequence_num_(0), - time_stamp_(0), - picture_id_(kNoPictureId), - temporal_id_(kNoTemporalIdx), - tl0_pic_id_(kNoTl0PicIdx), - full_sync_(true), - in_initial_state_(true) { - memset(frame_decoded_, 0, sizeof(frame_decoded_)); -} - -VCMDecodingState::~VCMDecodingState() {} - -void VCMDecodingState::Reset() { - // TODO(mikhal): Verify - not always would want to reset the sync - sequence_num_ = 0; - time_stamp_ = 0; - picture_id_ = kNoPictureId; - temporal_id_ = kNoTemporalIdx; - tl0_pic_id_ = kNoTl0PicIdx; - full_sync_ = true; - in_initial_state_ = true; - memset(frame_decoded_, 0, sizeof(frame_decoded_)); - received_sps_.clear(); - received_pps_.clear(); -} - -uint32_t VCMDecodingState::time_stamp() const { - return time_stamp_; -} - -uint16_t VCMDecodingState::sequence_num() const { - return sequence_num_; -} - -bool VCMDecodingState::IsOldFrame(const VCMFrameBuffer* frame) const { - RTC_DCHECK(frame); - if (in_initial_state_) - return false; - return !IsNewerTimestamp(frame->RtpTimestamp(), time_stamp_); -} - -bool VCMDecodingState::IsOldPacket(const VCMPacket* packet) const { - RTC_DCHECK(packet); - if (in_initial_state_) - return false; - return !IsNewerTimestamp(packet->timestamp, time_stamp_); -} - -void VCMDecodingState::SetState(const VCMFrameBuffer* frame) { - RTC_DCHECK(frame); - RTC_CHECK_GE(frame->GetHighSeqNum(), 0); - if (!UsingFlexibleMode(frame)) - UpdateSyncState(frame); - sequence_num_ = static_cast(frame->GetHighSeqNum()); - time_stamp_ = frame->RtpTimestamp(); - picture_id_ = frame->PictureId(); - temporal_id_ = frame->TemporalId(); - tl0_pic_id_ = frame->Tl0PicId(); - - for (const NaluInfo& nalu : frame->GetNaluInfos()) { - if (nalu.type == H264::NaluType::kPps) { - if (nalu.pps_id < 0) { - RTC_LOG(LS_WARNING) << "Received pps without pps id."; - } else if (nalu.sps_id < 0) { - RTC_LOG(LS_WARNING) << "Received pps without sps id."; - } else { - received_pps_[nalu.pps_id] = nalu.sps_id; - } - } else if (nalu.type == H264::NaluType::kSps) { - if (nalu.sps_id < 0) { - RTC_LOG(LS_WARNING) << "Received sps without sps id."; - } else { - received_sps_.insert(nalu.sps_id); - } - } - } - - if (UsingFlexibleMode(frame)) { - uint16_t frame_index = picture_id_ % kFrameDecodedLength; - if (in_initial_state_) { - frame_decoded_cleared_to_ = frame_index; - } else if (frame->FrameType() == VideoFrameType::kVideoFrameKey) { - memset(frame_decoded_, 0, sizeof(frame_decoded_)); - frame_decoded_cleared_to_ = frame_index; - } else { - if (AheadOfFramesDecodedClearedTo(frame_index)) { - while (frame_decoded_cleared_to_ != frame_index) { - frame_decoded_cleared_to_ = - (frame_decoded_cleared_to_ + 1) % kFrameDecodedLength; - frame_decoded_[frame_decoded_cleared_to_] = false; - } - } - } - frame_decoded_[frame_index] = true; - } - - in_initial_state_ = false; -} - -void VCMDecodingState::CopyFrom(const VCMDecodingState& state) { - sequence_num_ = state.sequence_num_; - time_stamp_ = state.time_stamp_; - picture_id_ = state.picture_id_; - temporal_id_ = state.temporal_id_; - tl0_pic_id_ = state.tl0_pic_id_; - full_sync_ = state.full_sync_; - in_initial_state_ = state.in_initial_state_; - frame_decoded_cleared_to_ = state.frame_decoded_cleared_to_; - memcpy(frame_decoded_, state.frame_decoded_, sizeof(frame_decoded_)); - received_sps_ = state.received_sps_; - received_pps_ = state.received_pps_; -} - -bool VCMDecodingState::UpdateEmptyFrame(const VCMFrameBuffer* frame) { - bool empty_packet = frame->GetHighSeqNum() == frame->GetLowSeqNum(); - if (in_initial_state_ && empty_packet) { - // Drop empty packets as long as we are in the initial state. - return true; - } - if ((empty_packet && ContinuousSeqNum(frame->GetHighSeqNum())) || - ContinuousFrame(frame)) { - // Continuous empty packets or continuous frames can be dropped if we - // advance the sequence number. - sequence_num_ = frame->GetHighSeqNum(); - time_stamp_ = frame->RtpTimestamp(); - return true; - } - return false; -} - -void VCMDecodingState::UpdateOldPacket(const VCMPacket* packet) { - RTC_DCHECK(packet); - if (packet->timestamp == time_stamp_) { - // Late packet belonging to the last decoded frame - make sure we update the - // last decoded sequence number. - sequence_num_ = LatestSequenceNumber(packet->seqNum, sequence_num_); - } -} - -void VCMDecodingState::SetSeqNum(uint16_t new_seq_num) { - sequence_num_ = new_seq_num; -} - -bool VCMDecodingState::in_initial_state() const { - return in_initial_state_; -} - -bool VCMDecodingState::full_sync() const { - return full_sync_; -} - -void VCMDecodingState::UpdateSyncState(const VCMFrameBuffer* frame) { - if (in_initial_state_) - return; - if (frame->TemporalId() == kNoTemporalIdx || - frame->Tl0PicId() == kNoTl0PicIdx) { - full_sync_ = true; - } else if (frame->FrameType() == VideoFrameType::kVideoFrameKey || - frame->LayerSync()) { - full_sync_ = true; - } else if (full_sync_) { - // Verify that we are still in sync. - // Sync will be broken if continuity is true for layers but not for the - // other methods (PictureId and SeqNum). - if (UsingPictureId(frame)) { - // First check for a valid tl0PicId. - if (frame->Tl0PicId() - tl0_pic_id_ > 1) { - full_sync_ = false; - } else { - full_sync_ = ContinuousPictureId(frame->PictureId()); - } - } else { - full_sync_ = - ContinuousSeqNum(static_cast(frame->GetLowSeqNum())); - } - } -} - -bool VCMDecodingState::ContinuousFrame(const VCMFrameBuffer* frame) const { - // Check continuity based on the following hierarchy: - // - Temporal layers (stop here if out of sync). - // - Picture Id when available. - // - Sequence numbers. - // Return true when in initial state. - // Note that when a method is not applicable it will return false. - RTC_DCHECK(frame); - // A key frame is always considered continuous as it doesn't refer to any - // frames and therefore won't introduce any errors even if prior frames are - // missing. - if (frame->FrameType() == VideoFrameType::kVideoFrameKey && - HaveSpsAndPps(frame->GetNaluInfos())) { - return true; - } - // When in the initial state we always require a key frame to start decoding. - if (in_initial_state_) - return false; - if (ContinuousLayer(frame->TemporalId(), frame->Tl0PicId())) - return true; - // tl0picId is either not used, or should remain unchanged. - if (frame->Tl0PicId() != tl0_pic_id_) - return false; - // Base layers are not continuous or temporal layers are inactive. - // In the presence of temporal layers, check for Picture ID/sequence number - // continuity if sync can be restored by this frame. - if (!full_sync_ && !frame->LayerSync()) - return false; - if (UsingPictureId(frame)) { - if (UsingFlexibleMode(frame)) { - return ContinuousFrameRefs(frame); - } else { - return ContinuousPictureId(frame->PictureId()); - } - } else { - return ContinuousSeqNum(static_cast(frame->GetLowSeqNum())) && - HaveSpsAndPps(frame->GetNaluInfos()); - } -} - -bool VCMDecodingState::ContinuousPictureId(int picture_id) const { - int next_picture_id = picture_id_ + 1; - if (picture_id < picture_id_) { - // Wrap - if (picture_id_ >= 0x80) { - // 15 bits used for picture id - return ((next_picture_id & 0x7FFF) == picture_id); - } else { - // 7 bits used for picture id - return ((next_picture_id & 0x7F) == picture_id); - } - } - // No wrap - return (next_picture_id == picture_id); -} - -bool VCMDecodingState::ContinuousSeqNum(uint16_t seq_num) const { - return seq_num == static_cast(sequence_num_ + 1); -} - -bool VCMDecodingState::ContinuousLayer(int temporal_id, int tl0_pic_id) const { - // First, check if applicable. - if (temporal_id == kNoTemporalIdx || tl0_pic_id == kNoTl0PicIdx) - return false; - // If this is the first frame to use temporal layers, make sure we start - // from base. - else if (tl0_pic_id_ == kNoTl0PicIdx && temporal_id_ == kNoTemporalIdx && - temporal_id == 0) - return true; - - // Current implementation: Look for base layer continuity. - if (temporal_id != 0) - return false; - return (static_cast(tl0_pic_id_ + 1) == tl0_pic_id); -} - -bool VCMDecodingState::ContinuousFrameRefs(const VCMFrameBuffer* frame) const { - uint8_t num_refs = frame->CodecSpecific()->codecSpecific.VP9.num_ref_pics; - for (uint8_t r = 0; r < num_refs; ++r) { - uint16_t frame_ref = frame->PictureId() - - frame->CodecSpecific()->codecSpecific.VP9.p_diff[r]; - uint16_t frame_index = frame_ref % kFrameDecodedLength; - if (AheadOfFramesDecodedClearedTo(frame_index) || - !frame_decoded_[frame_index]) { - return false; - } - } - return true; -} - -bool VCMDecodingState::UsingPictureId(const VCMFrameBuffer* frame) const { - return (frame->PictureId() != kNoPictureId && picture_id_ != kNoPictureId); -} - -bool VCMDecodingState::UsingFlexibleMode(const VCMFrameBuffer* frame) const { - bool is_flexible_mode = - frame->CodecSpecific()->codecType == kVideoCodecVP9 && - frame->CodecSpecific()->codecSpecific.VP9.flexible_mode; - if (is_flexible_mode && frame->PictureId() == kNoPictureId) { - RTC_LOG(LS_WARNING) << "Frame is marked as using flexible mode but no" - "picture id is set."; - return false; - } - return is_flexible_mode; -} - -// TODO(philipel): change how check work, this check practially -// limits the max p_diff to 64. -bool VCMDecodingState::AheadOfFramesDecodedClearedTo(uint16_t index) const { - // No way of knowing for sure if we are actually ahead of - // frame_decoded_cleared_to_. We just make the assumption - // that we are not trying to reference back to a very old - // index, but instead are referencing a newer index. - uint16_t diff = - index > frame_decoded_cleared_to_ - ? kFrameDecodedLength - (index - frame_decoded_cleared_to_) - : frame_decoded_cleared_to_ - index; - return diff > kFrameDecodedLength / 2; -} - -bool VCMDecodingState::HaveSpsAndPps(const std::vector& nalus) const { - std::set new_sps; - std::map new_pps; - for (const NaluInfo& nalu : nalus) { - // Check if this nalu actually contains sps/pps information or dependencies. - if (nalu.sps_id == -1 && nalu.pps_id == -1) - continue; - switch (nalu.type) { - case H264::NaluType::kPps: - if (nalu.pps_id < 0) { - RTC_LOG(LS_WARNING) << "Received pps without pps id."; - } else if (nalu.sps_id < 0) { - RTC_LOG(LS_WARNING) << "Received pps without sps id."; - } else { - new_pps[nalu.pps_id] = nalu.sps_id; - } - break; - case H264::NaluType::kSps: - if (nalu.sps_id < 0) { - RTC_LOG(LS_WARNING) << "Received sps without sps id."; - } else { - new_sps.insert(nalu.sps_id); - } - break; - default: { - int needed_sps = -1; - auto pps_it = new_pps.find(nalu.pps_id); - if (pps_it != new_pps.end()) { - needed_sps = pps_it->second; - } else { - auto pps_it2 = received_pps_.find(nalu.pps_id); - if (pps_it2 == received_pps_.end()) { - return false; - } - needed_sps = pps_it2->second; - } - if (new_sps.find(needed_sps) == new_sps.end() && - received_sps_.find(needed_sps) == received_sps_.end()) { - return false; - } - break; - } - } - } - return true; -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/decoding_state.h b/modules/video_coding/deprecated/decoding_state.h deleted file mode 100644 index 19f6daabc5f..00000000000 --- a/modules/video_coding/deprecated/decoding_state.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_DECODING_STATE_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_DECODING_STATE_H_ - -#include -#include -#include -#include - -namespace webrtc { - -// Forward declarations -struct NaluInfo; -class VCMFrameBuffer; -class VCMPacket; - -class VCMDecodingState { - public: - // The max number of bits used to reference back - // to a previous frame when using flexible mode. - static const uint16_t kNumRefBits = 7; - static const uint16_t kFrameDecodedLength = 1 << kNumRefBits; - - VCMDecodingState(); - ~VCMDecodingState(); - // Check for old frame - bool IsOldFrame(const VCMFrameBuffer* frame) const; - // Check for old packet - bool IsOldPacket(const VCMPacket* packet) const; - // Check for frame continuity based on current decoded state. Use best method - // possible, i.e. temporal info, picture ID or sequence number. - bool ContinuousFrame(const VCMFrameBuffer* frame) const; - void SetState(const VCMFrameBuffer* frame); - void CopyFrom(const VCMDecodingState& state); - bool UpdateEmptyFrame(const VCMFrameBuffer* frame); - // Update the sequence number if the timestamp matches current state and the - // sequence number is higher than the current one. This accounts for packets - // arriving late. - void UpdateOldPacket(const VCMPacket* packet); - void SetSeqNum(uint16_t new_seq_num); - void Reset(); - uint32_t time_stamp() const; - uint16_t sequence_num() const; - // Return true if at initial state. - bool in_initial_state() const; - // Return true when sync is on - decode all layers. - bool full_sync() const; - - private: - void UpdateSyncState(const VCMFrameBuffer* frame); - // Designated continuity functions - bool ContinuousPictureId(int picture_id) const; - bool ContinuousSeqNum(uint16_t seq_num) const; - bool ContinuousLayer(int temporal_id, int tl0_pic_id) const; - bool ContinuousFrameRefs(const VCMFrameBuffer* frame) const; - bool UsingPictureId(const VCMFrameBuffer* frame) const; - bool UsingFlexibleMode(const VCMFrameBuffer* frame) const; - bool AheadOfFramesDecodedClearedTo(uint16_t index) const; - bool HaveSpsAndPps(const std::vector& nalus) const; - - // Keep state of last decoded frame. - // TODO(mikhal/stefan): create designated classes to handle these types. - uint16_t sequence_num_; - uint32_t time_stamp_; - int picture_id_; - int temporal_id_; - int tl0_pic_id_; - bool full_sync_; // Sync flag when temporal layers are used. - bool in_initial_state_; - - // Used to check references in flexible mode. - bool frame_decoded_[kFrameDecodedLength]; - uint16_t frame_decoded_cleared_to_; - std::set received_sps_; - std::map received_pps_; -}; - -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_DECODING_STATE_H_ diff --git a/modules/video_coding/deprecated/decoding_state_unittest.cc b/modules/video_coding/deprecated/decoding_state_unittest.cc deleted file mode 100644 index a32583d4dc8..00000000000 --- a/modules/video_coding/deprecated/decoding_state_unittest.cc +++ /dev/null @@ -1,716 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/decoding_state.h" - -#include - -#include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "modules/video_coding/codecs/interface/common_constants.h" -#include "modules/video_coding/codecs/vp8/include/vp8_globals.h" -#include "modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "modules/video_coding/deprecated/frame_buffer.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/deprecated/session_info.h" -#include "test/gtest.h" - -namespace webrtc { - -TEST(TestDecodingState, Sanity) { - VCMDecodingState dec_state; - dec_state.Reset(); - EXPECT_TRUE(dec_state.in_initial_state()); - EXPECT_TRUE(dec_state.full_sync()); -} - -TEST(TestDecodingState, FrameContinuity) { - VCMDecodingState dec_state; - // Check that makes decision based on correct method. - VCMFrameBuffer frame; - VCMFrameBuffer frame_key; - VCMPacket packet; - packet.video_header.is_first_packet_in_frame = true; - packet.timestamp = 1; - packet.seqNum = 0xffff; - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.codec = kVideoCodecVP8; - auto& vp8_header = - packet.video_header.video_type_header.emplace(); - vp8_header.pictureId = 0x007F; - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - // Always start with a key frame. - dec_state.Reset(); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - EXPECT_LE(0, frame_key.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame_key)); - dec_state.SetState(&frame); - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - // Use pictureId - packet.video_header.is_first_packet_in_frame = false; - vp8_header.pictureId = 0x0002; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - frame.Reset(); - vp8_header.pictureId = 0; - packet.seqNum = 10; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - - // Use sequence numbers. - vp8_header.pictureId = kNoPictureId; - frame.Reset(); - packet.seqNum = dec_state.sequence_num() - 1u; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - frame.Reset(); - packet.seqNum = dec_state.sequence_num() + 1u; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - // Insert another packet to this frame - packet.seqNum++; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - // Verify wrap. - EXPECT_LE(dec_state.sequence_num(), 0xffff); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Insert packet with temporal info. - dec_state.Reset(); - frame.Reset(); - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 0; - packet.seqNum = 1; - packet.timestamp = 1; - EXPECT_TRUE(dec_state.full_sync()); - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - EXPECT_TRUE(dec_state.full_sync()); - frame.Reset(); - // 1 layer up - still good. - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 1; - vp8_header.pictureId = 1; - packet.seqNum = 2; - packet.timestamp = 2; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - EXPECT_TRUE(dec_state.full_sync()); - frame.Reset(); - // Lost non-base layer packet => should update sync parameter. - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 3; - vp8_header.pictureId = 3; - packet.seqNum = 4; - packet.timestamp = 4; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - // Now insert the next non-base layer (belonging to a next tl0PicId). - frame.Reset(); - vp8_header.tl0PicIdx = 1; - vp8_header.temporalIdx = 2; - vp8_header.pictureId = 4; - packet.seqNum = 5; - packet.timestamp = 5; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - // Checking continuity and not updating the state - this should not trigger - // an update of sync state. - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - EXPECT_TRUE(dec_state.full_sync()); - // Next base layer (dropped interim non-base layers) - should update sync. - frame.Reset(); - vp8_header.tl0PicIdx = 1; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 5; - packet.seqNum = 6; - packet.timestamp = 6; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - EXPECT_FALSE(dec_state.full_sync()); - - // Check wrap for temporal layers. - frame.Reset(); - vp8_header.tl0PicIdx = 0x00FF; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 6; - packet.seqNum = 7; - packet.timestamp = 7; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - EXPECT_FALSE(dec_state.full_sync()); - frame.Reset(); - vp8_header.tl0PicIdx = 0x0000; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 7; - packet.seqNum = 8; - packet.timestamp = 8; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - // The current frame is not continuous - dec_state.SetState(&frame); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); -} - -TEST(TestDecodingState, UpdateOldPacket) { - VCMDecodingState dec_state; - // Update only if zero size and newer than previous. - // Should only update if the timeStamp match. - VCMFrameBuffer frame; - VCMPacket packet; - packet.timestamp = 1; - packet.seqNum = 1; - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - EXPECT_EQ(dec_state.sequence_num(), 1); - // Insert an empty packet that does not belong to the same frame. - // => Sequence num should be the same. - packet.timestamp = 2; - dec_state.UpdateOldPacket(&packet); - EXPECT_EQ(dec_state.sequence_num(), 1); - // Now insert empty packet belonging to the same frame. - packet.timestamp = 1; - packet.seqNum = 2; - packet.video_header.frame_type = VideoFrameType::kEmptyFrame; - packet.sizeBytes = 0; - dec_state.UpdateOldPacket(&packet); - EXPECT_EQ(dec_state.sequence_num(), 2); - // Now insert delta packet belonging to the same frame. - packet.timestamp = 1; - packet.seqNum = 3; - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.sizeBytes = 1400; - dec_state.UpdateOldPacket(&packet); - EXPECT_EQ(dec_state.sequence_num(), 3); - // Insert a packet belonging to an older timestamp - should not update the - // sequence number. - packet.timestamp = 0; - packet.seqNum = 4; - packet.video_header.frame_type = VideoFrameType::kEmptyFrame; - packet.sizeBytes = 0; - dec_state.UpdateOldPacket(&packet); - EXPECT_EQ(dec_state.sequence_num(), 3); -} - -TEST(TestDecodingState, MultiLayerBehavior) { - // Identify sync/non-sync when more than one layer. - VCMDecodingState dec_state; - // Identify packets belonging to old frames/packets. - // Set state for current frames. - // tl0PicIdx 0, temporal id 0. - VCMFrameBuffer frame; - VCMPacket packet; - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.codec = kVideoCodecVP8; - packet.timestamp = 0; - packet.seqNum = 0; - auto& vp8_header = - packet.video_header.video_type_header.emplace(); - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 0; - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - // tl0PicIdx 0, temporal id 1. - frame.Reset(); - packet.timestamp = 1; - packet.seqNum = 1; - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 1; - vp8_header.pictureId = 1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - EXPECT_TRUE(dec_state.full_sync()); - // Lost tl0PicIdx 0, temporal id 2. - // Insert tl0PicIdx 0, temporal id 3. - frame.Reset(); - packet.timestamp = 3; - packet.seqNum = 3; - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 3; - vp8_header.pictureId = 3; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - EXPECT_FALSE(dec_state.full_sync()); - // Insert next base layer - frame.Reset(); - packet.timestamp = 4; - packet.seqNum = 4; - vp8_header.tl0PicIdx = 1; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 4; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - EXPECT_FALSE(dec_state.full_sync()); - // Insert key frame - should update sync value. - // A key frame is always a base layer. - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet.video_header.is_first_packet_in_frame = true; - packet.timestamp = 5; - packet.seqNum = 5; - vp8_header.tl0PicIdx = 2; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 5; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - EXPECT_TRUE(dec_state.full_sync()); - // After sync, a continuous PictureId is required - // (continuous base layer is not enough ) - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.timestamp = 6; - packet.seqNum = 6; - vp8_header.tl0PicIdx = 3; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 6; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - EXPECT_TRUE(dec_state.full_sync()); - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.is_first_packet_in_frame = true; - packet.timestamp = 8; - packet.seqNum = 8; - vp8_header.tl0PicIdx = 4; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 8; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - EXPECT_TRUE(dec_state.full_sync()); - dec_state.SetState(&frame); - EXPECT_FALSE(dec_state.full_sync()); - - // Insert a non-ref frame - should update sync value. - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.is_first_packet_in_frame = true; - packet.timestamp = 9; - packet.seqNum = 9; - vp8_header.tl0PicIdx = 4; - vp8_header.temporalIdx = 2; - vp8_header.pictureId = 9; - vp8_header.layerSync = true; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - EXPECT_TRUE(dec_state.full_sync()); - - // The following test will verify the sync flag behavior after a loss. - // Create the following pattern: - // Update base layer, lose packet 1 (sync flag on, layer 2), insert packet 3 - // (sync flag on, layer 2) check continuity and sync flag after inserting - // packet 2 (sync flag on, layer 1). - // Base layer. - frame.Reset(); - dec_state.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.is_first_packet_in_frame = true; - packet.markerBit = 1; - packet.timestamp = 0; - packet.seqNum = 0; - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 0; - vp8_header.layerSync = false; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - EXPECT_TRUE(dec_state.full_sync()); - // Layer 2 - 2 packets (insert one, lose one). - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.is_first_packet_in_frame = true; - packet.markerBit = 0; - packet.timestamp = 1; - packet.seqNum = 1; - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 2; - vp8_header.pictureId = 1; - vp8_header.layerSync = true; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - // Layer 1 - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.is_first_packet_in_frame = true; - packet.markerBit = 1; - packet.timestamp = 2; - packet.seqNum = 3; - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 1; - vp8_header.pictureId = 2; - vp8_header.layerSync = true; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - EXPECT_TRUE(dec_state.full_sync()); -} - -TEST(TestDecodingState, DiscontinuousPicIdContinuousSeqNum) { - VCMDecodingState dec_state; - VCMFrameBuffer frame; - VCMPacket packet; - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet.video_header.codec = kVideoCodecVP8; - packet.timestamp = 0; - packet.seqNum = 0; - auto& vp8_header = - packet.video_header.video_type_header.emplace(); - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 0; - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - EXPECT_TRUE(dec_state.full_sync()); - - // Continuous sequence number but discontinuous picture id. This implies a - // a loss and we have to fall back to only decoding the base layer. - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.timestamp += 3000; - ++packet.seqNum; - vp8_header.temporalIdx = 1; - vp8_header.pictureId = 2; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - EXPECT_FALSE(dec_state.full_sync()); -} - -TEST(TestDecodingState, OldInput) { - VCMDecodingState dec_state; - // Identify packets belonging to old frames/packets. - // Set state for current frames. - VCMFrameBuffer frame; - VCMPacket packet; - packet.timestamp = 10; - packet.seqNum = 1; - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - packet.timestamp = 9; - EXPECT_TRUE(dec_state.IsOldPacket(&packet)); - // Check for old frame - frame.Reset(); - frame.InsertPacket(packet, 0, frame_data); - EXPECT_TRUE(dec_state.IsOldFrame(&frame)); -} - -TEST(TestDecodingState, PictureIdRepeat) { - VCMDecodingState dec_state; - VCMFrameBuffer frame; - VCMPacket packet; - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet.video_header.codec = kVideoCodecVP8; - packet.timestamp = 0; - packet.seqNum = 0; - auto& vp8_header = - packet.video_header.video_type_header.emplace(); - vp8_header.tl0PicIdx = 0; - vp8_header.temporalIdx = 0; - vp8_header.pictureId = 0; - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - // tl0PicIdx 0, temporal id 1. - frame.Reset(); - ++packet.timestamp; - ++packet.seqNum; - vp8_header.temporalIdx++; - vp8_header.pictureId++; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - frame.Reset(); - // Testing only gap in tl0PicIdx when tl0PicIdx in continuous. - vp8_header.tl0PicIdx += 3; - vp8_header.temporalIdx++; - vp8_header.tl0PicIdx = 1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); -} - -TEST(TestDecodingState, FrameContinuityFlexibleModeKeyFrame) { - VCMDecodingState dec_state; - VCMFrameBuffer frame; - VCMPacket packet; - packet.video_header.is_first_packet_in_frame = true; - packet.timestamp = 1; - packet.seqNum = 0xffff; - uint8_t data[] = "I need a data pointer for this test!"; - packet.sizeBytes = sizeof(data); - packet.dataPtr = data; - packet.video_header.codec = kVideoCodecVP9; - - auto& vp9_hdr = - packet.video_header.video_type_header.emplace(); - vp9_hdr.picture_id = 10; - vp9_hdr.flexible_mode = true; - - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - - // Key frame as first frame - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Key frame again - vp9_hdr.picture_id = 11; - frame.Reset(); - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Ref to 11, continuous - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_hdr.picture_id = 12; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); -} - -TEST(TestDecodingState, FrameContinuityFlexibleModeOutOfOrderFrames) { - VCMDecodingState dec_state; - VCMFrameBuffer frame; - VCMPacket packet; - packet.video_header.is_first_packet_in_frame = true; - packet.timestamp = 1; - packet.seqNum = 0xffff; - uint8_t data[] = "I need a data pointer for this test!"; - packet.sizeBytes = sizeof(data); - packet.dataPtr = data; - packet.video_header.codec = kVideoCodecVP9; - - auto& vp9_hdr = - packet.video_header.video_type_header.emplace(); - vp9_hdr.picture_id = 10; - vp9_hdr.flexible_mode = true; - - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - - // Key frame as first frame - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Ref to 10, continuous - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_hdr.picture_id = 15; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 5; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Out of order, last id 15, this id 12, ref to 10, continuous - frame.Reset(); - vp9_hdr.picture_id = 12; - vp9_hdr.pid_diff[0] = 2; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Ref 10, 12, 15, continuous - frame.Reset(); - vp9_hdr.picture_id = 20; - vp9_hdr.num_ref_pics = 3; - vp9_hdr.pid_diff[0] = 10; - vp9_hdr.pid_diff[1] = 8; - vp9_hdr.pid_diff[2] = 5; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); -} - -TEST(TestDecodingState, FrameContinuityFlexibleModeGeneral) { - VCMDecodingState dec_state; - VCMFrameBuffer frame; - VCMPacket packet; - packet.video_header.is_first_packet_in_frame = true; - packet.timestamp = 1; - packet.seqNum = 0xffff; - uint8_t data[] = "I need a data pointer for this test!"; - packet.sizeBytes = sizeof(data); - packet.dataPtr = data; - packet.video_header.codec = kVideoCodecVP9; - - auto& vp9_hdr = - packet.video_header.video_type_header.emplace(); - vp9_hdr.picture_id = 10; - vp9_hdr.flexible_mode = true; - - FrameData frame_data; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - - // Key frame as first frame - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - - // Delta frame as first frame - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - - // Key frame then delta frame - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - dec_state.SetState(&frame); - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.picture_id = 15; - vp9_hdr.pid_diff[0] = 5; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Ref to 11, not continuous - frame.Reset(); - vp9_hdr.picture_id = 16; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - - // Ref to 15, continuous - frame.Reset(); - vp9_hdr.picture_id = 16; - vp9_hdr.pid_diff[0] = 1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Ref to 11 and 15, not continuous - frame.Reset(); - vp9_hdr.picture_id = 20; - vp9_hdr.num_ref_pics = 2; - vp9_hdr.pid_diff[0] = 9; - vp9_hdr.pid_diff[1] = 5; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - - // Ref to 10, 15 and 16, continuous - frame.Reset(); - vp9_hdr.picture_id = 22; - vp9_hdr.num_ref_pics = 3; - vp9_hdr.pid_diff[0] = 12; - vp9_hdr.pid_diff[1] = 7; - vp9_hdr.pid_diff[2] = 6; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Key Frame, continuous - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - vp9_hdr.picture_id = VCMDecodingState::kFrameDecodedLength - 2; - vp9_hdr.num_ref_pics = 0; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Frame at last index, ref to KF, continuous - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_hdr.picture_id = VCMDecodingState::kFrameDecodedLength - 1; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Frame after wrapping buffer length, ref to last index, continuous - frame.Reset(); - vp9_hdr.picture_id = 0; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Frame after wrapping start frame, ref to 0, continuous - frame.Reset(); - vp9_hdr.picture_id = 20; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 20; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Frame after wrapping start frame, ref to 10, not continuous - frame.Reset(); - vp9_hdr.picture_id = 23; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 13; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); - - // Key frame, continuous - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameKey; - vp9_hdr.picture_id = 25; - vp9_hdr.num_ref_pics = 0; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Ref to KF, continuous - frame.Reset(); - packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_hdr.picture_id = 26; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 1; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_TRUE(dec_state.ContinuousFrame(&frame)); - dec_state.SetState(&frame); - - // Ref to frame previous to KF, not continuous - frame.Reset(); - vp9_hdr.picture_id = 30; - vp9_hdr.num_ref_pics = 1; - vp9_hdr.pid_diff[0] = 30; - EXPECT_LE(0, frame.InsertPacket(packet, 0, frame_data)); - EXPECT_FALSE(dec_state.ContinuousFrame(&frame)); -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/event_wrapper.cc b/modules/video_coding/deprecated/event_wrapper.cc deleted file mode 100644 index 709ec1c4d2c..00000000000 --- a/modules/video_coding/deprecated/event_wrapper.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/event_wrapper.h" - -#include "api/units/time_delta.h" -#include "rtc_base/event.h" - -namespace webrtc { - -class EventWrapperImpl : public EventWrapper { - public: - ~EventWrapperImpl() override {} - - bool Set() override { - event_.Set(); - return true; - } - - // TODO(bugs.webrtc.org/14366): Migrate to TimeDelta. - EventTypeWrapper Wait(int max_time_ms) override { - return event_.Wait(TimeDelta::Millis(max_time_ms)) ? kEventSignaled - : kEventTimeout; - } - - private: - Event event_; -}; - -// static -EventWrapper* EventWrapper::Create() { - return new EventWrapperImpl(); -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/event_wrapper.h b/modules/video_coding/deprecated/event_wrapper.h deleted file mode 100644 index eddcc31d719..00000000000 --- a/modules/video_coding/deprecated/event_wrapper.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_EVENT_WRAPPER_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_EVENT_WRAPPER_H_ - -namespace webrtc { -enum EventTypeWrapper { kEventSignaled = 1, kEventTimeout = 2 }; - -class EventWrapper { - public: - // Factory method. Constructor disabled. - static EventWrapper* Create(); - - virtual ~EventWrapper() {} - - // Releases threads who are calling Wait() and has started waiting. Please - // note that a thread calling Wait() will not start waiting immediately. - // assumptions to the contrary is a very common source of issues in - // multithreaded programming. - // Set is sticky in the sense that it will release at least one thread - // either immediately or some time in the future. - virtual bool Set() = 0; - - // Puts the calling thread into a wait state. The thread may be released - // by a Set() call depending on if other threads are waiting and if so on - // timing. The thread that was released will reset the event before leaving - // preventing more threads from being released. If multiple threads - // are waiting for the same Set(), only one (random) thread is guaranteed to - // be released. It is possible that multiple (random) threads are released - // Depending on timing. - // - // `max_time_ms` is the maximum time to wait in milliseconds. - // TODO(bugs.webrtc.org/14366): Migrate to TimeDelta. - virtual EventTypeWrapper Wait(int max_time_ms) = 0; -}; - -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_EVENT_WRAPPER_H_ diff --git a/modules/video_coding/deprecated/frame_buffer.cc b/modules/video_coding/deprecated/frame_buffer.cc deleted file mode 100644 index 84511bbeb95..00000000000 --- a/modules/video_coding/deprecated/frame_buffer.cc +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/frame_buffer.h" - -#include -#include -#include - -#include "api/video/encoded_image.h" -#include "api/video/video_frame_type.h" -#include "api/video/video_timing.h" -#include "modules/video_coding/codecs/h264/include/h264_globals.h" -#include "modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/deprecated/session_info.h" -#include "modules/video_coding/encoded_frame.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/trace_event.h" - -namespace webrtc { - -VCMFrameBuffer::VCMFrameBuffer() - : _state(kStateEmpty), _nackCount(0), _latestPacketTimeMs(-1) {} - -VCMFrameBuffer::~VCMFrameBuffer() {} - -VideoFrameType VCMFrameBuffer::FrameType() const { - return _sessionInfo.FrameType(); -} - -int32_t VCMFrameBuffer::GetLowSeqNum() const { - return _sessionInfo.LowSequenceNumber(); -} - -int32_t VCMFrameBuffer::GetHighSeqNum() const { - return _sessionInfo.HighSequenceNumber(); -} - -int VCMFrameBuffer::PictureId() const { - return _sessionInfo.PictureId(); -} - -int VCMFrameBuffer::TemporalId() const { - return _sessionInfo.TemporalId(); -} - -bool VCMFrameBuffer::LayerSync() const { - return _sessionInfo.LayerSync(); -} - -int VCMFrameBuffer::Tl0PicId() const { - return _sessionInfo.Tl0PicId(); -} - -std::vector VCMFrameBuffer::GetNaluInfos() const { - return _sessionInfo.GetNaluInfos(); -} - -void VCMFrameBuffer::SetGofInfo(const GofInfoVP9& gof_info, size_t idx) { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::SetGofInfo"); - _sessionInfo.SetGofInfo(gof_info, idx); - // TODO(asapersson): Consider adding hdr->VP9.ref_picture_id for testing. - _codecSpecificInfo.codecSpecific.VP9.temporal_idx = - gof_info.temporal_idx[idx]; - _codecSpecificInfo.codecSpecific.VP9.temporal_up_switch = - gof_info.temporal_up_switch[idx]; -} - -// Insert packet -VCMFrameBufferEnum VCMFrameBuffer::InsertPacket(const VCMPacket& packet, - int64_t timeInMs, - const FrameData& frame_data) { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::InsertPacket"); - RTC_DCHECK(!(nullptr == packet.dataPtr && packet.sizeBytes > 0)); - if (packet.dataPtr != nullptr) { - _payloadType = packet.payloadType; - } - - if (kStateEmpty == _state) { - // First packet (empty and/or media) inserted into this frame. - // store some info and set some initial values. - SetRtpTimestamp(packet.timestamp); - // We only take the ntp timestamp of the first packet of a frame. - ntp_time_ms_ = packet.ntp_time_ms_; - _codec = packet.codec(); - if (packet.video_header.frame_type != VideoFrameType::kEmptyFrame) { - // first media packet - SetState(kStateIncomplete); - } - } - - size_t oldSize = encoded_image_buffer_ ? encoded_image_buffer_->size() : 0; - uint32_t requiredSizeBytes = - size() + packet.sizeBytes + - (packet.insertStartCode ? kH264StartCodeLengthBytes : 0); - if (requiredSizeBytes > oldSize) { - const uint8_t* prevBuffer = data(); - const uint32_t increments = - requiredSizeBytes / kBufferIncStepSizeBytes + - (requiredSizeBytes % kBufferIncStepSizeBytes > 0); - const uint32_t newSize = oldSize + increments * kBufferIncStepSizeBytes; - if (newSize > kMaxJBFrameSizeBytes) { - RTC_LOG(LS_ERROR) << "Failed to insert packet due to frame being too " - "big."; - return kSizeError; - } - if (data() == nullptr) { - encoded_image_buffer_ = EncodedImageBuffer::Create(newSize); - SetEncodedData(encoded_image_buffer_); - set_size(0); - } else { - RTC_CHECK(encoded_image_buffer_ != nullptr); - RTC_DCHECK_EQ(encoded_image_buffer_->data(), data()); - encoded_image_buffer_->Realloc(newSize); - } - _sessionInfo.UpdateDataPointers(prevBuffer, data()); - } - - if (packet.width() > 0 && packet.height() > 0) { - _encodedWidth = packet.width(); - _encodedHeight = packet.height(); - } - - // Don't copy payload specific data for empty packets (e.g padding packets). - if (packet.sizeBytes > 0) - CopyCodecSpecific(&packet.video_header); - - int retVal = _sessionInfo.InsertPacket( - packet, encoded_image_buffer_ ? encoded_image_buffer_->data() : nullptr, - frame_data); - if (retVal == -1) { - return kSizeError; - } else if (retVal == -2) { - return kDuplicatePacket; - } else if (retVal == -3) { - return kOutOfBoundsPacket; - } - // update size - set_size(size() + static_cast(retVal)); - - _latestPacketTimeMs = timeInMs; - - // http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/ - // ts_126114v120700p.pdf Section 7.4.5. - // The MTSI client shall add the payload bytes as defined in this clause - // onto the last RTP packet in each group of packets which make up a key - // frame (I-frame or IDR frame in H.264 (AVC), or an IRAP picture in H.265 - // (HEVC)). - if (packet.markerBit) { - rotation_ = packet.video_header.rotation; - content_type_ = packet.video_header.content_type; - if (packet.video_header.video_timing.flags != VideoSendTiming::kInvalid) { - timing_.encode_start_ms = - ntp_time_ms_ + packet.video_header.video_timing.encode_start_delta_ms; - timing_.encode_finish_ms = - ntp_time_ms_ + - packet.video_header.video_timing.encode_finish_delta_ms; - timing_.packetization_finish_ms = - ntp_time_ms_ + - packet.video_header.video_timing.packetization_finish_delta_ms; - timing_.pacer_exit_ms = - ntp_time_ms_ + packet.video_header.video_timing.pacer_exit_delta_ms; - timing_.network_timestamp_ms = - ntp_time_ms_ + - packet.video_header.video_timing.network_timestamp_delta_ms; - timing_.network2_timestamp_ms = - ntp_time_ms_ + - packet.video_header.video_timing.network2_timestamp_delta_ms; - } - timing_.flags = packet.video_header.video_timing.flags; - } - - if (packet.is_first_packet_in_frame()) { - SetPlayoutDelay(packet.video_header.playout_delay); - } - - if (_sessionInfo.complete()) { - SetState(kStateComplete); - return kCompleteSession; - } - return kIncomplete; -} - -int64_t VCMFrameBuffer::LatestPacketTimeMs() const { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::LatestPacketTimeMs"); - return _latestPacketTimeMs; -} - -void VCMFrameBuffer::IncrementNackCount() { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::IncrementNackCount"); - _nackCount++; -} - -int16_t VCMFrameBuffer::GetNackCount() const { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::GetNackCount"); - return _nackCount; -} - -bool VCMFrameBuffer::HaveFirstPacket() const { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::HaveFirstPacket"); - return _sessionInfo.HaveFirstPacket(); -} - -int VCMFrameBuffer::NumPackets() const { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::NumPackets"); - return _sessionInfo.NumPackets(); -} - -void VCMFrameBuffer::Reset() { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::Reset"); - set_size(0); - _sessionInfo.Reset(); - _payloadType = 0; - _nackCount = 0; - _latestPacketTimeMs = -1; - _state = kStateEmpty; - VCMEncodedFrame::Reset(); -} - -// Set state of frame -void VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state) { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::SetState"); - if (_state == state) { - return; - } - switch (state) { - case kStateIncomplete: - // we can go to this state from state kStateEmpty - RTC_DCHECK_EQ(_state, kStateEmpty); - - // Do nothing, we received a packet - break; - - case kStateComplete: - RTC_DCHECK(_state == kStateEmpty || _state == kStateIncomplete); - - break; - - case kStateEmpty: - // Should only be set to empty through Reset(). - RTC_DCHECK_NOTREACHED(); - break; - } - _state = state; -} - -// Get current state of frame -VCMFrameBufferStateEnum VCMFrameBuffer::GetState() const { - return _state; -} - -void VCMFrameBuffer::PrepareForDecode(bool continuous) { - TRACE_EVENT0("webrtc", "VCMFrameBuffer::PrepareForDecode"); - size_t bytes_removed = _sessionInfo.MakeDecodable(); - set_size(size() - bytes_removed); - // Transfer frame information to EncodedFrame and create any codec - // specific information. - _frameType = _sessionInfo.FrameType(); - _missingFrame = !continuous; -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/frame_buffer.h b/modules/video_coding/deprecated/frame_buffer.h deleted file mode 100644 index 18c36529b31..00000000000 --- a/modules/video_coding/deprecated/frame_buffer.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_FRAME_BUFFER_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_FRAME_BUFFER_H_ - -#include -#include - -#include - -#include "api/scoped_refptr.h" -#include "api/video/encoded_image.h" -#include "api/video/video_frame_type.h" -#include "modules/video_coding/codecs/h264/include/h264_globals.h" -#include "modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/deprecated/session_info.h" -#include "modules/video_coding/encoded_frame.h" - -namespace webrtc { - -class VCMFrameBuffer : public VCMEncodedFrame { - public: - VCMFrameBuffer(); - virtual ~VCMFrameBuffer(); - - virtual void Reset(); - - VCMFrameBufferEnum InsertPacket(const VCMPacket& packet, - int64_t timeInMs, - const FrameData& frame_data); - - // State - // Get current state of frame - VCMFrameBufferStateEnum GetState() const; - void PrepareForDecode(bool continuous); - - bool IsSessionComplete() const; - bool HaveFirstPacket() const; - int NumPackets() const; - - // Sequence numbers - // Get lowest packet sequence number in frame - int32_t GetLowSeqNum() const; - // Get highest packet sequence number in frame - int32_t GetHighSeqNum() const; - - int PictureId() const; - int TemporalId() const; - bool LayerSync() const; - int Tl0PicId() const; - - std::vector GetNaluInfos() const; - - void SetGofInfo(const GofInfoVP9& gof_info, size_t idx); - - // Increments a counter to keep track of the number of packets of this frame - // which were NACKed before they arrived. - void IncrementNackCount(); - // Returns the number of packets of this frame which were NACKed before they - // arrived. - int16_t GetNackCount() const; - - int64_t LatestPacketTimeMs() const; - - VideoFrameType FrameType() const; - - private: - void SetState(VCMFrameBufferStateEnum state); // Set state of frame - - VCMFrameBufferStateEnum _state; // Current state of the frame - // Set with SetEncodedData, but keep pointer to the concrete class here, to - // enable reallocation and mutation. - scoped_refptr encoded_image_buffer_; - VCMSessionInfo _sessionInfo; - uint16_t _nackCount; - int64_t _latestPacketTimeMs; -}; - -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_FRAME_BUFFER_H_ diff --git a/modules/video_coding/deprecated/jitter_buffer.cc b/modules/video_coding/deprecated/jitter_buffer.cc deleted file mode 100644 index dea2437a694..00000000000 --- a/modules/video_coding/deprecated/jitter_buffer.cc +++ /dev/null @@ -1,907 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include "modules/video_coding/deprecated/jitter_buffer.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "api/field_trials_view.h" -#include "api/units/data_size.h" -#include "api/units/timestamp.h" -#include "api/video/video_frame_type.h" -#include "modules/include/module_common_types_public.h" -#include "modules/video_coding/deprecated/decoding_state.h" -#include "modules/video_coding/deprecated/event_wrapper.h" -#include "modules/video_coding/deprecated/frame_buffer.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/deprecated/session_info.h" -#include "modules/video_coding/timing/inter_frame_delay_variation_calculator.h" -#include "modules/video_coding/timing/jitter_estimator.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/synchronization/mutex.h" -#include "system_wrappers/include/clock.h" - -namespace webrtc { -// Use this rtt if no value has been reported. -static const int64_t kDefaultRtt = 200; - -typedef std::pair FrameListPair; - -bool IsKeyFrame(FrameListPair pair) { - return pair.second->FrameType() == VideoFrameType::kVideoFrameKey; -} - -bool HasNonEmptyState(FrameListPair pair) { - return pair.second->GetState() != kStateEmpty; -} - -void FrameList::InsertFrame(VCMFrameBuffer* frame) { - insert(rbegin().base(), FrameListPair(frame->RtpTimestamp(), frame)); -} - -VCMFrameBuffer* FrameList::PopFrame(uint32_t timestamp) { - FrameList::iterator it = find(timestamp); - if (it == end()) - return nullptr; - VCMFrameBuffer* frame = it->second; - erase(it); - return frame; -} - -VCMFrameBuffer* FrameList::Front() const { - return begin()->second; -} - -VCMFrameBuffer* FrameList::Back() const { - return rbegin()->second; -} - -int FrameList::RecycleFramesUntilKeyFrame(FrameList::iterator* key_frame_it, - UnorderedFrameList* free_frames) { - int drop_count = 0; - FrameList::iterator it = begin(); - while (!empty()) { - // Throw at least one frame. - it->second->Reset(); - free_frames->push_back(it->second); - erase(it++); - ++drop_count; - if (it != end() && - it->second->FrameType() == VideoFrameType::kVideoFrameKey) { - *key_frame_it = it; - return drop_count; - } - } - *key_frame_it = end(); - return drop_count; -} - -void FrameList::CleanUpOldOrEmptyFrames(VCMDecodingState* decoding_state, - UnorderedFrameList* free_frames) { - while (!empty()) { - VCMFrameBuffer* oldest_frame = Front(); - bool remove_frame = false; - if (oldest_frame->GetState() == kStateEmpty && size() > 1) { - // This frame is empty, try to update the last decoded state and drop it - // if successful. - remove_frame = decoding_state->UpdateEmptyFrame(oldest_frame); - } else { - remove_frame = decoding_state->IsOldFrame(oldest_frame); - } - if (!remove_frame) { - break; - } - free_frames->push_back(oldest_frame); - erase(begin()); - } -} - -void FrameList::Reset(UnorderedFrameList* free_frames) { - while (!empty()) { - begin()->second->Reset(); - free_frames->push_back(begin()->second); - erase(begin()); - } -} - -VCMJitterBuffer::VCMJitterBuffer(Clock* clock, - std::unique_ptr event, - const FieldTrialsView& field_trials) - : clock_(clock), - running_(false), - frame_event_(std::move(event)), - max_number_of_frames_(kStartNumberOfFrames), - free_frames_(), - decodable_frames_(), - incomplete_frames_(), - last_decoded_state_(), - first_packet_since_reset_(true), - num_consecutive_old_packets_(0), - num_packets_(0), - num_duplicated_packets_(0), - jitter_estimate_(clock, field_trials), - missing_sequence_numbers_(SequenceNumberLessThan()), - latest_received_sequence_number_(0), - max_nack_list_size_(0), - max_packet_age_to_nack_(0), - max_incomplete_time_ms_(0), - average_packets_per_frame_(0.0f), - frame_counter_(0) { - for (int i = 0; i < kStartNumberOfFrames; i++) - free_frames_.push_back(new VCMFrameBuffer()); -} - -VCMJitterBuffer::~VCMJitterBuffer() { - Stop(); - for (UnorderedFrameList::iterator it = free_frames_.begin(); - it != free_frames_.end(); ++it) { - delete *it; - } - for (FrameList::iterator it = incomplete_frames_.begin(); - it != incomplete_frames_.end(); ++it) { - delete it->second; - } - for (FrameList::iterator it = decodable_frames_.begin(); - it != decodable_frames_.end(); ++it) { - delete it->second; - } -} - -void VCMJitterBuffer::Start() { - MutexLock lock(&mutex_); - running_ = true; - - num_consecutive_old_packets_ = 0; - num_packets_ = 0; - num_duplicated_packets_ = 0; - - // Start in a non-signaled state. - waiting_for_completion_.frame_size = 0; - waiting_for_completion_.timestamp = 0; - waiting_for_completion_.latest_packet_time = -1; - first_packet_since_reset_ = true; - last_decoded_state_.Reset(); - - decodable_frames_.Reset(&free_frames_); - incomplete_frames_.Reset(&free_frames_); -} - -void VCMJitterBuffer::Stop() { - MutexLock lock(&mutex_); - running_ = false; - last_decoded_state_.Reset(); - - // Make sure we wake up any threads waiting on these events. - frame_event_->Set(); -} - -bool VCMJitterBuffer::Running() const { - MutexLock lock(&mutex_); - return running_; -} - -void VCMJitterBuffer::Flush() { - MutexLock lock(&mutex_); - FlushInternal(); -} - -void VCMJitterBuffer::FlushInternal() { - decodable_frames_.Reset(&free_frames_); - incomplete_frames_.Reset(&free_frames_); - last_decoded_state_.Reset(); // TODO(mikhal): sync reset. - num_consecutive_old_packets_ = 0; - // Also reset the jitter and delay estimates - jitter_estimate_.Reset(); - inter_frame_delay_.Reset(); - waiting_for_completion_.frame_size = 0; - waiting_for_completion_.timestamp = 0; - waiting_for_completion_.latest_packet_time = -1; - first_packet_since_reset_ = true; - missing_sequence_numbers_.clear(); -} - -int VCMJitterBuffer::num_packets() const { - MutexLock lock(&mutex_); - return num_packets_; -} - -int VCMJitterBuffer::num_duplicated_packets() const { - MutexLock lock(&mutex_); - return num_duplicated_packets_; -} - -// Returns immediately or a `max_wait_time_ms` ms event hang waiting for a -// complete frame, `max_wait_time_ms` decided by caller. -VCMEncodedFrame* VCMJitterBuffer::NextCompleteFrame(uint32_t max_wait_time_ms) { - MutexLock lock(&mutex_); - if (!running_) { - return nullptr; - } - CleanUpOldOrEmptyFrames(); - - if (decodable_frames_.empty() || - decodable_frames_.Front()->GetState() != kStateComplete) { - const int64_t end_wait_time_ms = - clock_->TimeInMilliseconds() + max_wait_time_ms; - int64_t wait_time_ms = max_wait_time_ms; - while (wait_time_ms > 0) { - mutex_.Unlock(); - const EventTypeWrapper ret = - frame_event_->Wait(static_cast(wait_time_ms)); - mutex_.Lock(); - if (ret == kEventSignaled) { - // Are we shutting down the jitter buffer? - if (!running_) { - return nullptr; - } - // Finding oldest frame ready for decoder. - CleanUpOldOrEmptyFrames(); - if (decodable_frames_.empty() || - decodable_frames_.Front()->GetState() != kStateComplete) { - wait_time_ms = end_wait_time_ms - clock_->TimeInMilliseconds(); - } else { - break; - } - } else { - break; - } - } - } - if (decodable_frames_.empty() || - decodable_frames_.Front()->GetState() != kStateComplete) { - return nullptr; - } - return decodable_frames_.Front(); -} - -VCMEncodedFrame* VCMJitterBuffer::ExtractAndSetDecode(uint32_t timestamp) { - MutexLock lock(&mutex_); - if (!running_) { - return nullptr; - } - // Extract the frame with the desired timestamp. - VCMFrameBuffer* frame = decodable_frames_.PopFrame(timestamp); - bool continuous = true; - if (!frame) { - frame = incomplete_frames_.PopFrame(timestamp); - if (frame) - continuous = last_decoded_state_.ContinuousFrame(frame); - else - return nullptr; - } - // Frame pulled out from jitter buffer, update the jitter estimate. - const bool retransmitted = (frame->GetNackCount() > 0); - if (retransmitted) { - jitter_estimate_.FrameNacked(); - } else if (frame->size() > 0) { - // Ignore retransmitted and empty frames. - if (waiting_for_completion_.latest_packet_time >= 0) { - UpdateJitterEstimate(waiting_for_completion_, true); - } - if (frame->GetState() == kStateComplete) { - UpdateJitterEstimate(*frame, false); - } else { - // Wait for this one to get complete. - waiting_for_completion_.frame_size = frame->size(); - waiting_for_completion_.latest_packet_time = frame->LatestPacketTimeMs(); - waiting_for_completion_.timestamp = frame->RtpTimestamp(); - } - } - - // The state must be changed to decoding before cleaning up zero sized - // frames to avoid empty frames being cleaned up and then given to the - // decoder. Propagates the missing_frame bit. - frame->PrepareForDecode(continuous); - - // We have a frame - update the last decoded state and nack list. - last_decoded_state_.SetState(frame); - DropPacketsFromNackList(last_decoded_state_.sequence_num()); - - UpdateAveragePacketsPerFrame(frame->NumPackets()); - - return frame; -} - -// Release frame when done with decoding. Should never be used to release -// frames from within the jitter buffer. -void VCMJitterBuffer::ReleaseFrame(VCMEncodedFrame* frame) { - RTC_CHECK(frame != nullptr); - MutexLock lock(&mutex_); - VCMFrameBuffer* frame_buffer = static_cast(frame); - RecycleFrameBuffer(frame_buffer); -} - -// Gets frame to use for this timestamp. If no match, get empty frame. -VCMFrameBufferEnum VCMJitterBuffer::GetFrame(const VCMPacket& packet, - VCMFrameBuffer** frame, - FrameList** frame_list) { - *frame = incomplete_frames_.PopFrame(packet.timestamp); - if (*frame != nullptr) { - *frame_list = &incomplete_frames_; - return kNoError; - } - *frame = decodable_frames_.PopFrame(packet.timestamp); - if (*frame != nullptr) { - *frame_list = &decodable_frames_; - return kNoError; - } - - *frame_list = nullptr; - // No match, return empty frame. - *frame = GetEmptyFrame(); - if (*frame == nullptr) { - // No free frame! Try to reclaim some... - RTC_LOG(LS_WARNING) << "Unable to get empty frame; Recycling."; - bool found_key_frame = RecycleFramesUntilKeyFrame(); - *frame = GetEmptyFrame(); - RTC_CHECK(*frame); - if (!found_key_frame) { - RecycleFrameBuffer(*frame); - return kFlushIndicator; - } - } - (*frame)->Reset(); - return kNoError; -} - -int64_t VCMJitterBuffer::LastPacketTime(const VCMEncodedFrame* frame, - bool* retransmitted) const { - RTC_DCHECK(retransmitted); - MutexLock lock(&mutex_); - const VCMFrameBuffer* frame_buffer = - static_cast(frame); - *retransmitted = (frame_buffer->GetNackCount() > 0); - return frame_buffer->LatestPacketTimeMs(); -} - -VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, - bool* retransmitted) { - MutexLock lock(&mutex_); - - ++num_packets_; - // Does this packet belong to an old frame? - if (last_decoded_state_.IsOldPacket(&packet)) { - // Account only for media packets. - if (packet.sizeBytes > 0) { - num_consecutive_old_packets_++; - } - // Update last decoded sequence number if the packet arrived late and - // belongs to a frame with a timestamp equal to the last decoded - // timestamp. - last_decoded_state_.UpdateOldPacket(&packet); - DropPacketsFromNackList(last_decoded_state_.sequence_num()); - - // Also see if this old packet made more incomplete frames continuous. - FindAndInsertContinuousFramesWithState(last_decoded_state_); - - if (num_consecutive_old_packets_ > kMaxConsecutiveOldPackets) { - RTC_LOG(LS_WARNING) - << num_consecutive_old_packets_ - << " consecutive old packets received. Flushing the jitter buffer."; - FlushInternal(); - return kFlushIndicator; - } - return kOldPacket; - } - - num_consecutive_old_packets_ = 0; - - VCMFrameBuffer* frame; - FrameList* frame_list; - const VCMFrameBufferEnum error = GetFrame(packet, &frame, &frame_list); - if (error != kNoError) - return error; - - Timestamp now = clock_->CurrentTime(); - // We are keeping track of the first and latest seq numbers, and - // the number of wraps to be able to calculate how many packets we expect. - if (first_packet_since_reset_) { - // Now it's time to start estimating jitter - // reset the delay estimate. - inter_frame_delay_.Reset(); - } - - // Empty packets may bias the jitter estimate (lacking size component), - // therefore don't let empty packet trigger the following updates: - if (packet.video_header.frame_type != VideoFrameType::kEmptyFrame) { - if (waiting_for_completion_.timestamp == packet.timestamp) { - // This can get bad if we have a lot of duplicate packets, - // we will then count some packet multiple times. - waiting_for_completion_.frame_size += packet.sizeBytes; - waiting_for_completion_.latest_packet_time = now.ms(); - } else if (waiting_for_completion_.latest_packet_time >= 0 && - waiting_for_completion_.latest_packet_time + 2000 <= now.ms()) { - // A packet should never be more than two seconds late - UpdateJitterEstimate(waiting_for_completion_, true); - waiting_for_completion_.latest_packet_time = -1; - waiting_for_completion_.frame_size = 0; - waiting_for_completion_.timestamp = 0; - } - } - - VCMFrameBufferStateEnum previous_state = frame->GetState(); - // Insert packet. - FrameData frame_data; - frame_data.rtt_ms = kDefaultRtt; - frame_data.rolling_average_packets_per_frame = average_packets_per_frame_; - VCMFrameBufferEnum buffer_state = - frame->InsertPacket(packet, now.ms(), frame_data); - - if (buffer_state > 0) { - if (first_packet_since_reset_) { - latest_received_sequence_number_ = packet.seqNum; - first_packet_since_reset_ = false; - } else { - if (IsPacketRetransmitted(packet)) { - frame->IncrementNackCount(); - } - if (!UpdateNackList(packet.seqNum) && - packet.video_header.frame_type != VideoFrameType::kVideoFrameKey) { - buffer_state = kFlushIndicator; - } - - latest_received_sequence_number_ = - LatestSequenceNumber(latest_received_sequence_number_, packet.seqNum); - } - } - - // Is the frame already in the decodable list? - bool continuous = IsContinuous(*frame); - switch (buffer_state) { - case kGeneralError: - case kTimeStampError: - case kSizeError: { - RecycleFrameBuffer(frame); - break; - } - case kCompleteSession: { - if (previous_state != kStateComplete) { - if (continuous) { - // Signal that we have a complete session. - frame_event_->Set(); - } - } - - *retransmitted = (frame->GetNackCount() > 0); - if (continuous) { - decodable_frames_.InsertFrame(frame); - FindAndInsertContinuousFrames(*frame); - } else { - incomplete_frames_.InsertFrame(frame); - } - break; - } - case kIncomplete: { - if (frame->GetState() == kStateEmpty && - last_decoded_state_.UpdateEmptyFrame(frame)) { - RecycleFrameBuffer(frame); - return kNoError; - } else { - incomplete_frames_.InsertFrame(frame); - } - break; - } - case kNoError: - case kOutOfBoundsPacket: - case kDuplicatePacket: { - // Put back the frame where it came from. - if (frame_list != nullptr) { - frame_list->InsertFrame(frame); - } else { - RecycleFrameBuffer(frame); - } - ++num_duplicated_packets_; - break; - } - case kFlushIndicator: - RecycleFrameBuffer(frame); - return kFlushIndicator; - default: - RTC_DCHECK_NOTREACHED(); - } - return buffer_state; -} - -bool VCMJitterBuffer::IsContinuousInState( - const VCMFrameBuffer& frame, - const VCMDecodingState& decoding_state) const { - // Is this frame complete and continuous? - return (frame.GetState() == kStateComplete) && - decoding_state.ContinuousFrame(&frame); -} - -bool VCMJitterBuffer::IsContinuous(const VCMFrameBuffer& frame) const { - if (IsContinuousInState(frame, last_decoded_state_)) { - return true; - } - VCMDecodingState decoding_state; - decoding_state.CopyFrom(last_decoded_state_); - for (FrameList::const_iterator it = decodable_frames_.begin(); - it != decodable_frames_.end(); ++it) { - VCMFrameBuffer* decodable_frame = it->second; - if (IsNewerTimestamp(decodable_frame->RtpTimestamp(), - frame.RtpTimestamp())) { - break; - } - decoding_state.SetState(decodable_frame); - if (IsContinuousInState(frame, decoding_state)) { - return true; - } - } - return false; -} - -void VCMJitterBuffer::FindAndInsertContinuousFrames( - const VCMFrameBuffer& new_frame) { - VCMDecodingState decoding_state; - decoding_state.CopyFrom(last_decoded_state_); - decoding_state.SetState(&new_frame); - FindAndInsertContinuousFramesWithState(decoding_state); -} - -void VCMJitterBuffer::FindAndInsertContinuousFramesWithState( - const VCMDecodingState& original_decoded_state) { - // Copy original_decoded_state so we can move the state forward with each - // decodable frame we find. - VCMDecodingState decoding_state; - decoding_state.CopyFrom(original_decoded_state); - - // When temporal layers are available, we search for a complete or decodable - // frame until we hit one of the following: - // 1. Continuous base or sync layer. - // 2. The end of the list was reached. - for (FrameList::iterator it = incomplete_frames_.begin(); - it != incomplete_frames_.end();) { - VCMFrameBuffer* frame = it->second; - if (IsNewerTimestamp(original_decoded_state.time_stamp(), - frame->RtpTimestamp())) { - ++it; - continue; - } - if (IsContinuousInState(*frame, decoding_state)) { - decodable_frames_.InsertFrame(frame); - incomplete_frames_.erase(it++); - decoding_state.SetState(frame); - } else if (frame->TemporalId() <= 0) { - break; - } else { - ++it; - } - } -} - -uint32_t VCMJitterBuffer::EstimatedJitterMs() { - MutexLock lock(&mutex_); - const double rtt_mult = 1.0f; - return jitter_estimate_.GetJitterEstimate(rtt_mult, std::nullopt).ms(); -} - -void VCMJitterBuffer::SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms) { - MutexLock lock(&mutex_); - RTC_DCHECK_GE(max_packet_age_to_nack, 0); - RTC_DCHECK_GE(max_incomplete_time_ms_, 0); - max_nack_list_size_ = max_nack_list_size; - max_packet_age_to_nack_ = max_packet_age_to_nack; - max_incomplete_time_ms_ = max_incomplete_time_ms; -} - -int VCMJitterBuffer::NonContinuousOrIncompleteDuration() { - if (incomplete_frames_.empty()) { - return 0; - } - uint32_t start_timestamp = incomplete_frames_.Front()->RtpTimestamp(); - if (!decodable_frames_.empty()) { - start_timestamp = decodable_frames_.Back()->RtpTimestamp(); - } - return incomplete_frames_.Back()->RtpTimestamp() - start_timestamp; -} - -uint16_t VCMJitterBuffer::EstimatedLowSequenceNumber( - const VCMFrameBuffer& frame) const { - RTC_DCHECK_GE(frame.GetLowSeqNum(), 0); - if (frame.HaveFirstPacket()) - return frame.GetLowSeqNum(); - - // This estimate is not accurate if more than one packet with lower sequence - // number is lost. - return frame.GetLowSeqNum() - 1; -} - -std::vector VCMJitterBuffer::GetNackList(bool* request_key_frame) { - MutexLock lock(&mutex_); - *request_key_frame = false; - if (last_decoded_state_.in_initial_state()) { - VCMFrameBuffer* next_frame = NextFrame(); - const bool first_frame_is_key = - next_frame && - next_frame->FrameType() == VideoFrameType::kVideoFrameKey && - next_frame->HaveFirstPacket(); - if (!first_frame_is_key) { - bool have_non_empty_frame = - decodable_frames_.end() != find_if(decodable_frames_.begin(), - decodable_frames_.end(), - HasNonEmptyState); - if (!have_non_empty_frame) { - have_non_empty_frame = - incomplete_frames_.end() != find_if(incomplete_frames_.begin(), - incomplete_frames_.end(), - HasNonEmptyState); - } - bool found_key_frame = RecycleFramesUntilKeyFrame(); - if (!found_key_frame) { - *request_key_frame = have_non_empty_frame; - return std::vector(); - } - } - } - if (TooLargeNackList()) { - *request_key_frame = !HandleTooLargeNackList(); - } - if (max_incomplete_time_ms_ > 0) { - int non_continuous_incomplete_duration = - NonContinuousOrIncompleteDuration(); - if (non_continuous_incomplete_duration > 90 * max_incomplete_time_ms_) { - RTC_LOG_F(LS_WARNING) << "Too long non-decodable duration: " - << non_continuous_incomplete_duration << " > " - << 90 * max_incomplete_time_ms_; - FrameList::reverse_iterator rit = find_if( - incomplete_frames_.rbegin(), incomplete_frames_.rend(), IsKeyFrame); - if (rit == incomplete_frames_.rend()) { - // Request a key frame if we don't have one already. - *request_key_frame = true; - return std::vector(); - } else { - // Skip to the last key frame. If it's incomplete we will start - // NACKing it. - // Note that the estimated low sequence number is correct for VP8 - // streams because only the first packet of a key frame is marked. - last_decoded_state_.Reset(); - DropPacketsFromNackList(EstimatedLowSequenceNumber(*rit->second)); - } - } - } - std::vector nack_list(missing_sequence_numbers_.begin(), - missing_sequence_numbers_.end()); - return nack_list; -} - -VCMFrameBuffer* VCMJitterBuffer::NextFrame() const { - if (!decodable_frames_.empty()) - return decodable_frames_.Front(); - if (!incomplete_frames_.empty()) - return incomplete_frames_.Front(); - return nullptr; -} - -bool VCMJitterBuffer::UpdateNackList(uint16_t sequence_number) { - // Make sure we don't add packets which are already too old to be decoded. - if (!last_decoded_state_.in_initial_state()) { - latest_received_sequence_number_ = LatestSequenceNumber( - latest_received_sequence_number_, last_decoded_state_.sequence_num()); - } - if (IsNewerSequenceNumber(sequence_number, - latest_received_sequence_number_)) { - // Push any missing sequence numbers to the NACK list. - for (uint16_t i = latest_received_sequence_number_ + 1; - IsNewerSequenceNumber(sequence_number, i); ++i) { - missing_sequence_numbers_.insert(missing_sequence_numbers_.end(), i); - } - if (TooLargeNackList() && !HandleTooLargeNackList()) { - RTC_LOG(LS_WARNING) << "Requesting key frame due to too large NACK list."; - return false; - } - if (MissingTooOldPacket(sequence_number) && - !HandleTooOldPackets(sequence_number)) { - RTC_LOG(LS_WARNING) - << "Requesting key frame due to missing too old packets"; - return false; - } - } else { - missing_sequence_numbers_.erase(sequence_number); - } - return true; -} - -bool VCMJitterBuffer::TooLargeNackList() const { - return missing_sequence_numbers_.size() > max_nack_list_size_; -} - -bool VCMJitterBuffer::HandleTooLargeNackList() { - // Recycle frames until the NACK list is small enough. It is likely cheaper to - // request a key frame than to retransmit this many missing packets. - RTC_LOG_F(LS_WARNING) << "NACK list has grown too large: " - << missing_sequence_numbers_.size() << " > " - << max_nack_list_size_; - bool key_frame_found = false; - while (TooLargeNackList()) { - key_frame_found = RecycleFramesUntilKeyFrame(); - } - return key_frame_found; -} - -bool VCMJitterBuffer::MissingTooOldPacket( - uint16_t latest_sequence_number) const { - if (missing_sequence_numbers_.empty()) { - return false; - } - const uint16_t age_of_oldest_missing_packet = - latest_sequence_number - *missing_sequence_numbers_.begin(); - // Recycle frames if the NACK list contains too old sequence numbers as - // the packets may have already been dropped by the sender. - return age_of_oldest_missing_packet > max_packet_age_to_nack_; -} - -bool VCMJitterBuffer::HandleTooOldPackets(uint16_t latest_sequence_number) { - bool key_frame_found = false; - const uint16_t age_of_oldest_missing_packet = - latest_sequence_number - *missing_sequence_numbers_.begin(); - RTC_LOG_F(LS_WARNING) << "NACK list contains too old sequence numbers: " - << age_of_oldest_missing_packet << " > " - << max_packet_age_to_nack_; - while (MissingTooOldPacket(latest_sequence_number)) { - key_frame_found = RecycleFramesUntilKeyFrame(); - } - return key_frame_found; -} - -void VCMJitterBuffer::DropPacketsFromNackList( - uint16_t last_decoded_sequence_number) { - // Erase all sequence numbers from the NACK list which we won't need any - // longer. - missing_sequence_numbers_.erase( - missing_sequence_numbers_.begin(), - missing_sequence_numbers_.upper_bound(last_decoded_sequence_number)); -} - -VCMFrameBuffer* VCMJitterBuffer::GetEmptyFrame() { - if (free_frames_.empty()) { - if (!TryToIncreaseJitterBufferSize()) { - return nullptr; - } - } - VCMFrameBuffer* frame = free_frames_.front(); - free_frames_.pop_front(); - return frame; -} - -bool VCMJitterBuffer::TryToIncreaseJitterBufferSize() { - if (max_number_of_frames_ >= kMaxNumberOfFrames) - return false; - free_frames_.push_back(new VCMFrameBuffer()); - ++max_number_of_frames_; - return true; -} - -// Recycle oldest frames up to a key frame, used if jitter buffer is completely -// full. -bool VCMJitterBuffer::RecycleFramesUntilKeyFrame() { - // First release incomplete frames, and only release decodable frames if there - // are no incomplete ones. - FrameList::iterator key_frame_it; - bool key_frame_found = false; - int dropped_frames = 0; - dropped_frames += incomplete_frames_.RecycleFramesUntilKeyFrame( - &key_frame_it, &free_frames_); - key_frame_found = key_frame_it != incomplete_frames_.end(); - if (dropped_frames == 0) { - dropped_frames += decodable_frames_.RecycleFramesUntilKeyFrame( - &key_frame_it, &free_frames_); - key_frame_found = key_frame_it != decodable_frames_.end(); - } - if (key_frame_found) { - RTC_LOG(LS_INFO) << "Found key frame while dropping frames."; - // Reset last decoded state to make sure the next frame decoded is a key - // frame, and start NACKing from here. - last_decoded_state_.Reset(); - DropPacketsFromNackList(EstimatedLowSequenceNumber(*key_frame_it->second)); - } else if (decodable_frames_.empty()) { - // All frames dropped. Reset the decoding state and clear missing sequence - // numbers as we're starting fresh. - last_decoded_state_.Reset(); - missing_sequence_numbers_.clear(); - } - return key_frame_found; -} - -void VCMJitterBuffer::UpdateAveragePacketsPerFrame(int current_number_packets) { - if (frame_counter_ > kFastConvergeThreshold) { - average_packets_per_frame_ = - average_packets_per_frame_ * (1 - kNormalConvergeMultiplier) + - current_number_packets * kNormalConvergeMultiplier; - } else if (frame_counter_ > 0) { - average_packets_per_frame_ = - average_packets_per_frame_ * (1 - kFastConvergeMultiplier) + - current_number_packets * kFastConvergeMultiplier; - frame_counter_++; - } else { - average_packets_per_frame_ = current_number_packets; - frame_counter_++; - } -} - -// Must be called under the critical section `mutex_`. -void VCMJitterBuffer::CleanUpOldOrEmptyFrames() { - decodable_frames_.CleanUpOldOrEmptyFrames(&last_decoded_state_, - &free_frames_); - incomplete_frames_.CleanUpOldOrEmptyFrames(&last_decoded_state_, - &free_frames_); - if (!last_decoded_state_.in_initial_state()) { - DropPacketsFromNackList(last_decoded_state_.sequence_num()); - } -} - -// Must be called from within `mutex_`. -bool VCMJitterBuffer::IsPacketRetransmitted(const VCMPacket& packet) const { - return missing_sequence_numbers_.find(packet.seqNum) != - missing_sequence_numbers_.end(); -} - -// Must be called under the critical section `mutex_`. Should never be -// called with retransmitted frames, they must be filtered out before this -// function is called. -void VCMJitterBuffer::UpdateJitterEstimate(const VCMJitterSample& sample, - bool incomplete_frame) { - if (sample.latest_packet_time == -1) { - return; - } - UpdateJitterEstimate(sample.latest_packet_time, sample.timestamp, - sample.frame_size, incomplete_frame); -} - -// Must be called under the critical section mutex_. Should never be -// called with retransmitted frames, they must be filtered out before this -// function is called. -void VCMJitterBuffer::UpdateJitterEstimate(const VCMFrameBuffer& frame, - bool incomplete_frame) { - if (frame.LatestPacketTimeMs() == -1) { - return; - } - // No retransmitted frames should be a part of the jitter - // estimate. - UpdateJitterEstimate(frame.LatestPacketTimeMs(), frame.RtpTimestamp(), - frame.size(), incomplete_frame); -} - -// Must be called under the critical section `mutex_`. Should never be -// called with retransmitted frames, they must be filtered out before this -// function is called. -void VCMJitterBuffer::UpdateJitterEstimate(int64_t latest_packet_time_ms, - uint32_t timestamp, - unsigned int frame_size, - bool /*incomplete_frame*/) { - if (latest_packet_time_ms == -1) { - return; - } - auto frame_delay = inter_frame_delay_.Calculate( - timestamp, Timestamp::Millis(latest_packet_time_ms)); - - bool not_reordered = frame_delay.has_value(); - // Filter out frames which have been reordered in time by the network - if (not_reordered) { - // Update the jitter estimate with the new samples - jitter_estimate_.UpdateEstimate(*frame_delay, DataSize::Bytes(frame_size)); - } -} - -void VCMJitterBuffer::RecycleFrameBuffer(VCMFrameBuffer* frame) { - frame->Reset(); - free_frames_.push_back(frame); -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/jitter_buffer.h b/modules/video_coding/deprecated/jitter_buffer.h deleted file mode 100644 index 1657f465739..00000000000 --- a/modules/video_coding/deprecated/jitter_buffer.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "api/field_trials_view.h" -#include "modules/include/module_common_types_public.h" -#include "modules/video_coding/deprecated/decoding_state.h" -#include "modules/video_coding/deprecated/event_wrapper.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/timing/inter_frame_delay_variation_calculator.h" -#include "modules/video_coding/timing/jitter_estimator.h" -#include "rtc_base/synchronization/mutex.h" -#include "rtc_base/thread_annotations.h" - -namespace webrtc { - -// forward declarations -class Clock; -class VCMFrameBuffer; -class VCMPacket; -class VCMEncodedFrame; - -typedef std::list UnorderedFrameList; - -struct VCMJitterSample { - VCMJitterSample() : timestamp(0), frame_size(0), latest_packet_time(-1) {} - uint32_t timestamp; - uint32_t frame_size; - int64_t latest_packet_time; -}; - -class TimestampLessThan { - public: - bool operator()(uint32_t timestamp1, uint32_t timestamp2) const { - return IsNewerTimestamp(timestamp2, timestamp1); - } -}; - -class FrameList - : public std::map { - public: - void InsertFrame(VCMFrameBuffer* frame); - VCMFrameBuffer* PopFrame(uint32_t timestamp); - VCMFrameBuffer* Front() const; - VCMFrameBuffer* Back() const; - int RecycleFramesUntilKeyFrame(FrameList::iterator* key_frame_it, - UnorderedFrameList* free_frames); - void CleanUpOldOrEmptyFrames(VCMDecodingState* decoding_state, - UnorderedFrameList* free_frames); - void Reset(UnorderedFrameList* free_frames); -}; - -class VCMJitterBuffer { - public: - VCMJitterBuffer(Clock* clock, - std::unique_ptr event, - const FieldTrialsView& field_trials); - - ~VCMJitterBuffer(); - - VCMJitterBuffer(const VCMJitterBuffer&) = delete; - VCMJitterBuffer& operator=(const VCMJitterBuffer&) = delete; - - // Initializes and starts jitter buffer. - void Start() RTC_LOCKS_EXCLUDED(mutex_); - - // Signals all internal events and stops the jitter buffer. - void Stop() RTC_LOCKS_EXCLUDED(mutex_); - - // Returns true if the jitter buffer is running. - bool Running() const RTC_LOCKS_EXCLUDED(mutex_); - - // Empty the jitter buffer of all its data. - void Flush() RTC_LOCKS_EXCLUDED(mutex_); - - // Gets number of packets received. - int num_packets() const RTC_LOCKS_EXCLUDED(mutex_); - - // Gets number of duplicated packets received. - int num_duplicated_packets() const RTC_LOCKS_EXCLUDED(mutex_); - - // Wait `max_wait_time_ms` for a complete frame to arrive. - // If found, a pointer to the frame is returned. Returns nullptr otherwise. - VCMEncodedFrame* NextCompleteFrame(uint32_t max_wait_time_ms) - RTC_LOCKS_EXCLUDED(mutex_); - - // Extract frame corresponding to input timestamp. - // Frame will be set to a decoding state. - VCMEncodedFrame* ExtractAndSetDecode(uint32_t timestamp) - RTC_LOCKS_EXCLUDED(mutex_); - - // Releases a frame returned from the jitter buffer, should be called when - // done with decoding. - void ReleaseFrame(VCMEncodedFrame* frame) RTC_LOCKS_EXCLUDED(mutex_); - - // Returns the time in ms when the latest packet was inserted into the frame. - // Retransmitted is set to true if any of the packets belonging to the frame - // has been retransmitted. - int64_t LastPacketTime(const VCMEncodedFrame* frame, - bool* retransmitted) const RTC_LOCKS_EXCLUDED(mutex_); - - // Inserts a packet into a frame returned from GetFrame(). - // If the return value is <= 0, `frame` is invalidated and the pointer must - // be dropped after this function returns. - VCMFrameBufferEnum InsertPacket(const VCMPacket& packet, bool* retransmitted) - RTC_LOCKS_EXCLUDED(mutex_); - - // Returns the estimated jitter in milliseconds. - uint32_t EstimatedJitterMs() RTC_LOCKS_EXCLUDED(mutex_); - - void SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms) RTC_LOCKS_EXCLUDED(mutex_); - - // Returns a list of the sequence numbers currently missing. - std::vector GetNackList(bool* request_key_frame) - RTC_LOCKS_EXCLUDED(mutex_); - - private: - class SequenceNumberLessThan { - public: - bool operator()(const uint16_t& sequence_number1, - const uint16_t& sequence_number2) const { - return IsNewerSequenceNumber(sequence_number2, sequence_number1); - } - }; - typedef std::set SequenceNumberSet; - - // Gets the frame assigned to the timestamp of the packet. May recycle - // existing frames if no free frames are available. Returns an error code if - // failing, or kNoError on success. `frame_list` contains which list the - // packet was in, or NULL if it was not in a FrameList (a new frame). - VCMFrameBufferEnum GetFrame(const VCMPacket& packet, - VCMFrameBuffer** frame, - FrameList** frame_list) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if `frame` is continuous in `decoding_state`, not taking - // decodable frames into account. - bool IsContinuousInState(const VCMFrameBuffer& frame, - const VCMDecodingState& decoding_state) const - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Returns true if `frame` is continuous in the `last_decoded_state_`, taking - // all decodable frames into account. - bool IsContinuous(const VCMFrameBuffer& frame) const - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Looks for frames in `incomplete_frames_` which are continuous in the - // provided `decoded_state`. Starts the search from the timestamp of - // `decoded_state`. - void FindAndInsertContinuousFramesWithState( - const VCMDecodingState& decoded_state) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Looks for frames in `incomplete_frames_` which are continuous in - // `last_decoded_state_` taking all decodable frames into account. Starts - // the search from `new_frame`. - void FindAndInsertContinuousFrames(const VCMFrameBuffer& new_frame) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - VCMFrameBuffer* NextFrame() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Returns true if the NACK list was updated to cover sequence numbers up to - // `sequence_number`. If false a key frame is needed to get into a state where - // we can continue decoding. - bool UpdateNackList(uint16_t sequence_number) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - bool TooLargeNackList() const; - // Returns true if the NACK list was reduced without problem. If false a key - // frame is needed to get into a state where we can continue decoding. - bool HandleTooLargeNackList() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - bool MissingTooOldPacket(uint16_t latest_sequence_number) const - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Returns true if the too old packets was successfully removed from the NACK - // list. If false, a key frame is needed to get into a state where we can - // continue decoding. - bool HandleTooOldPackets(uint16_t latest_sequence_number) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Drops all packets in the NACK list up until `last_decoded_sequence_number`. - void DropPacketsFromNackList(uint16_t last_decoded_sequence_number); - - // Gets an empty frame, creating a new frame if necessary (i.e. increases - // jitter buffer size). - VCMFrameBuffer* GetEmptyFrame() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Attempts to increase the size of the jitter buffer. Returns true on - // success, false otherwise. - bool TryToIncreaseJitterBufferSize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Recycles oldest frames until a key frame is found. Used if jitter buffer is - // completely full. Returns true if a key frame was found. - bool RecycleFramesUntilKeyFrame() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Update rolling average of packets per frame. - void UpdateAveragePacketsPerFrame(int current_number_packets_) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Cleans the frame list in the JB from old/empty frames. - // Should only be called prior to actual use. - void CleanUpOldOrEmptyFrames() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if `packet` is likely to have been retransmitted. - bool IsPacketRetransmitted(const VCMPacket& packet) const; - - // The following three functions update the jitter estimate with the - // payload size, receive time and RTP timestamp of a frame. - void UpdateJitterEstimate(const VCMJitterSample& sample, - bool incomplete_frame); - void UpdateJitterEstimate(const VCMFrameBuffer& frame, bool incomplete_frame); - void UpdateJitterEstimate(int64_t latest_packet_time_ms, - uint32_t timestamp, - unsigned int frame_size, - bool incomplete_frame); - - int NonContinuousOrIncompleteDuration() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - uint16_t EstimatedLowSequenceNumber(const VCMFrameBuffer& frame) const; - - // Reset frame buffer and return it to free_frames_. - void RecycleFrameBuffer(VCMFrameBuffer* frame) - RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Empty the jitter buffer of all its data. - void FlushInternal() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - Clock* clock_; - // If we are running (have started) or not. - bool running_; - mutable Mutex mutex_; - // Event to signal when we have a frame ready for decoder. - std::unique_ptr frame_event_; - // Number of allocated frames. - int max_number_of_frames_; - UnorderedFrameList free_frames_ RTC_GUARDED_BY(mutex_); - FrameList decodable_frames_ RTC_GUARDED_BY(mutex_); - FrameList incomplete_frames_ RTC_GUARDED_BY(mutex_); - VCMDecodingState last_decoded_state_ RTC_GUARDED_BY(mutex_); - bool first_packet_since_reset_; - - // Number of packets in a row that have been too old. - int num_consecutive_old_packets_; - // Number of packets received. - int num_packets_ RTC_GUARDED_BY(mutex_); - // Number of duplicated packets received. - int num_duplicated_packets_ RTC_GUARDED_BY(mutex_); - - // Jitter estimation. - // Filter for estimating jitter. - JitterEstimator jitter_estimate_; - // Calculates network delays used for jitter calculations. - InterFrameDelayVariationCalculator inter_frame_delay_; - VCMJitterSample waiting_for_completion_; - - // Holds the internal NACK list (the missing sequence numbers). - SequenceNumberSet missing_sequence_numbers_; - uint16_t latest_received_sequence_number_; - size_t max_nack_list_size_; - int max_packet_age_to_nack_; // Measured in sequence numbers. - int max_incomplete_time_ms_; - - // Estimated rolling average of packets per frame - float average_packets_per_frame_; - // average_packets_per_frame converges fast if we have fewer than this many - // frames. - int frame_counter_; -}; -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_H_ diff --git a/modules/video_coding/deprecated/jitter_buffer_common.h b/modules/video_coding/deprecated/jitter_buffer_common.h deleted file mode 100644 index 48be9589f8e..00000000000 --- a/modules/video_coding/deprecated/jitter_buffer_common.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_COMMON_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_COMMON_H_ - -namespace webrtc { - -// Used to estimate rolling average of packets per frame. -static const float kFastConvergeMultiplier = 0.4f; -static const float kNormalConvergeMultiplier = 0.2f; - -enum { kMaxNumberOfFrames = 300 }; -enum { kStartNumberOfFrames = 6 }; -enum { kMaxVideoDelayMs = 10000 }; -enum { kPacketsPerFrameMultiplier = 5 }; -enum { kFastConvergeThreshold = 5 }; - -enum VCMJitterBufferEnum { - kMaxConsecutiveOldFrames = 60, - kMaxConsecutiveOldPackets = 300, - // TODO(sprang): Reduce this limit once codecs don't sometimes wildly - // overshoot bitrate target. - kMaxPacketsInSession = 1400, // Allows ~2MB frames. - kBufferIncStepSizeBytes = 30000, // >20 packets. - kMaxJBFrameSizeBytes = 4000000 // sanity don't go above 4Mbyte. -}; - -enum VCMFrameBufferEnum { - kOutOfBoundsPacket = -7, - kNotInitialized = -6, - kOldPacket = -5, - kGeneralError = -4, - kFlushIndicator = -3, // Indicator that a flush has occurred. - kTimeStampError = -2, - kSizeError = -1, - kNoError = 0, - kIncomplete = 1, // Frame incomplete. - kCompleteSession = 3, // at least one layer in the frame complete. - kDuplicatePacket = 5 // We're receiving a duplicate packet. -}; - -enum VCMFrameBufferStateEnum { - kStateEmpty, // frame popped by the RTP receiver - kStateIncomplete, // frame that have one or more packet(s) stored - kStateComplete, // frame that have all packets -}; - -enum { kH264StartCodeLengthBytes = 4 }; -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_JITTER_BUFFER_COMMON_H_ diff --git a/modules/video_coding/deprecated/jitter_buffer_unittest.cc b/modules/video_coding/deprecated/jitter_buffer_unittest.cc deleted file mode 100644 index cf1dd549603..00000000000 --- a/modules/video_coding/deprecated/jitter_buffer_unittest.cc +++ /dev/null @@ -1,1846 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/jitter_buffer.h" - -#include -#include -#include -#include -#include - -#include "absl/memory/memory.h" -#include "api/field_trials.h" -#include "api/rtp_headers.h" -#include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" -#include "common_video/h264/h264_common.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "modules/video_coding/codecs/h264/include/h264_globals.h" -#include "modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "modules/video_coding/deprecated/event_wrapper.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/deprecated/stream_generator.h" -#include "modules/video_coding/encoded_frame.h" -#include "system_wrappers/include/clock.h" -#include "test/create_test_field_trials.h" -#include "test/gtest.h" - -namespace webrtc { - -class TestBasicJitterBuffer : public ::testing::Test { - protected: - TestBasicJitterBuffer() {} - void SetUp() override { - clock_.reset(new SimulatedClock(0)); - jitter_buffer_.reset(new VCMJitterBuffer( - clock_.get(), absl::WrapUnique(EventWrapper::Create()), field_trials_)); - jitter_buffer_->Start(); - seq_num_ = 1234; - timestamp_ = 0; - size_ = 1400; - // Data vector - 0, 0, 0x80, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0x80, 3.... - data_[0] = 0; - data_[1] = 0; - data_[2] = 0x80; - int count = 3; - for (unsigned int i = 3; i < sizeof(data_) - 3; ++i) { - data_[i] = count; - count++; - if (count == 10) { - data_[i + 1] = 0; - data_[i + 2] = 0; - data_[i + 3] = 0x80; - count = 3; - i += 3; - } - } - RTPHeader rtp_header; - RTPVideoHeader video_header; - rtp_header.sequenceNumber = seq_num_; - rtp_header.timestamp = timestamp_; - rtp_header.markerBit = true; - video_header.codec = kVideoCodecGeneric; - video_header.is_first_packet_in_frame = true; - video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_.reset(new VCMPacket(data_, size_, rtp_header, video_header, - /*ntp_time_ms=*/0, clock_->CurrentTime())); - } - - VCMEncodedFrame* DecodeCompleteFrame() { - VCMEncodedFrame* found_frame = jitter_buffer_->NextCompleteFrame(10); - if (!found_frame) - return nullptr; - return jitter_buffer_->ExtractAndSetDecode(found_frame->RtpTimestamp()); - } - - void CheckOutFrame(VCMEncodedFrame* frame_out, - unsigned int size, - bool startCode) { - ASSERT_TRUE(frame_out); - - const uint8_t* outData = frame_out->data(); - unsigned int i = 0; - - if (startCode) { - EXPECT_EQ(0, outData[0]); - EXPECT_EQ(0, outData[1]); - EXPECT_EQ(0, outData[2]); - EXPECT_EQ(1, outData[3]); - i += 4; - } - - EXPECT_EQ(size, frame_out->size()); - int count = 3; - for (; i < size; i++) { - if (outData[i] == 0 && outData[i + 1] == 0 && outData[i + 2] == 0x80) { - i += 2; - } else if (startCode && outData[i] == 0 && outData[i + 1] == 0) { - EXPECT_EQ(0, outData[0]); - EXPECT_EQ(0, outData[1]); - EXPECT_EQ(0, outData[2]); - EXPECT_EQ(1, outData[3]); - i += 3; - } else { - EXPECT_EQ(count, outData[i]); - count++; - if (count == 10) { - count = 3; - } - } - } - } - - uint16_t seq_num_; - uint32_t timestamp_; - int size_; - uint8_t data_[1500]; - FieldTrials field_trials_ = CreateTestFieldTrials(); - std::unique_ptr packet_; - std::unique_ptr clock_; - std::unique_ptr jitter_buffer_; -}; - -class TestRunningJitterBuffer : public ::testing::Test { - protected: - enum { kDataBufferSize = 10 }; - - void SetUp() override { - clock_.reset(new SimulatedClock(0)); - max_nack_list_size_ = 150; - oldest_packet_to_nack_ = 250; - jitter_buffer_ = new VCMJitterBuffer( - clock_.get(), absl::WrapUnique(EventWrapper::Create()), field_trials_); - stream_generator_ = new StreamGenerator(0, clock_->TimeInMilliseconds()); - jitter_buffer_->Start(); - jitter_buffer_->SetNackSettings(max_nack_list_size_, oldest_packet_to_nack_, - 0); - memset(data_buffer_, 0, kDataBufferSize); - } - - void TearDown() override { - jitter_buffer_->Stop(); - delete stream_generator_; - delete jitter_buffer_; - } - - VCMFrameBufferEnum InsertPacketAndPop(int index) { - VCMPacket packet; - packet.dataPtr = data_buffer_; - bool packet_available = stream_generator_->PopPacket(&packet, index); - EXPECT_TRUE(packet_available); - if (!packet_available) - return kGeneralError; // Return here to avoid crashes below. - bool retransmitted = false; - return jitter_buffer_->InsertPacket(packet, &retransmitted); - } - - VCMFrameBufferEnum InsertPacket(int index) { - VCMPacket packet; - packet.dataPtr = data_buffer_; - bool packet_available = stream_generator_->GetPacket(&packet, index); - EXPECT_TRUE(packet_available); - if (!packet_available) - return kGeneralError; // Return here to avoid crashes below. - bool retransmitted = false; - return jitter_buffer_->InsertPacket(packet, &retransmitted); - } - - VCMFrameBufferEnum InsertFrame(VideoFrameType frame_type) { - stream_generator_->GenerateFrame( - frame_type, (frame_type != VideoFrameType::kEmptyFrame) ? 1 : 0, - (frame_type == VideoFrameType::kEmptyFrame) ? 1 : 0, - clock_->TimeInMilliseconds()); - VCMFrameBufferEnum ret = InsertPacketAndPop(0); - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - return ret; - } - - VCMFrameBufferEnum InsertFrames(int num_frames, VideoFrameType frame_type) { - VCMFrameBufferEnum ret_for_all = kNoError; - for (int i = 0; i < num_frames; ++i) { - VCMFrameBufferEnum ret = InsertFrame(frame_type); - if (ret < kNoError) { - ret_for_all = ret; - } else if (ret_for_all >= kNoError) { - ret_for_all = ret; - } - } - return ret_for_all; - } - - void DropFrame(int num_packets) { - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, - num_packets, 0, - clock_->TimeInMilliseconds()); - for (int i = 0; i < num_packets; ++i) - stream_generator_->DropLastPacket(); - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - } - - bool DecodeCompleteFrame() { - VCMEncodedFrame* found_frame = jitter_buffer_->NextCompleteFrame(0); - if (!found_frame) - return false; - - VCMEncodedFrame* frame = - jitter_buffer_->ExtractAndSetDecode(found_frame->RtpTimestamp()); - bool ret = (frame != nullptr); - jitter_buffer_->ReleaseFrame(frame); - return ret; - } - - FieldTrials field_trials_ = CreateTestFieldTrials(); - VCMJitterBuffer* jitter_buffer_; - StreamGenerator* stream_generator_; - std::unique_ptr clock_; - size_t max_nack_list_size_; - int oldest_packet_to_nack_; - uint8_t data_buffer_[kDataBufferSize]; -}; - -class TestJitterBufferNack : public TestRunningJitterBuffer { - protected: - TestJitterBufferNack() {} - void SetUp() override { TestRunningJitterBuffer::SetUp(); } - - void TearDown() override { TestRunningJitterBuffer::TearDown(); } -}; - -TEST_F(TestBasicJitterBuffer, StopRunning) { - jitter_buffer_->Stop(); - EXPECT_TRUE(nullptr == DecodeCompleteFrame()); - jitter_buffer_->Start(); - - // No packets inserted. - EXPECT_TRUE(nullptr == DecodeCompleteFrame()); -} - -TEST_F(TestBasicJitterBuffer, SinglePacketFrame) { - // Always start with a complete key frame when not allowing errors. - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->timestamp += 123 * 90; - - // Insert the packet to the jitter buffer and get a frame. - bool retransmitted = false; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, DualPacketFrame) { - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - // Should not be complete. - EXPECT_TRUE(frame_out == nullptr); - - ++seq_num_; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, 2 * size_, false); - - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, 100PacketKeyFrame) { - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - - // Frame should not be complete. - EXPECT_TRUE(frame_out == nullptr); - - // Insert 98 frames. - int loop = 0; - do { - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - loop++; - } while (loop < 98); - - // Insert last packet. - ++seq_num_; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - CheckOutFrame(frame_out, 100 * size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, 100PacketDeltaFrame) { - // Always start with a complete key frame. - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - - bool retransmitted = false; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_FALSE(frame_out == nullptr); - jitter_buffer_->ReleaseFrame(frame_out); - - ++seq_num_; - packet_->seqNum = seq_num_; - packet_->markerBit = false; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->timestamp += 33 * 90; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - // Frame should not be complete. - EXPECT_TRUE(frame_out == nullptr); - - packet_->video_header.is_first_packet_in_frame = false; - // Insert 98 frames. - int loop = 0; - do { - ++seq_num_; - packet_->seqNum = seq_num_; - - // Insert a packet into a frame. - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - loop++; - } while (loop < 98); - - // Insert the last packet. - ++seq_num_; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - CheckOutFrame(frame_out, 100 * size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, PacketReorderingReverseOrder) { - // Insert the "first" packet last. - seq_num_ += 100; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - - EXPECT_TRUE(frame_out == nullptr); - - // Insert 98 packets. - int loop = 0; - do { - seq_num_--; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - loop++; - } while (loop < 98); - - // Insert the last packet. - seq_num_--; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - CheckOutFrame(frame_out, 100 * size_, false); - - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, FrameReordering2Frames2PacketsEach) { - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - - EXPECT_TRUE(frame_out == nullptr); - - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - // check that we fail to get frame since seqnum is not continuous - frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out == nullptr); - - seq_num_ -= 3; - timestamp_ -= 33 * 90; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - // It should not be complete. - EXPECT_TRUE(frame_out == nullptr); - - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, 2 * size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); - - frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, 2 * size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, TestReorderingWithPadding) { - jitter_buffer_->SetNackSettings(kMaxNumberOfFrames, kMaxNumberOfFrames, 0); - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - - // Send in an initial good packet/frame (Frame A) to start things off. - bool retransmitted = false; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out != nullptr); - jitter_buffer_->ReleaseFrame(frame_out); - - // Now send in a complete delta frame (Frame C), but with a sequence number - // gap. No pic index either, so no temporal scalability cheating :) - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - // Leave a gap of 2 sequence numbers and two frames. - packet_->seqNum = seq_num_ + 3; - packet_->timestamp = timestamp_ + (66 * 90); - // Still isFirst = marker = true. - // Session should be complete (frame is complete), but there's nothing to - // decode yet. - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out == nullptr); - - // Now send in a complete delta frame (Frame B) that is continuous from A, but - // doesn't fill the full gap to C. The rest of the gap is going to be padding. - packet_->seqNum = seq_num_ + 1; - packet_->timestamp = timestamp_ + (33 * 90); - // Still isFirst = marker = true. - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out != nullptr); - jitter_buffer_->ReleaseFrame(frame_out); - - // But Frame C isn't continuous yet. - frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out == nullptr); - - // Add in the padding. These are empty packets (data length is 0) with no - // marker bit and matching the timestamp of Frame B. - RTPHeader rtp_header; - RTPVideoHeader video_header; - rtp_header.sequenceNumber = seq_num_ + 2; - rtp_header.timestamp = timestamp_ + (33 * 90); - rtp_header.markerBit = false; - video_header.codec = kVideoCodecGeneric; - video_header.frame_type = VideoFrameType::kEmptyFrame; - VCMPacket empty_packet(data_, 0, rtp_header, video_header, - /*ntp_time_ms=*/0, clock_->CurrentTime()); - EXPECT_EQ(kOldPacket, - jitter_buffer_->InsertPacket(empty_packet, &retransmitted)); - empty_packet.seqNum += 1; - EXPECT_EQ(kOldPacket, - jitter_buffer_->InsertPacket(empty_packet, &retransmitted)); - - // But now Frame C should be ready! - frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out != nullptr); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, DuplicatePackets) { - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - EXPECT_EQ(0, jitter_buffer_->num_packets()); - EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - - EXPECT_TRUE(frame_out == nullptr); - EXPECT_EQ(1, jitter_buffer_->num_packets()); - EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); - - // Insert a packet into a frame. - EXPECT_EQ(kDuplicatePacket, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - EXPECT_EQ(2, jitter_buffer_->num_packets()); - EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets()); - - seq_num_++; - packet_->seqNum = seq_num_; - packet_->markerBit = true; - packet_->video_header.is_first_packet_in_frame = false; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - ASSERT_TRUE(frame_out != nullptr); - CheckOutFrame(frame_out, 2 * size_, false); - - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - EXPECT_EQ(3, jitter_buffer_->num_packets()); - EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, DuplicatePreviousDeltaFramePacket) { - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - EXPECT_EQ(0, jitter_buffer_->num_packets()); - EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); - - bool retransmitted = false; - // Insert first complete frame. - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - ASSERT_TRUE(frame_out != nullptr); - CheckOutFrame(frame_out, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); - - // Insert 3 delta frames. - for (uint16_t i = 1; i <= 3; ++i) { - packet_->seqNum = seq_num_ + i; - packet_->timestamp = timestamp_ + (i * 33) * 90; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - EXPECT_EQ(i + 1, jitter_buffer_->num_packets()); - EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets()); - } - - // Retransmit second delta frame. - packet_->seqNum = seq_num_ + 2; - packet_->timestamp = timestamp_ + 66 * 90; - - EXPECT_EQ(kDuplicatePacket, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - EXPECT_EQ(5, jitter_buffer_->num_packets()); - EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets()); - - // Should be able to decode 3 delta frames, key frame already decoded. - for (size_t i = 0; i < 3; ++i) { - frame_out = DecodeCompleteFrame(); - ASSERT_TRUE(frame_out != nullptr); - CheckOutFrame(frame_out, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); - } -} - -TEST_F(TestBasicJitterBuffer, TestSkipForwardVp9) { - // Verify that JB skips forward to next base layer frame. - // ------------------------------------------------- - // | 65485 | 65486 | 65487 | 65488 | 65489 | ... - // | pid:5 | pid:6 | pid:7 | pid:8 | pid:9 | ... - // | tid:0 | tid:2 | tid:1 | tid:2 | tid:0 | ... - // | ss | x | x | x | | - // ------------------------------------------------- - // |<----------tl0idx:200--------->|<---tl0idx:201--- - - jitter_buffer_->SetNackSettings(kMaxNumberOfFrames, kMaxNumberOfFrames, 0); - auto& vp9_header = - packet_->video_header.video_type_header.emplace(); - - bool re = false; - packet_->video_header.codec = kVideoCodecVP9; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - vp9_header.flexible_mode = false; - vp9_header.spatial_idx = 0; - vp9_header.beginning_of_frame = true; - vp9_header.end_of_frame = true; - vp9_header.temporal_up_switch = false; - - packet_->seqNum = 65485; - packet_->timestamp = 1000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - vp9_header.picture_id = 5; - vp9_header.tl0_pic_idx = 200; - vp9_header.temporal_idx = 0; - vp9_header.ss_data_available = true; - vp9_header.gof.SetGofInfoVP9( - kTemporalStructureMode3); // kTemporalStructureMode3: 0-2-1-2.. - EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, &re)); - - // Insert next temporal layer 0. - packet_->seqNum = 65489; - packet_->timestamp = 13000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_header.picture_id = 9; - vp9_header.tl0_pic_idx = 201; - vp9_header.temporal_idx = 0; - vp9_header.ss_data_available = false; - EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, &re)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(1000U, frame_out->RtpTimestamp()); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); - - frame_out = DecodeCompleteFrame(); - EXPECT_EQ(13000U, frame_out->RtpTimestamp()); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, ReorderedVp9SsData_3TlLayers) { - // Verify that frames are updated with SS data when SS packet is reordered. - // -------------------------------- - // | 65486 | 65487 | 65485 |... - // | pid:6 | pid:7 | pid:5 |... - // | tid:2 | tid:1 | tid:0 |... - // | | | ss | - // -------------------------------- - // |<--------tl0idx:200--------->| - - auto& vp9_header = - packet_->video_header.video_type_header.emplace(); - - bool re = false; - packet_->video_header.codec = kVideoCodecVP9; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - vp9_header.flexible_mode = false; - vp9_header.spatial_idx = 0; - vp9_header.beginning_of_frame = true; - vp9_header.end_of_frame = true; - vp9_header.tl0_pic_idx = 200; - - packet_->seqNum = 65486; - packet_->timestamp = 6000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_header.picture_id = 6; - vp9_header.temporal_idx = 2; - vp9_header.temporal_up_switch = true; - EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, &re)); - - packet_->seqNum = 65487; - packet_->timestamp = 9000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_header.picture_id = 7; - vp9_header.temporal_idx = 1; - vp9_header.temporal_up_switch = true; - EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, &re)); - - // Insert first frame with SS data. - packet_->seqNum = 65485; - packet_->timestamp = 3000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.width = 352; - packet_->video_header.height = 288; - vp9_header.picture_id = 5; - vp9_header.temporal_idx = 0; - vp9_header.temporal_up_switch = false; - vp9_header.ss_data_available = true; - vp9_header.gof.SetGofInfoVP9( - kTemporalStructureMode3); // kTemporalStructureMode3: 0-2-1-2.. - EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, &re)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(3000U, frame_out->RtpTimestamp()); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - EXPECT_EQ(0, frame_out->CodecSpecific()->codecSpecific.VP9.temporal_idx); - EXPECT_FALSE( - frame_out->CodecSpecific()->codecSpecific.VP9.temporal_up_switch); - jitter_buffer_->ReleaseFrame(frame_out); - - frame_out = DecodeCompleteFrame(); - EXPECT_EQ(6000U, frame_out->RtpTimestamp()); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - EXPECT_EQ(2, frame_out->CodecSpecific()->codecSpecific.VP9.temporal_idx); - EXPECT_TRUE(frame_out->CodecSpecific()->codecSpecific.VP9.temporal_up_switch); - jitter_buffer_->ReleaseFrame(frame_out); - - frame_out = DecodeCompleteFrame(); - EXPECT_EQ(9000U, frame_out->RtpTimestamp()); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - EXPECT_EQ(1, frame_out->CodecSpecific()->codecSpecific.VP9.temporal_idx); - EXPECT_TRUE(frame_out->CodecSpecific()->codecSpecific.VP9.temporal_up_switch); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, ReorderedVp9SsData_2Tl2SLayers) { - // Verify that frames are updated with SS data when SS packet is reordered. - // ----------------------------------------- - // | 65486 | 65487 | 65485 | 65484 |... - // | pid:6 | pid:6 | pid:5 | pid:5 |... - // | tid:1 | tid:1 | tid:0 | tid:0 |... - // | sid:0 | sid:1 | sid:1 | sid:0 |... - // | t:6000 | t:6000 | t:3000 | t:3000 | - // | | | | ss | - // ----------------------------------------- - // |<-----------tl0idx:200------------>| - - auto& vp9_header = - packet_->video_header.video_type_header.emplace(); - - bool re = false; - packet_->video_header.codec = kVideoCodecVP9; - vp9_header.flexible_mode = false; - vp9_header.beginning_of_frame = true; - vp9_header.end_of_frame = true; - vp9_header.tl0_pic_idx = 200; - - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = 65486; - packet_->timestamp = 6000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_header.spatial_idx = 0; - vp9_header.picture_id = 6; - vp9_header.temporal_idx = 1; - vp9_header.temporal_up_switch = true; - EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(*packet_, &re)); - - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = 65487; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - vp9_header.spatial_idx = 1; - vp9_header.picture_id = 6; - vp9_header.temporal_idx = 1; - vp9_header.temporal_up_switch = true; - EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, &re)); - - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = 65485; - packet_->timestamp = 3000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - vp9_header.spatial_idx = 1; - vp9_header.picture_id = 5; - vp9_header.temporal_idx = 0; - vp9_header.temporal_up_switch = false; - EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(*packet_, &re)); - - // Insert first frame with SS data. - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = 65484; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.width = 352; - packet_->video_header.height = 288; - vp9_header.spatial_idx = 0; - vp9_header.picture_id = 5; - vp9_header.temporal_idx = 0; - vp9_header.temporal_up_switch = false; - vp9_header.ss_data_available = true; - vp9_header.gof.SetGofInfoVP9( - kTemporalStructureMode2); // kTemporalStructureMode3: 0-1-0-1.. - EXPECT_EQ(kCompleteSession, jitter_buffer_->InsertPacket(*packet_, &re)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(3000U, frame_out->RtpTimestamp()); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - EXPECT_EQ(0, frame_out->CodecSpecific()->codecSpecific.VP9.temporal_idx); - EXPECT_FALSE( - frame_out->CodecSpecific()->codecSpecific.VP9.temporal_up_switch); - jitter_buffer_->ReleaseFrame(frame_out); - - frame_out = DecodeCompleteFrame(); - EXPECT_EQ(6000U, frame_out->RtpTimestamp()); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - EXPECT_EQ(1, frame_out->CodecSpecific()->codecSpecific.VP9.temporal_idx); - EXPECT_TRUE(frame_out->CodecSpecific()->codecSpecific.VP9.temporal_up_switch); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, H264InsertStartCode) { - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - packet_->insertStartCode = true; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - - // Frame should not be complete. - EXPECT_TRUE(frame_out == nullptr); - - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, size_ * 2 + 4 * 2, true); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, SpsAndPpsHandling) { - auto& h264_header = - packet_->video_header.video_type_header.emplace(); - packet_->timestamp = timestamp_; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->video_header.codec = kVideoCodecH264; - h264_header.nalu_type = H264::NaluType::kIdr; - h264_header.nalus = { - {.type = H264::NaluType::kIdr, .sps_id = -1, .pps_id = 0}}; - bool retransmitted = false; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - // Not decodable since sps and pps are missing. - EXPECT_EQ(nullptr, DecodeCompleteFrame()); - - timestamp_ += 3000; - packet_->timestamp = timestamp_; - ++seq_num_; - packet_->seqNum = seq_num_; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->video_header.codec = kVideoCodecH264; - h264_header.nalu_type = H264::NaluType::kStapA; - h264_header.nalus = { - {.type = H264::NaluType::kSps, .sps_id = 0, .pps_id = -1}, - {.type = H264::NaluType::kPps, .sps_id = 0, .pps_id = 0}}; - // Not complete since the marker bit hasn't been received. - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - ++seq_num_; - packet_->seqNum = seq_num_; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->video_header.codec = kVideoCodecH264; - h264_header.nalu_type = H264::NaluType::kIdr; - h264_header.nalus = { - {.type = H264::NaluType::kIdr, .sps_id = -1, .pps_id = 0}}; - // Complete and decodable since the pps and sps are received in the first - // packet of this frame. - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - ASSERT_NE(nullptr, frame_out); - jitter_buffer_->ReleaseFrame(frame_out); - - timestamp_ += 3000; - packet_->timestamp = timestamp_; - ++seq_num_; - packet_->seqNum = seq_num_; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->video_header.codec = kVideoCodecH264; - h264_header.nalu_type = H264::NaluType::kSlice; - h264_header.nalus = { - {.type = H264::NaluType::kIdr, .sps_id = -1, .pps_id = 0}}; - - // Complete and decodable since sps, pps and key frame has been received. - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - frame_out = DecodeCompleteFrame(); - ASSERT_NE(nullptr, frame_out); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, DeltaFrame100PacketsWithSeqNumWrap) { - seq_num_ = 0xfff0; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - - EXPECT_TRUE(frame_out == nullptr); - - int loop = 0; - do { - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - EXPECT_TRUE(frame_out == nullptr); - - loop++; - } while (loop < 98); - - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - CheckOutFrame(frame_out, 100 * size_, false); - - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, PacketReorderingReverseWithNegSeqNumWrap) { - // Insert "first" packet last seqnum. - seq_num_ = 10; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - - // Should not be complete. - EXPECT_TRUE(frame_out == nullptr); - - // Insert 98 frames. - int loop = 0; - do { - seq_num_--; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - - EXPECT_TRUE(frame_out == nullptr); - - loop++; - } while (loop < 98); - - // Insert last packet. - seq_num_--; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, 100 * size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, TestInsertOldFrame) { - // ------- ------- - // | 2 | | 1 | - // ------- ------- - // t = 3000 t = 2000 - seq_num_ = 2; - timestamp_ = 3000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->timestamp = timestamp_; - packet_->seqNum = seq_num_; - - bool retransmitted = false; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(3000u, frame_out->RtpTimestamp()); - CheckOutFrame(frame_out, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); - - seq_num_--; - timestamp_ = 2000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - EXPECT_EQ(kOldPacket, jitter_buffer_->InsertPacket(*packet_, &retransmitted)); -} - -TEST_F(TestBasicJitterBuffer, TestInsertOldFrameWithSeqNumWrap) { - // ------- ------- - // | 2 | | 1 | - // ------- ------- - // t = 3000 t = 0xffffff00 - - seq_num_ = 2; - timestamp_ = 3000; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - bool retransmitted = false; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(timestamp_, frame_out->RtpTimestamp()); - - CheckOutFrame(frame_out, size_, false); - - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - - jitter_buffer_->ReleaseFrame(frame_out); - - seq_num_--; - timestamp_ = 0xffffff00; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - // This timestamp is old. - EXPECT_EQ(kOldPacket, jitter_buffer_->InsertPacket(*packet_, &retransmitted)); -} - -TEST_F(TestBasicJitterBuffer, TimestampWrap) { - // --------------- --------------- - // | 1 | 2 | | 3 | 4 | - // --------------- --------------- - // t = 0xffffff00 t = 33*90 - - timestamp_ = 0xffffff00; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - bool retransmitted = false; - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out == nullptr); - - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, 2 * size_, false); - jitter_buffer_->ReleaseFrame(frame_out); - - seq_num_++; - timestamp_ += 33 * 90; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out == nullptr); - - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - frame_out = DecodeCompleteFrame(); - CheckOutFrame(frame_out, 2 * size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, 2FrameWithTimestampWrap) { - // ------- ------- - // | 1 | | 2 | - // ------- ------- - // t = 0xffffff00 t = 2700 - - timestamp_ = 0xffffff00; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->timestamp = timestamp_; - - bool retransmitted = false; - // Insert first frame (session will be complete). - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - // Insert next frame. - seq_num_++; - timestamp_ = 2700; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(0xffffff00, frame_out->RtpTimestamp()); - CheckOutFrame(frame_out, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); - - VCMEncodedFrame* frame_out2 = DecodeCompleteFrame(); - EXPECT_EQ(2700u, frame_out2->RtpTimestamp()); - CheckOutFrame(frame_out2, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out2->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out2); -} - -TEST_F(TestBasicJitterBuffer, Insert2FramesReOrderedWithTimestampWrap) { - // ------- ------- - // | 2 | | 1 | - // ------- ------- - // t = 2700 t = 0xffffff00 - - seq_num_ = 2; - timestamp_ = 2700; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - bool retransmitted = false; - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - // Insert second frame - seq_num_--; - timestamp_ = 0xffffff00; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(0xffffff00, frame_out->RtpTimestamp()); - CheckOutFrame(frame_out, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); - - VCMEncodedFrame* frame_out2 = DecodeCompleteFrame(); - EXPECT_EQ(2700u, frame_out2->RtpTimestamp()); - CheckOutFrame(frame_out2, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameDelta, frame_out2->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out2); -} - -TEST_F(TestBasicJitterBuffer, DeltaFrameWithMoreThanMaxNumberOfPackets) { - int loop = 0; - bool firstPacket = true; - bool retransmitted = false; - // Insert kMaxPacketsInJitterBuffer into frame. - do { - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - - if (firstPacket) { - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - firstPacket = false; - } else { - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - } - - loop++; - } while (loop < kMaxPacketsInSession); - - // Max number of packets inserted. - // Insert one more packet. - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - - // Insert the packet -> frame recycled. - EXPECT_EQ(kSizeError, jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - EXPECT_TRUE(nullptr == DecodeCompleteFrame()); -} - -TEST_F(TestBasicJitterBuffer, ExceedNumOfFrameWithSeqNumWrap) { - // TEST fill JB with more than max number of frame (50 delta frames + - // 51 key frames) with wrap in seq_num_ - // - // -------------------------------------------------------------- - // | 65485 | 65486 | 65487 | .... | 65535 | 0 | 1 | 2 | .....| 50 | - // -------------------------------------------------------------- - // |<-----------delta frames------------->|<------key frames----->| - - // Make sure the jitter doesn't request a keyframe after too much non- - // decodable frames. - jitter_buffer_->SetNackSettings(kMaxNumberOfFrames, kMaxNumberOfFrames, 0); - - int loop = 0; - seq_num_ = 65485; - uint32_t first_key_frame_timestamp = 0; - bool retransmitted = false; - // Insert MAX_NUMBER_OF_FRAMES frames. - do { - timestamp_ += 33 * 90; - seq_num_++; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - if (loop == 50) { - first_key_frame_timestamp = packet_->timestamp; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - } - - // Insert frame. - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - loop++; - } while (loop < kMaxNumberOfFrames); - - // Max number of frames inserted. - - // Insert one more frame. - timestamp_ += 33 * 90; - seq_num_++; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - - // Now, no free frame - frames will be recycled until first key frame. - EXPECT_EQ(kFlushIndicator, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_EQ(first_key_frame_timestamp, frame_out->RtpTimestamp()); - CheckOutFrame(frame_out, size_, false); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, frame_out->FrameType()); - jitter_buffer_->ReleaseFrame(frame_out); -} - -TEST_F(TestBasicJitterBuffer, EmptyLastFrame) { - seq_num_ = 3; - // Insert one empty packet per frame, should never return the last timestamp - // inserted. Only return empty frames in the presence of subsequent frames. - int maxSize = 1000; - bool retransmitted = false; - for (int i = 0; i < maxSize + 10; i++) { - timestamp_ += 33 * 90; - seq_num_++; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = false; - packet_->seqNum = seq_num_; - packet_->timestamp = timestamp_; - packet_->video_header.frame_type = VideoFrameType::kEmptyFrame; - - EXPECT_EQ(kNoError, jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - } -} - -TEST_F(TestBasicJitterBuffer, NextFrameWhenIncomplete) { - // Test that a we cannot get incomplete frames from the JB if we haven't - // received the marker bit, unless we have received a packet from a later - // timestamp. - // Start with a complete key frame - insert and decode. - jitter_buffer_->SetNackSettings(kMaxNumberOfFrames, kMaxNumberOfFrames, 0); - packet_->video_header.frame_type = VideoFrameType::kVideoFrameKey; - packet_->video_header.is_first_packet_in_frame = true; - packet_->markerBit = true; - bool retransmitted = false; - - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - VCMEncodedFrame* frame_out = DecodeCompleteFrame(); - EXPECT_TRUE(frame_out != nullptr); - jitter_buffer_->ReleaseFrame(frame_out); - - packet_->seqNum += 2; - packet_->timestamp += 33 * 90; - packet_->video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_->video_header.is_first_packet_in_frame = false; - packet_->markerBit = false; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); - - packet_->seqNum += 2; - packet_->timestamp += 33 * 90; - packet_->video_header.is_first_packet_in_frame = true; - - EXPECT_EQ(kIncomplete, - jitter_buffer_->InsertPacket(*packet_, &retransmitted)); -} - -TEST_F(TestRunningJitterBuffer, Full) { - // Make sure the jitter doesn't request a keyframe after too much non- - // decodable frames. - jitter_buffer_->SetNackSettings(kMaxNumberOfFrames, kMaxNumberOfFrames, 0); - // Insert a key frame and decode it. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey), kNoError); - EXPECT_TRUE(DecodeCompleteFrame()); - DropFrame(1); - // Fill the jitter buffer. - EXPECT_GE(InsertFrames(kMaxNumberOfFrames, VideoFrameType::kVideoFrameDelta), - kNoError); - // Make sure we can't decode these frames. - EXPECT_FALSE(DecodeCompleteFrame()); - // This frame will make the jitter buffer recycle frames until a key frame. - // Since none is found it will have to wait until the next key frame before - // decoding. - EXPECT_EQ(kFlushIndicator, InsertFrame(VideoFrameType::kVideoFrameDelta)); - EXPECT_FALSE(DecodeCompleteFrame()); -} - -TEST_F(TestRunningJitterBuffer, EmptyPackets) { - // Make sure a frame can get complete even though empty packets are missing. - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey, 3, 3, - clock_->TimeInMilliseconds()); - bool request_key_frame = false; - // Insert empty packet. - EXPECT_EQ(kNoError, InsertPacketAndPop(4)); - EXPECT_FALSE(request_key_frame); - // Insert 3 media packets. - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); - EXPECT_EQ(kCompleteSession, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); - // Insert empty packet. - EXPECT_EQ(kCompleteSession, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); -} - -TEST_F(TestRunningJitterBuffer, SkipToKeyFrame) { - // Insert delta frames. - EXPECT_GE(InsertFrames(5, VideoFrameType::kVideoFrameDelta), kNoError); - // Can't decode without a key frame. - EXPECT_FALSE(DecodeCompleteFrame()); - InsertFrame(VideoFrameType::kVideoFrameKey); - // Skip to the next key frame. - EXPECT_TRUE(DecodeCompleteFrame()); -} - -TEST_F(TestRunningJitterBuffer, DontSkipToKeyFrameIfDecodable) { - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_TRUE(DecodeCompleteFrame()); - const int kNumDeltaFrames = 5; - EXPECT_GE(InsertFrames(kNumDeltaFrames, VideoFrameType::kVideoFrameDelta), - kNoError); - InsertFrame(VideoFrameType::kVideoFrameKey); - for (int i = 0; i < kNumDeltaFrames + 1; ++i) { - EXPECT_TRUE(DecodeCompleteFrame()); - } -} - -TEST_F(TestRunningJitterBuffer, KeyDeltaKeyDelta) { - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_TRUE(DecodeCompleteFrame()); - const int kNumDeltaFrames = 5; - EXPECT_GE(InsertFrames(kNumDeltaFrames, VideoFrameType::kVideoFrameDelta), - kNoError); - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_GE(InsertFrames(kNumDeltaFrames, VideoFrameType::kVideoFrameDelta), - kNoError); - InsertFrame(VideoFrameType::kVideoFrameKey); - for (int i = 0; i < 2 * (kNumDeltaFrames + 1); ++i) { - EXPECT_TRUE(DecodeCompleteFrame()); - } -} - -TEST_F(TestRunningJitterBuffer, TwoPacketsNonContinuous) { - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_TRUE(DecodeCompleteFrame()); - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, 1, 0, - clock_->TimeInMilliseconds()); - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, 2, 0, - clock_->TimeInMilliseconds()); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(1)); - EXPECT_EQ(kCompleteSession, InsertPacketAndPop(1)); - EXPECT_FALSE(DecodeCompleteFrame()); - EXPECT_EQ(kCompleteSession, InsertPacketAndPop(0)); - EXPECT_TRUE(DecodeCompleteFrame()); - EXPECT_TRUE(DecodeCompleteFrame()); -} - -TEST_F(TestJitterBufferNack, EmptyPackets) { - // Make sure empty packets doesn't clog the jitter buffer. - EXPECT_GE(InsertFrames(kMaxNumberOfFrames, VideoFrameType::kEmptyFrame), - kNoError); - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_TRUE(DecodeCompleteFrame()); -} - -TEST_F(TestJitterBufferNack, NackTooOldPackets) { - // Insert a key frame and decode it. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey), kNoError); - EXPECT_TRUE(DecodeCompleteFrame()); - - // Drop one frame and insert `kNackHistoryLength` to trigger NACKing a too - // old packet. - DropFrame(1); - // Insert a frame which should trigger a recycle until the next key frame. - EXPECT_EQ(kFlushIndicator, InsertFrames(oldest_packet_to_nack_ + 1, - VideoFrameType::kVideoFrameDelta)); - EXPECT_FALSE(DecodeCompleteFrame()); - - bool request_key_frame = false; - std::vector nack_list = - jitter_buffer_->GetNackList(&request_key_frame); - // No key frame will be requested since the jitter buffer is empty. - EXPECT_FALSE(request_key_frame); - EXPECT_EQ(0u, nack_list.size()); - - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta), kNoError); - // Waiting for a key frame. - EXPECT_FALSE(DecodeCompleteFrame()); - - // The next complete continuous frame isn't a key frame, but we're waiting - // for one. - EXPECT_FALSE(DecodeCompleteFrame()); - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey), kNoError); - // Skipping ahead to the key frame. - EXPECT_TRUE(DecodeCompleteFrame()); -} - -TEST_F(TestJitterBufferNack, NackLargeJitterBuffer) { - // Insert a key frame and decode it. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey), kNoError); - EXPECT_TRUE(DecodeCompleteFrame()); - - // Insert a frame which should trigger a recycle until the next key frame. - EXPECT_GE( - InsertFrames(oldest_packet_to_nack_, VideoFrameType::kVideoFrameDelta), - kNoError); - - bool request_key_frame = false; - std::vector nack_list = - jitter_buffer_->GetNackList(&request_key_frame); - // Verify that the jitter buffer does not request a key frame. - EXPECT_FALSE(request_key_frame); - // Verify that no packets are NACKed. - EXPECT_EQ(0u, nack_list.size()); - // Verify that we can decode the next frame. - EXPECT_TRUE(DecodeCompleteFrame()); -} - -TEST_F(TestJitterBufferNack, NackListFull) { - // Insert a key frame and decode it. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey), kNoError); - EXPECT_TRUE(DecodeCompleteFrame()); - - // Generate and drop `kNackHistoryLength` packets to fill the NACK list. - DropFrame(max_nack_list_size_ + 1); - // Insert a frame which should trigger a recycle until the next key frame. - EXPECT_EQ(kFlushIndicator, InsertFrame(VideoFrameType::kVideoFrameDelta)); - EXPECT_FALSE(DecodeCompleteFrame()); - - bool request_key_frame = false; - jitter_buffer_->GetNackList(&request_key_frame); - // The jitter buffer is empty, so we won't request key frames until we get a - // packet. - EXPECT_FALSE(request_key_frame); - - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta), kNoError); - // Now we have a packet in the jitter buffer, a key frame will be requested - // since it's not a key frame. - jitter_buffer_->GetNackList(&request_key_frame); - // The jitter buffer is empty, so we won't request key frames until we get a - // packet. - EXPECT_TRUE(request_key_frame); - // The next complete continuous frame isn't a key frame, but we're waiting - // for one. - EXPECT_FALSE(DecodeCompleteFrame()); - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey), kNoError); - // Skipping ahead to the key frame. - EXPECT_TRUE(DecodeCompleteFrame()); -} - -TEST_F(TestJitterBufferNack, NoNackListReturnedBeforeFirstDecode) { - DropFrame(10); - // Insert a frame and try to generate a NACK list. Shouldn't get one. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta), kNoError); - bool request_key_frame = false; - std::vector nack_list = - jitter_buffer_->GetNackList(&request_key_frame); - // No list generated, and a key frame request is signaled. - EXPECT_EQ(0u, nack_list.size()); - EXPECT_TRUE(request_key_frame); -} - -TEST_F(TestJitterBufferNack, NackListBuiltBeforeFirstDecode) { - stream_generator_->Init(0, clock_->TimeInMilliseconds()); - InsertFrame(VideoFrameType::kVideoFrameKey); - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, 2, 0, - clock_->TimeInMilliseconds()); - stream_generator_->NextPacket(nullptr); // Drop packet. - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - EXPECT_TRUE(DecodeCompleteFrame()); - bool extended = false; - std::vector nack_list = jitter_buffer_->GetNackList(&extended); - EXPECT_EQ(1u, nack_list.size()); -} - -TEST_F(TestJitterBufferNack, VerifyRetransmittedFlag) { - stream_generator_->Init(0, clock_->TimeInMilliseconds()); - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey, 3, 0, - clock_->TimeInMilliseconds()); - VCMPacket packet; - stream_generator_->PopPacket(&packet, 0); - bool retransmitted = false; - EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(packet, &retransmitted)); - EXPECT_FALSE(retransmitted); - // Drop second packet. - stream_generator_->PopPacket(&packet, 1); - EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(packet, &retransmitted)); - EXPECT_FALSE(retransmitted); - EXPECT_FALSE(DecodeCompleteFrame()); - bool extended = false; - std::vector nack_list = jitter_buffer_->GetNackList(&extended); - uint16_t seq_num; - EXPECT_EQ(1u, nack_list.size()); - seq_num = nack_list[0]; - stream_generator_->PopPacket(&packet, 0); - EXPECT_EQ(packet.seqNum, seq_num); - EXPECT_EQ(kCompleteSession, - jitter_buffer_->InsertPacket(packet, &retransmitted)); - EXPECT_TRUE(retransmitted); - EXPECT_TRUE(DecodeCompleteFrame()); -} - -TEST_F(TestJitterBufferNack, UseNackToRecoverFirstKeyFrame) { - stream_generator_->Init(0, clock_->TimeInMilliseconds()); - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey, 3, 0, - clock_->TimeInMilliseconds()); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - // Drop second packet. - EXPECT_EQ(kIncomplete, InsertPacketAndPop(1)); - EXPECT_FALSE(DecodeCompleteFrame()); - bool extended = false; - std::vector nack_list = jitter_buffer_->GetNackList(&extended); - uint16_t seq_num; - ASSERT_EQ(1u, nack_list.size()); - seq_num = nack_list[0]; - VCMPacket packet; - stream_generator_->GetPacket(&packet, 0); - EXPECT_EQ(packet.seqNum, seq_num); -} - -TEST_F(TestJitterBufferNack, UseNackToRecoverFirstKeyFrameSecondInQueue) { - VCMPacket packet; - stream_generator_->Init(0, clock_->TimeInMilliseconds()); - // First frame is delta. - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, 3, 0, - clock_->TimeInMilliseconds()); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - // Drop second packet in frame. - ASSERT_TRUE(stream_generator_->PopPacket(&packet, 0)); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - // Second frame is key. - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey, 3, 0, - clock_->TimeInMilliseconds() + 10); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - // Drop second packet in frame. - EXPECT_EQ(kIncomplete, InsertPacketAndPop(1)); - EXPECT_FALSE(DecodeCompleteFrame()); - bool extended = false; - std::vector nack_list = jitter_buffer_->GetNackList(&extended); - uint16_t seq_num; - ASSERT_EQ(1u, nack_list.size()); - seq_num = nack_list[0]; - stream_generator_->GetPacket(&packet, 0); - EXPECT_EQ(packet.seqNum, seq_num); -} - -TEST_F(TestJitterBufferNack, NormalOperation) { - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey), kNoError); - EXPECT_TRUE(DecodeCompleteFrame()); - - // ---------------------------------------------------------------- - // | 1 | 2 | .. | 8 | 9 | x | 11 | 12 | .. | 19 | x | 21 | .. | 100 | - // ---------------------------------------------------------------- - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey, 100, 0, - clock_->TimeInMilliseconds()); - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - // Verify that the frame is incomplete. - EXPECT_FALSE(DecodeCompleteFrame()); - while (stream_generator_->PacketsRemaining() > 1) { - if (stream_generator_->NextSequenceNumber() % 10 != 0) { - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - } else { - stream_generator_->NextPacket(nullptr); // Drop packet - } - } - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - EXPECT_EQ(0, stream_generator_->PacketsRemaining()); - EXPECT_FALSE(DecodeCompleteFrame()); - bool request_key_frame = false; - - // Verify the NACK list. - std::vector nack_list = - jitter_buffer_->GetNackList(&request_key_frame); - const size_t kExpectedNackSize = 9; - ASSERT_EQ(kExpectedNackSize, nack_list.size()); - for (size_t i = 0; i < nack_list.size(); ++i) - EXPECT_EQ((1 + i) * 10, nack_list[i]); -} - -TEST_F(TestJitterBufferNack, NormalOperationWrap) { - bool request_key_frame = false; - // ------- ------------------------------------------------------------ - // | 65532 | | 65533 | 65534 | 65535 | x | 1 | .. | 9 | x | 11 |.....| 96 | - // ------- ------------------------------------------------------------ - stream_generator_->Init(65532, clock_->TimeInMilliseconds()); - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_FALSE(request_key_frame); - EXPECT_TRUE(DecodeCompleteFrame()); - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, 100, 0, - clock_->TimeInMilliseconds()); - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - while (stream_generator_->PacketsRemaining() > 1) { - if (stream_generator_->NextSequenceNumber() % 10 != 0) { - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); - } else { - stream_generator_->NextPacket(nullptr); // Drop packet - } - } - EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); - EXPECT_EQ(0, stream_generator_->PacketsRemaining()); - EXPECT_FALSE(DecodeCompleteFrame()); - EXPECT_FALSE(DecodeCompleteFrame()); - bool extended = false; - std::vector nack_list = jitter_buffer_->GetNackList(&extended); - // Verify the NACK list. - const size_t kExpectedNackSize = 10; - ASSERT_EQ(kExpectedNackSize, nack_list.size()); - for (size_t i = 0; i < nack_list.size(); ++i) - EXPECT_EQ(i * 10, nack_list[i]); -} - -TEST_F(TestJitterBufferNack, NormalOperationWrap2) { - bool request_key_frame = false; - // ----------------------------------- - // | 65532 | 65533 | 65534 | x | 0 | 1 | - // ----------------------------------- - stream_generator_->Init(65532, clock_->TimeInMilliseconds()); - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_FALSE(request_key_frame); - EXPECT_TRUE(DecodeCompleteFrame()); - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, 1, 0, - clock_->TimeInMilliseconds()); - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - for (int i = 0; i < 5; ++i) { - if (stream_generator_->NextSequenceNumber() != 65535) { - EXPECT_EQ(kCompleteSession, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); - } else { - stream_generator_->NextPacket(nullptr); // Drop packet - } - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameDelta, 1, 0, - clock_->TimeInMilliseconds()); - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - } - EXPECT_EQ(kCompleteSession, InsertPacketAndPop(0)); - EXPECT_FALSE(request_key_frame); - bool extended = false; - std::vector nack_list = jitter_buffer_->GetNackList(&extended); - // Verify the NACK list. - ASSERT_EQ(1u, nack_list.size()); - EXPECT_EQ(65535, nack_list[0]); -} - -TEST_F(TestJitterBufferNack, ResetByFutureKeyFrameDoesntError) { - stream_generator_->Init(0, clock_->TimeInMilliseconds()); - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_TRUE(DecodeCompleteFrame()); - bool extended = false; - std::vector nack_list = jitter_buffer_->GetNackList(&extended); - EXPECT_EQ(0u, nack_list.size()); - - // Far-into-the-future video frame, could be caused by resetting the encoder - // or otherwise restarting. This should not fail when error when the packet is - // a keyframe, even if all of the nack list needs to be flushed. - stream_generator_->Init(10000, clock_->TimeInMilliseconds()); - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - InsertFrame(VideoFrameType::kVideoFrameKey); - EXPECT_TRUE(DecodeCompleteFrame()); - nack_list = jitter_buffer_->GetNackList(&extended); - EXPECT_EQ(0u, nack_list.size()); - - // Stream should be decodable from this point. - clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - InsertFrame(VideoFrameType::kVideoFrameDelta); - EXPECT_TRUE(DecodeCompleteFrame()); - nack_list = jitter_buffer_->GetNackList(&extended); - EXPECT_EQ(0u, nack_list.size()); -} -} // namespace webrtc diff --git a/modules/video_coding/deprecated/packet.cc b/modules/video_coding/deprecated/packet.cc deleted file mode 100644 index ca9bfa4eec6..00000000000 --- a/modules/video_coding/deprecated/packet.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/packet.h" - -#include -#include -#include - -#include "api/rtp_headers.h" -#include "api/units/timestamp.h" -#include "api/video/video_codec_type.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" - -namespace webrtc { - -VCMPacket::VCMPacket() - : payloadType(0), - timestamp(0), - ntp_time_ms_(0), - seqNum(0), - dataPtr(nullptr), - sizeBytes(0), - markerBit(false), - timesNacked(-1), - completeNALU(kNaluUnset), - insertStartCode(false), - video_header() {} - -VCMPacket::VCMPacket(const uint8_t* ptr, - size_t size, - const RTPHeader& rtp_header, - const RTPVideoHeader& videoHeader, - int64_t ntp_time_ms, - Timestamp receive_time) - : payloadType(rtp_header.payloadType), - timestamp(rtp_header.timestamp), - ntp_time_ms_(ntp_time_ms), - seqNum(rtp_header.sequenceNumber), - dataPtr(ptr), - sizeBytes(size), - markerBit(rtp_header.markerBit), - timesNacked(-1), - completeNALU(kNaluIncomplete), - insertStartCode(videoHeader.codec == kVideoCodecH264 && - videoHeader.is_first_packet_in_frame), - video_header(videoHeader), - packet_info(rtp_header, receive_time) { - if (is_first_packet_in_frame() && markerBit) { - completeNALU = kNaluComplete; - } else if (is_first_packet_in_frame()) { - completeNALU = kNaluStart; - } else if (markerBit) { - completeNALU = kNaluEnd; - } else { - completeNALU = kNaluIncomplete; - } - - // Playout decisions are made entirely based on first packet in a frame. - if (!is_first_packet_in_frame()) { - video_header.playout_delay = std::nullopt; - } -} - -VCMPacket::~VCMPacket() = default; - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/packet.h b/modules/video_coding/deprecated/packet.h deleted file mode 100644 index 7bb1ee6fa31..00000000000 --- a/modules/video_coding/deprecated/packet.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_PACKET_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_PACKET_H_ - -#include -#include - -#include - -#include "api/rtp_headers.h" -#include "api/rtp_packet_info.h" -#include "api/units/timestamp.h" -#include "api/video/video_codec_type.h" -#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" - -namespace webrtc { - -// Used to indicate if a received packet contain a complete NALU (or equivalent) -enum VCMNaluCompleteness { - kNaluUnset = 0, // Packet has not been filled. - kNaluComplete = 1, // Packet can be decoded as is. - kNaluStart, // Packet contain beginning of NALU - kNaluIncomplete, // Packet is not beginning or end of NALU - kNaluEnd, // Packet is the end of a NALU -}; - -class VCMPacket { - public: - VCMPacket(); - - VCMPacket(const uint8_t* ptr, - size_t size, - const RTPHeader& rtp_header, - const RTPVideoHeader& video_header, - int64_t ntp_time_ms, - Timestamp receive_time); - - ~VCMPacket(); - - VideoCodecType codec() const { return video_header.codec; } - int width() const { return video_header.width; } - int height() const { return video_header.height; } - - bool is_first_packet_in_frame() const { - return video_header.is_first_packet_in_frame; - } - bool is_last_packet_in_frame() const { - return video_header.is_last_packet_in_frame; - } - - uint8_t payloadType; - uint32_t timestamp; - // NTP time of the capture time in local timebase in milliseconds. - int64_t ntp_time_ms_; - uint16_t seqNum; - const uint8_t* dataPtr; - size_t sizeBytes; - bool markerBit; - int timesNacked; - - VCMNaluCompleteness completeNALU; // Default is kNaluIncomplete. - bool insertStartCode; // True if a start code should be inserted before this - // packet. - RTPVideoHeader video_header; - std::optional generic_descriptor; - - RtpPacketInfo packet_info; -}; - -} // namespace webrtc -#endif // MODULES_VIDEO_CODING_DEPRECATED_PACKET_H_ diff --git a/modules/video_coding/deprecated/receiver.cc b/modules/video_coding/deprecated/receiver.cc deleted file mode 100644 index b42d5599c36..00000000000 --- a/modules/video_coding/deprecated/receiver.cc +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/receiver.h" - -#include -#include -#include -#include -#include -#include - -#include "absl/memory/memory.h" -#include "api/field_trials_view.h" -#include "api/units/time_delta.h" -#include "api/units/timestamp.h" -#include "api/video/encoded_image.h" -#include "api/video/video_timing.h" -#include "modules/video_coding/deprecated/event_wrapper.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/encoded_frame.h" -#include "modules/video_coding/include/video_coding_defines.h" -#include "modules/video_coding/internal_defines.h" -#include "modules/video_coding/timing/timing.h" -#include "rtc_base/logging.h" -#include "rtc_base/numerics/safe_conversions.h" -#include "rtc_base/trace_event.h" -#include "system_wrappers/include/clock.h" - -namespace webrtc { - -enum { kMaxReceiverDelayMs = 10000 }; - -VCMReceiver::VCMReceiver(VCMTiming* timing, - Clock* clock, - const FieldTrialsView& field_trials) - : VCMReceiver::VCMReceiver(timing, - clock, - absl::WrapUnique(EventWrapper::Create()), - absl::WrapUnique(EventWrapper::Create()), - field_trials) {} - -VCMReceiver::VCMReceiver(VCMTiming* timing, - Clock* clock, - std::unique_ptr receiver_event, - std::unique_ptr jitter_buffer_event, - const FieldTrialsView& field_trials) - : clock_(clock), - jitter_buffer_(clock_, std::move(jitter_buffer_event), field_trials), - timing_(timing), - render_wait_event_(std::move(receiver_event)), - max_video_delay_ms_(kMaxVideoDelayMs) { - jitter_buffer_.Start(); -} - -VCMReceiver::~VCMReceiver() { - render_wait_event_->Set(); -} - -int32_t VCMReceiver::InsertPacket(const VCMPacket& packet) { - // Insert the packet into the jitter buffer. The packet can either be empty or - // contain media at this point. - bool retransmitted = false; - const VCMFrameBufferEnum ret = - jitter_buffer_.InsertPacket(packet, &retransmitted); - if (ret == kOldPacket) { - return VCM_OK; - } else if (ret == kFlushIndicator) { - return VCM_FLUSH_INDICATOR; - } else if (ret < 0) { - return VCM_JITTER_BUFFER_ERROR; - } - if (ret == kCompleteSession && !retransmitted) { - // We don't want to include timestamps which have suffered from - // retransmission here, since we compensate with extra retransmission - // delay within the jitter estimate. - timing_->IncomingTimestamp(packet.timestamp, clock_->CurrentTime()); - } - return VCM_OK; -} - -VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms, - bool prefer_late_decoding) { - const int64_t start_time_ms = clock_->TimeInMilliseconds(); - int64_t render_time_ms = 0; - // Exhaust wait time to get a complete frame for decoding. - VCMEncodedFrame* found_frame = - jitter_buffer_.NextCompleteFrame(max_wait_time_ms); - - if (found_frame == nullptr) { - return nullptr; - } - uint32_t frame_timestamp = found_frame->RtpTimestamp(); - - if (std::optional playout_delay = - found_frame->EncodedImage().PlayoutDelay()) { - timing_->set_playout_delay(*playout_delay); - } - - // We have a frame - Set timing and render timestamp. - timing_->SetJitterDelay( - TimeDelta::Millis(jitter_buffer_.EstimatedJitterMs())); - const Timestamp now = clock_->CurrentTime(); - const int64_t now_ms = now.ms(); - timing_->UpdateCurrentDelay(frame_timestamp); - render_time_ms = timing_->RenderTime(frame_timestamp, now).ms(); - // Check render timing. - bool timing_error = false; - // Assume that render timing errors are due to changes in the video stream. - if (render_time_ms < 0) { - timing_error = true; - } else if (std::abs(render_time_ms - now_ms) > max_video_delay_ms_) { - int frame_delay = static_cast(std::abs(render_time_ms - now_ms)); - RTC_LOG(LS_WARNING) - << "A frame about to be decoded is out of the configured " - "delay bounds (" - << frame_delay << " > " << max_video_delay_ms_ - << "). Resetting the video jitter buffer."; - timing_error = true; - } else if (static_cast(timing_->TargetVideoDelay().ms()) > - max_video_delay_ms_) { - RTC_LOG(LS_WARNING) << "The video target delay has grown larger than " - << max_video_delay_ms_ - << " ms. Resetting jitter buffer."; - timing_error = true; - } - - if (timing_error) { - // Timing error => reset timing and flush the jitter buffer. - jitter_buffer_.Flush(); - timing_->Reset(); - return nullptr; - } - - if (prefer_late_decoding) { - // Decode frame as close as possible to the render timestamp. - const int32_t available_wait_time = - max_wait_time_ms - - static_cast(clock_->TimeInMilliseconds() - start_time_ms); - uint16_t new_max_wait_time = - static_cast(VCM_MAX(available_wait_time, 0)); - uint32_t wait_time_ms = saturated_cast( - timing_ - ->MaxWaitingTime(Timestamp::Millis(render_time_ms), - clock_->CurrentTime(), - /*too_many_frames_queued=*/false) - .ms()); - if (new_max_wait_time < wait_time_ms) { - // We're not allowed to wait until the frame is supposed to be rendered, - // waiting as long as we're allowed to avoid busy looping, and then return - // NULL. Next call to this function might return the frame. - render_wait_event_->Wait(new_max_wait_time); - return nullptr; - } - // Wait until it's time to render. - render_wait_event_->Wait(wait_time_ms); - } - - // Extract the frame from the jitter buffer and set the render time. - VCMEncodedFrame* frame = jitter_buffer_.ExtractAndSetDecode(frame_timestamp); - if (frame == nullptr) { - return nullptr; - } - frame->SetRenderTime(render_time_ms); - TRACE_EVENT_ASYNC_STEP_INTO1("webrtc", "Video", frame->RtpTimestamp(), - "SetRenderTS", "render_time", - frame->RenderTimeMs()); - return frame; -} - -void VCMReceiver::ReleaseFrame(VCMEncodedFrame* frame) { - jitter_buffer_.ReleaseFrame(frame); -} - -void VCMReceiver::SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms) { - jitter_buffer_.SetNackSettings(max_nack_list_size, max_packet_age_to_nack, - max_incomplete_time_ms); -} - -std::vector VCMReceiver::NackList(bool* request_key_frame) { - return jitter_buffer_.GetNackList(request_key_frame); -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/receiver.h b/modules/video_coding/deprecated/receiver.h deleted file mode 100644 index f537952abe2..00000000000 --- a/modules/video_coding/deprecated/receiver.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_RECEIVER_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_RECEIVER_H_ - -#include -#include -#include -#include - -#include "api/field_trials_view.h" -#include "modules/video_coding/deprecated/event_wrapper.h" -#include "modules/video_coding/deprecated/jitter_buffer.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/timing/timing.h" - -namespace webrtc { - -class Clock; -class VCMEncodedFrame; - -class VCMReceiver { - public: - VCMReceiver(VCMTiming* timing, - Clock* clock, - const FieldTrialsView& field_trials); - - // Using this constructor, you can specify a different event implemetation for - // the jitter buffer. Useful for unit tests when you want to simulate incoming - // packets, in which case the jitter buffer's wait event is different from - // that of VCMReceiver itself. - VCMReceiver(VCMTiming* timing, - Clock* clock, - std::unique_ptr receiver_event, - std::unique_ptr jitter_buffer_event, - const FieldTrialsView& field_trials); - - ~VCMReceiver(); - - int32_t InsertPacket(const VCMPacket& packet); - VCMEncodedFrame* FrameForDecoding(uint16_t max_wait_time_ms, - bool prefer_late_decoding); - void ReleaseFrame(VCMEncodedFrame* frame); - - // NACK. - void SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms); - std::vector NackList(bool* request_key_frame); - - private: - Clock* const clock_; - VCMJitterBuffer jitter_buffer_; - VCMTiming* timing_; - std::unique_ptr render_wait_event_; - int max_video_delay_ms_; -}; - -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_RECEIVER_H_ diff --git a/modules/video_coding/deprecated/receiver_unittest.cc b/modules/video_coding/deprecated/receiver_unittest.cc deleted file mode 100644 index f6e30d84800..00000000000 --- a/modules/video_coding/deprecated/receiver_unittest.cc +++ /dev/null @@ -1,498 +0,0 @@ -/* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/receiver.h" - -#include -#include -#include -#include -#include - -#include "api/field_trials.h" -#include "api/units/time_delta.h" -#include "api/video/video_frame_type.h" -#include "modules/video_coding/deprecated/event_wrapper.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/deprecated/stream_generator.h" -#include "modules/video_coding/encoded_frame.h" -#include "modules/video_coding/timing/timing.h" -#include "rtc_base/checks.h" -#include "system_wrappers/include/clock.h" -#include "test/create_test_field_trials.h" -#include "test/gtest.h" - -namespace webrtc { - -class TestVCMReceiver : public ::testing::Test { - protected: - TestVCMReceiver() - : field_trials_(CreateTestFieldTrials()), - clock_(0), - timing_(&clock_, field_trials_), - receiver_(&timing_, &clock_, field_trials_), - stream_generator_(0, clock_.TimeInMilliseconds()) {} - - int32_t InsertPacket(int index) { - VCMPacket packet; - bool packet_available = stream_generator_.GetPacket(&packet, index); - EXPECT_TRUE(packet_available); - if (!packet_available) - return kGeneralError; // Return here to avoid crashes below. - return receiver_.InsertPacket(packet); - } - - int32_t InsertPacketAndPop(int index) { - VCMPacket packet; - bool packet_available = stream_generator_.PopPacket(&packet, index); - EXPECT_TRUE(packet_available); - if (!packet_available) - return kGeneralError; // Return here to avoid crashes below. - return receiver_.InsertPacket(packet); - } - - int32_t InsertFrame(VideoFrameType frame_type, bool complete) { - int num_of_packets = complete ? 1 : 2; - stream_generator_.GenerateFrame( - frame_type, - (frame_type != VideoFrameType::kEmptyFrame) ? num_of_packets : 0, - (frame_type == VideoFrameType::kEmptyFrame) ? 1 : 0, - clock_.TimeInMilliseconds()); - int32_t ret = InsertPacketAndPop(0); - if (!complete) { - // Drop the second packet. - VCMPacket packet; - stream_generator_.PopPacket(&packet, 0); - } - clock_.AdvanceTimeMilliseconds(kDefaultFramePeriodMs); - return ret; - } - - bool DecodeNextFrame() { - VCMEncodedFrame* frame = receiver_.FrameForDecoding(0, false); - if (!frame) - return false; - receiver_.ReleaseFrame(frame); - return true; - } - - FieldTrials field_trials_; - SimulatedClock clock_; - VCMTiming timing_; - VCMReceiver receiver_; - StreamGenerator stream_generator_; -}; - -TEST_F(TestVCMReceiver, NonDecodableDuration_Empty) { - const size_t kMaxNackListSize = 1000; - const int kMaxPacketAgeToNack = 1000; - const int kMaxNonDecodableDuration = 500; - const int kMinDelayMs = 500; - receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, - kMaxNonDecodableDuration); - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); - // Advance time until it's time to decode the key frame. - clock_.AdvanceTimeMilliseconds(kMinDelayMs); - EXPECT_TRUE(DecodeNextFrame()); - bool request_key_frame = false; - std::vector nack_list = receiver_.NackList(&request_key_frame); - EXPECT_FALSE(request_key_frame); -} - -TEST_F(TestVCMReceiver, NonDecodableDuration_NoKeyFrame) { - const size_t kMaxNackListSize = 1000; - const int kMaxPacketAgeToNack = 1000; - const int kMaxNonDecodableDuration = 500; - receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, - kMaxNonDecodableDuration); - const int kNumFrames = kDefaultFrameRate * kMaxNonDecodableDuration / 1000; - for (int i = 0; i < kNumFrames; ++i) { - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError); - } - bool request_key_frame = false; - std::vector nack_list = receiver_.NackList(&request_key_frame); - EXPECT_TRUE(request_key_frame); -} - -TEST_F(TestVCMReceiver, NonDecodableDuration_OneIncomplete) { - const size_t kMaxNackListSize = 1000; - const int kMaxPacketAgeToNack = 1000; - const int kMaxNonDecodableDuration = 500; - const int kMaxNonDecodableDurationFrames = - (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; - const int kMinDelayMs = 500; - receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, - kMaxNonDecodableDuration); - timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); - int64_t key_frame_inserted = clock_.TimeInMilliseconds(); - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); - // Insert an incomplete frame. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError); - // Insert enough frames to have too long non-decodable sequence. - for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError); - } - // Advance time until it's time to decode the key frame. - clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() - - key_frame_inserted); - EXPECT_TRUE(DecodeNextFrame()); - // Make sure we get a key frame request. - bool request_key_frame = false; - std::vector nack_list = receiver_.NackList(&request_key_frame); - EXPECT_TRUE(request_key_frame); -} - -TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger) { - const size_t kMaxNackListSize = 1000; - const int kMaxPacketAgeToNack = 1000; - const int kMaxNonDecodableDuration = 500; - const int kMaxNonDecodableDurationFrames = - (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; - const int kMinDelayMs = 500; - receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, - kMaxNonDecodableDuration); - timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); - int64_t key_frame_inserted = clock_.TimeInMilliseconds(); - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); - // Insert an incomplete frame. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError); - // Insert all but one frame to not trigger a key frame request due to - // too long duration of non-decodable frames. - for (int i = 0; i < kMaxNonDecodableDurationFrames - 1; ++i) { - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError); - } - // Advance time until it's time to decode the key frame. - clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() - - key_frame_inserted); - EXPECT_TRUE(DecodeNextFrame()); - // Make sure we don't get a key frame request since we haven't generated - // enough frames. - bool request_key_frame = false; - std::vector nack_list = receiver_.NackList(&request_key_frame); - EXPECT_FALSE(request_key_frame); -} - -TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger2) { - const size_t kMaxNackListSize = 1000; - const int kMaxPacketAgeToNack = 1000; - const int kMaxNonDecodableDuration = 500; - const int kMaxNonDecodableDurationFrames = - (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; - const int kMinDelayMs = 500; - receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, - kMaxNonDecodableDuration); - timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); - int64_t key_frame_inserted = clock_.TimeInMilliseconds(); - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); - // Insert enough frames to have too long non-decodable sequence, except that - // we don't have any losses. - for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError); - } - // Insert an incomplete frame. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError); - // Advance time until it's time to decode the key frame. - clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() - - key_frame_inserted); - EXPECT_TRUE(DecodeNextFrame()); - // Make sure we don't get a key frame request since the non-decodable duration - // is only one frame. - bool request_key_frame = false; - std::vector nack_list = receiver_.NackList(&request_key_frame); - EXPECT_FALSE(request_key_frame); -} - -TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) { - const size_t kMaxNackListSize = 1000; - const int kMaxPacketAgeToNack = 1000; - const int kMaxNonDecodableDuration = 500; - const int kMaxNonDecodableDurationFrames = - (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; - const int kMinDelayMs = 500; - receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, - kMaxNonDecodableDuration); - timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); - int64_t key_frame_inserted = clock_.TimeInMilliseconds(); - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); - // Insert an incomplete frame. - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError); - // Insert enough frames to have too long non-decodable sequence. - for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError); - } - EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); - // Advance time until it's time to decode the key frame. - clock_.AdvanceTimeMilliseconds(kMinDelayMs - clock_.TimeInMilliseconds() - - key_frame_inserted); - EXPECT_TRUE(DecodeNextFrame()); - // Make sure we don't get a key frame request since we have a key frame - // in the list. - bool request_key_frame = false; - std::vector nack_list = receiver_.NackList(&request_key_frame); - EXPECT_FALSE(request_key_frame); -} - -// A simulated clock, when time elapses, will insert frames into the jitter -// buffer, based on initial settings. -class SimulatedClockWithFrames : public SimulatedClock { - public: - SimulatedClockWithFrames(StreamGenerator* stream_generator, - VCMReceiver* receiver) - : SimulatedClock(0), - stream_generator_(stream_generator), - receiver_(receiver) {} - ~SimulatedClockWithFrames() override {} - - // If `stop_on_frame` is true and next frame arrives between now and - // now+`milliseconds`, the clock will be advanced to the arrival time of next - // frame. - // Otherwise, the clock will be advanced by `milliseconds`. - // - // For both cases, a frame will be inserted into the jitter buffer at the - // instant when the clock time is timestamps_.front().arrive_time. - // - // Return true if some frame arrives between now and now+`milliseconds`. - bool AdvanceTimeMilliseconds(int64_t milliseconds, bool stop_on_frame) { - return AdvanceTimeMicroseconds(milliseconds * 1000, stop_on_frame); - } - - bool AdvanceTimeMicroseconds(int64_t microseconds, bool stop_on_frame) { - int64_t start_time = TimeInMicroseconds(); - int64_t end_time = start_time + microseconds; - bool frame_injected = false; - while (!timestamps_.empty() && - timestamps_.front().arrive_time <= end_time) { - RTC_DCHECK_GE(timestamps_.front().arrive_time, start_time); - - SimulatedClock::AdvanceTimeMicroseconds(timestamps_.front().arrive_time - - TimeInMicroseconds()); - GenerateAndInsertFrame((timestamps_.front().render_time + 500) / 1000); - timestamps_.pop(); - frame_injected = true; - - if (stop_on_frame) - return frame_injected; - } - - if (TimeInMicroseconds() < end_time) { - SimulatedClock::AdvanceTimeMicroseconds(end_time - TimeInMicroseconds()); - } - return frame_injected; - } - - // Input timestamps are in unit Milliseconds. - // And `arrive_timestamps` must be positive and in increasing order. - // `arrive_timestamps` determine when we are going to insert frames into the - // jitter buffer. - // `render_timestamps` are the timestamps on the frame. - void SetFrames(const int64_t* arrive_timestamps, - const int64_t* render_timestamps, - size_t size) { - int64_t previous_arrive_timestamp = 0; - for (size_t i = 0; i < size; i++) { - RTC_CHECK_GE(arrive_timestamps[i], previous_arrive_timestamp); - timestamps_.push(TimestampPair(arrive_timestamps[i] * 1000, - render_timestamps[i] * 1000)); - previous_arrive_timestamp = arrive_timestamps[i]; - } - } - - private: - struct TimestampPair { - TimestampPair(int64_t arrive_timestamp, int64_t render_timestamp) - : arrive_time(arrive_timestamp), render_time(render_timestamp) {} - - int64_t arrive_time; - int64_t render_time; - }; - - void GenerateAndInsertFrame(int64_t render_timestamp_ms) { - VCMPacket packet; - stream_generator_->GenerateFrame(VideoFrameType::kVideoFrameKey, - 1, // media packets - 0, // empty packets - render_timestamp_ms); - - bool packet_available = stream_generator_->PopPacket(&packet, 0); - EXPECT_TRUE(packet_available); - if (!packet_available) - return; // Return here to avoid crashes below. - receiver_->InsertPacket(packet); - } - - std::queue timestamps_; - StreamGenerator* stream_generator_; - VCMReceiver* receiver_; -}; - -// Use a SimulatedClockWithFrames -// Wait call will do either of these: -// 1. If `stop_on_frame` is true, the clock will be turned to the exact instant -// that the first frame comes and the frame will be inserted into the jitter -// buffer, or the clock will be turned to now + `max_time` if no frame comes in -// the window. -// 2. If `stop_on_frame` is false, the clock will be turn to now + `max_time`, -// and all the frames arriving between now and now + `max_time` will be -// inserted into the jitter buffer. -// -// This is used to simulate the JitterBuffer getting packets from internet as -// time elapses. - -class FrameInjectEvent : public EventWrapper { - public: - FrameInjectEvent(SimulatedClockWithFrames* clock, bool stop_on_frame) - : clock_(clock), stop_on_frame_(stop_on_frame) {} - - bool Set() override { return true; } - - EventTypeWrapper Wait(int max_time_ms) override { - if (clock_->AdvanceTimeMilliseconds(max_time_ms, stop_on_frame_) && - stop_on_frame_) { - return EventTypeWrapper::kEventSignaled; - } else { - return EventTypeWrapper::kEventTimeout; - } - } - - private: - SimulatedClockWithFrames* clock_; - bool stop_on_frame_; -}; - -class VCMReceiverTimingTest : public ::testing::Test { - protected: - VCMReceiverTimingTest() - : field_trials_(CreateTestFieldTrials()), - clock_(&stream_generator_, &receiver_), - stream_generator_(0, clock_.TimeInMilliseconds()), - timing_(&clock_, field_trials_), - receiver_( - &timing_, - &clock_, - std::unique_ptr(new FrameInjectEvent(&clock_, false)), - std::unique_ptr(new FrameInjectEvent(&clock_, true)), - field_trials_) {} - - void SetUp() override {} - - FieldTrials field_trials_; - SimulatedClockWithFrames clock_; - StreamGenerator stream_generator_; - VCMTiming timing_; - VCMReceiver receiver_; -}; - -// Test whether VCMReceiver::FrameForDecoding handles parameter -// `max_wait_time_ms` correctly: -// 1. The function execution should never take more than `max_wait_time_ms`. -// 2. If the function exit before now + `max_wait_time_ms`, a frame must be -// returned. -TEST_F(VCMReceiverTimingTest, FrameForDecoding) { - const size_t kNumFrames = 100; - const int kFramePeriod = 40; - int64_t arrive_timestamps[kNumFrames]; - int64_t render_timestamps[kNumFrames]; - - // Construct test samples. - // render_timestamps are the timestamps stored in the Frame; - // arrive_timestamps controls when the Frame packet got received. - for (size_t i = 0; i < kNumFrames; i++) { - // Preset frame rate to 25Hz. - // But we add a reasonable deviation to arrive_timestamps to mimic Internet - // fluctuation. - arrive_timestamps[i] = - (i + 1) * kFramePeriod + (i % 10) * ((i % 2) ? 1 : -1); - render_timestamps[i] = (i + 1) * kFramePeriod; - } - - clock_.SetFrames(arrive_timestamps, render_timestamps, kNumFrames); - - // Record how many frames we finally get out of the receiver. - size_t num_frames_return = 0; - - const int64_t kMaxWaitTime = 30; - - // Ideally, we should get all frames that we input in InitializeFrames. - // In the case that FrameForDecoding kills frames by error, we rely on the - // build bot to kill the test. - while (num_frames_return < kNumFrames) { - int64_t start_time = clock_.TimeInMilliseconds(); - VCMEncodedFrame* frame = receiver_.FrameForDecoding(kMaxWaitTime, false); - int64_t end_time = clock_.TimeInMilliseconds(); - - // In any case the FrameForDecoding should not wait longer than - // max_wait_time. - // In the case that we did not get a frame, it should have been waiting for - // exactly max_wait_time. (By the testing samples we constructed above, we - // are sure there is no timing error, so the only case it returns with NULL - // is that it runs out of time.) - if (frame) { - receiver_.ReleaseFrame(frame); - ++num_frames_return; - EXPECT_GE(kMaxWaitTime, end_time - start_time); - } else { - EXPECT_EQ(kMaxWaitTime, end_time - start_time); - } - } -} - -// Test whether VCMReceiver::FrameForDecoding handles parameter -// `prefer_late_decoding` and `max_wait_time_ms` correctly: -// 1. The function execution should never take more than `max_wait_time_ms`. -// 2. If the function exit before now + `max_wait_time_ms`, a frame must be -// returned and the end time must be equal to the render timestamp - delay -// for decoding and rendering. -TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) { - const size_t kNumFrames = 100; - const int kFramePeriod = 40; - - int64_t arrive_timestamps[kNumFrames]; - int64_t render_timestamps[kNumFrames]; - - auto timings = timing_.GetTimings(); - TimeDelta render_delay = timings.render_delay; - TimeDelta max_decode = timings.estimated_max_decode_time; - - // Construct test samples. - // render_timestamps are the timestamps stored in the Frame; - // arrive_timestamps controls when the Frame packet got received. - for (size_t i = 0; i < kNumFrames; i++) { - // Preset frame rate to 25Hz. - // But we add a reasonable deviation to arrive_timestamps to mimic Internet - // fluctuation. - arrive_timestamps[i] = - (i + 1) * kFramePeriod + (i % 10) * ((i % 2) ? 1 : -1); - render_timestamps[i] = (i + 1) * kFramePeriod; - } - - clock_.SetFrames(arrive_timestamps, render_timestamps, kNumFrames); - - // Record how many frames we finally get out of the receiver. - size_t num_frames_return = 0; - const int64_t kMaxWaitTime = 30; - bool prefer_late_decoding = true; - while (num_frames_return < kNumFrames) { - int64_t start_time = clock_.TimeInMilliseconds(); - - VCMEncodedFrame* frame = - receiver_.FrameForDecoding(kMaxWaitTime, prefer_late_decoding); - int64_t end_time = clock_.TimeInMilliseconds(); - if (frame) { - EXPECT_EQ(frame->RenderTimeMs() - max_decode.ms() - render_delay.ms(), - end_time); - receiver_.ReleaseFrame(frame); - ++num_frames_return; - } else { - EXPECT_EQ(kMaxWaitTime, end_time - start_time); - } - } -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/session_info.cc b/modules/video_coding/deprecated/session_info.cc deleted file mode 100644 index 19a93b41223..00000000000 --- a/modules/video_coding/deprecated/session_info.cc +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/session_info.h" - -#include -#include -#include -#include -#include - -#include "absl/algorithm/container.h" -#include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" -#include "modules/include/module_common_types_public.h" -#include "modules/video_coding/codecs/h264/include/h264_globals.h" -#include "modules/video_coding/codecs/interface/common_constants.h" -#include "modules/video_coding/codecs/vp8/include/vp8_globals.h" -#include "modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "modules/video_coding/deprecated/jitter_buffer_common.h" -#include "modules/video_coding/deprecated/packet.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" - -namespace webrtc { - -namespace { - -uint16_t BufferToUWord16(const uint8_t* dataBuffer) { - return (dataBuffer[0] << 8) | dataBuffer[1]; -} - -} // namespace - -VCMSessionInfo::VCMSessionInfo() - : complete_(false), - frame_type_(VideoFrameType::kVideoFrameDelta), - packets_(), - empty_seq_num_low_(-1), - empty_seq_num_high_(-1), - first_packet_seq_num_(-1), - last_packet_seq_num_(-1) {} - -VCMSessionInfo::~VCMSessionInfo() {} - -void VCMSessionInfo::UpdateDataPointers(const uint8_t* old_base_ptr, - const uint8_t* new_base_ptr) { - for (PacketIterator it = packets_.begin(); it != packets_.end(); ++it) - if ((*it).dataPtr != nullptr) { - RTC_DCHECK(old_base_ptr != nullptr && new_base_ptr != nullptr); - (*it).dataPtr = new_base_ptr + ((*it).dataPtr - old_base_ptr); - } -} - -int VCMSessionInfo::LowSequenceNumber() const { - if (packets_.empty()) - return empty_seq_num_low_; - return packets_.front().seqNum; -} - -int VCMSessionInfo::HighSequenceNumber() const { - if (packets_.empty()) - return empty_seq_num_high_; - if (empty_seq_num_high_ == -1) - return packets_.back().seqNum; - return LatestSequenceNumber(packets_.back().seqNum, empty_seq_num_high_); -} - -int VCMSessionInfo::PictureId() const { - if (packets_.empty()) - return kNoPictureId; - if (packets_.front().video_header.codec == kVideoCodecVP8) { - return std::get( - packets_.front().video_header.video_type_header) - .pictureId; - } else if (packets_.front().video_header.codec == kVideoCodecVP9) { - return std::get( - packets_.front().video_header.video_type_header) - .picture_id; - } else { - return kNoPictureId; - } -} - -int VCMSessionInfo::TemporalId() const { - if (packets_.empty()) - return kNoTemporalIdx; - if (packets_.front().video_header.codec == kVideoCodecVP8) { - return std::get( - packets_.front().video_header.video_type_header) - .temporalIdx; - } else if (packets_.front().video_header.codec == kVideoCodecVP9) { - return std::get( - packets_.front().video_header.video_type_header) - .temporal_idx; - } else { - return kNoTemporalIdx; - } -} - -bool VCMSessionInfo::LayerSync() const { - if (packets_.empty()) - return false; - if (packets_.front().video_header.codec == kVideoCodecVP8) { - return std::get( - packets_.front().video_header.video_type_header) - .layerSync; - } else if (packets_.front().video_header.codec == kVideoCodecVP9) { - return std::get( - packets_.front().video_header.video_type_header) - .temporal_up_switch; - } else { - return false; - } -} - -int VCMSessionInfo::Tl0PicId() const { - if (packets_.empty()) - return kNoTl0PicIdx; - if (packets_.front().video_header.codec == kVideoCodecVP8) { - return std::get( - packets_.front().video_header.video_type_header) - .tl0PicIdx; - } else if (packets_.front().video_header.codec == kVideoCodecVP9) { - return std::get( - packets_.front().video_header.video_type_header) - .tl0_pic_idx; - } else { - return kNoTl0PicIdx; - } -} - -std::vector VCMSessionInfo::GetNaluInfos() const { - if (packets_.empty() || - packets_.front().video_header.codec != kVideoCodecH264) - return std::vector(); - std::vector nalu_infos; - for (const VCMPacket& packet : packets_) { - const auto& h264 = - std::get(packet.video_header.video_type_header); - absl::c_copy(h264.nalus, std::back_inserter(nalu_infos)); - } - return nalu_infos; -} - -void VCMSessionInfo::SetGofInfo(const GofInfoVP9& gof_info, size_t idx) { - if (packets_.empty()) - return; - - auto* vp9_header = std::get_if( - &packets_.front().video_header.video_type_header); - if (!vp9_header || vp9_header->flexible_mode) - return; - - vp9_header->temporal_idx = gof_info.temporal_idx[idx]; - vp9_header->temporal_up_switch = gof_info.temporal_up_switch[idx]; - vp9_header->num_ref_pics = gof_info.num_ref_pics[idx]; - for (uint8_t i = 0; i < gof_info.num_ref_pics[idx]; ++i) { - vp9_header->pid_diff[i] = gof_info.pid_diff[idx][i]; - } -} - -void VCMSessionInfo::Reset() { - complete_ = false; - frame_type_ = VideoFrameType::kVideoFrameDelta; - packets_.clear(); - empty_seq_num_low_ = -1; - empty_seq_num_high_ = -1; - first_packet_seq_num_ = -1; - last_packet_seq_num_ = -1; -} - -size_t VCMSessionInfo::SessionLength() const { - size_t length = 0; - for (PacketIteratorConst it = packets_.begin(); it != packets_.end(); ++it) - length += (*it).sizeBytes; - return length; -} - -int VCMSessionInfo::NumPackets() const { - return packets_.size(); -} - -size_t VCMSessionInfo::InsertBuffer(uint8_t* frame_buffer, - PacketIterator packet_it) { - VCMPacket& packet = *packet_it; - PacketIterator it; - - // Calculate the offset into the frame buffer for this packet. - size_t offset = 0; - for (it = packets_.begin(); it != packet_it; ++it) - offset += (*it).sizeBytes; - - // Set the data pointer to pointing to the start of this packet in the - // frame buffer. - const uint8_t* packet_buffer = packet.dataPtr; - packet.dataPtr = frame_buffer + offset; - - // We handle H.264 STAP-A packets in a special way as we need to remove the - // two length bytes between each NAL unit, and potentially add start codes. - // TODO(pbos): Remove H264 parsing from this step and use a fragmentation - // header supplied by the H264 depacketizer. - const size_t kH264NALHeaderLengthInBytes = 1; - const size_t kLengthFieldLength = 2; - const auto* h264 = - std::get_if(&packet.video_header.video_type_header); - if (h264 && h264->packetization_type == kH264StapA) { - size_t required_length = 0; - const uint8_t* nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes; - while (nalu_ptr < packet_buffer + packet.sizeBytes) { - size_t length = BufferToUWord16(nalu_ptr); - required_length += - length + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0); - nalu_ptr += kLengthFieldLength + length; - } - ShiftSubsequentPackets(packet_it, required_length); - nalu_ptr = packet_buffer + kH264NALHeaderLengthInBytes; - uint8_t* frame_buffer_ptr = frame_buffer + offset; - while (nalu_ptr < packet_buffer + packet.sizeBytes) { - size_t length = BufferToUWord16(nalu_ptr); - nalu_ptr += kLengthFieldLength; - frame_buffer_ptr += Insert(nalu_ptr, length, packet.insertStartCode, - const_cast(frame_buffer_ptr)); - nalu_ptr += length; - } - packet.sizeBytes = required_length; - return packet.sizeBytes; - } - ShiftSubsequentPackets( - packet_it, packet.sizeBytes + - (packet.insertStartCode ? kH264StartCodeLengthBytes : 0)); - - packet.sizeBytes = - Insert(packet_buffer, packet.sizeBytes, packet.insertStartCode, - const_cast(packet.dataPtr)); - return packet.sizeBytes; -} - -size_t VCMSessionInfo::Insert(const uint8_t* buffer, - size_t length, - bool insert_start_code, - uint8_t* frame_buffer) { - if (!buffer || !frame_buffer) { - return 0; - } - if (insert_start_code) { - const unsigned char startCode[] = {0, 0, 0, 1}; - memcpy(frame_buffer, startCode, kH264StartCodeLengthBytes); - } - memcpy(frame_buffer + (insert_start_code ? kH264StartCodeLengthBytes : 0), - buffer, length); - length += (insert_start_code ? kH264StartCodeLengthBytes : 0); - - return length; -} - -void VCMSessionInfo::ShiftSubsequentPackets(PacketIterator it, - int steps_to_shift) { - ++it; - if (it == packets_.end()) - return; - uint8_t* first_packet_ptr = const_cast((*it).dataPtr); - int shift_length = 0; - // Calculate the total move length and move the data pointers in advance. - for (; it != packets_.end(); ++it) { - shift_length += (*it).sizeBytes; - if ((*it).dataPtr != nullptr) - (*it).dataPtr += steps_to_shift; - } - memmove(first_packet_ptr + steps_to_shift, first_packet_ptr, shift_length); -} - -void VCMSessionInfo::UpdateCompleteSession() { - if (HaveFirstPacket() && HaveLastPacket()) { - // Do we have all the packets in this session? - bool complete_session = true; - PacketIterator it = packets_.begin(); - PacketIterator prev_it = it; - ++it; - for (; it != packets_.end(); ++it) { - if (!InSequence(it, prev_it)) { - complete_session = false; - break; - } - prev_it = it; - } - complete_ = complete_session; - } -} - -bool VCMSessionInfo::complete() const { - return complete_; -} - -// Find the end of the NAL unit which the packet pointed to by `packet_it` -// belongs to. Returns an iterator to the last packet of the frame if the end -// of the NAL unit wasn't found. -VCMSessionInfo::PacketIterator VCMSessionInfo::FindNaluEnd( - PacketIterator packet_it) const { - if ((*packet_it).completeNALU == kNaluEnd || - (*packet_it).completeNALU == kNaluComplete) { - return packet_it; - } - // Find the end of the NAL unit. - for (; packet_it != packets_.end(); ++packet_it) { - if (((*packet_it).completeNALU == kNaluComplete && - (*packet_it).sizeBytes > 0) || - // Found next NALU. - (*packet_it).completeNALU == kNaluStart) - return --packet_it; - if ((*packet_it).completeNALU == kNaluEnd) - return packet_it; - } - // The end wasn't found. - return --packet_it; -} - -size_t VCMSessionInfo::DeletePacketData(PacketIterator start, - PacketIterator end) { - size_t bytes_to_delete = 0; // The number of bytes to delete. - PacketIterator packet_after_end = end; - ++packet_after_end; - - // Get the number of bytes to delete. - // Clear the size of these packets. - for (PacketIterator it = start; it != packet_after_end; ++it) { - bytes_to_delete += (*it).sizeBytes; - (*it).sizeBytes = 0; - (*it).dataPtr = nullptr; - } - if (bytes_to_delete > 0) - ShiftSubsequentPackets(end, -static_cast(bytes_to_delete)); - return bytes_to_delete; -} - -VCMSessionInfo::PacketIterator VCMSessionInfo::FindNextPartitionBeginning( - PacketIterator it) const { - while (it != packets_.end()) { - if (std::get((*it).video_header.video_type_header) - .beginningOfPartition) { - return it; - } - ++it; - } - return it; -} - -VCMSessionInfo::PacketIterator VCMSessionInfo::FindPartitionEnd( - PacketIterator it) const { - RTC_DCHECK_EQ((*it).codec(), kVideoCodecVP8); - PacketIterator prev_it = it; - const int partition_id = - std::get((*it).video_header.video_type_header) - .partitionId; - while (it != packets_.end()) { - bool beginning = - std::get((*it).video_header.video_type_header) - .beginningOfPartition; - int current_partition_id = - std::get((*it).video_header.video_type_header) - .partitionId; - bool packet_loss_found = (!beginning && !InSequence(it, prev_it)); - if (packet_loss_found || - (beginning && current_partition_id != partition_id)) { - // Missing packet, the previous packet was the last in sequence. - return prev_it; - } - prev_it = it; - ++it; - } - return prev_it; -} - -bool VCMSessionInfo::InSequence(const PacketIterator& packet_it, - const PacketIterator& prev_packet_it) { - // If the two iterators are pointing to the same packet they are considered - // to be in sequence. - return (packet_it == prev_packet_it || - (static_cast((*prev_packet_it).seqNum + 1) == - (*packet_it).seqNum)); -} - -size_t VCMSessionInfo::MakeDecodable() { - size_t return_length = 0; - if (packets_.empty()) { - return 0; - } - PacketIterator it = packets_.begin(); - // Make sure we remove the first NAL unit if it's not decodable. - if ((*it).completeNALU == kNaluIncomplete || (*it).completeNALU == kNaluEnd) { - PacketIterator nalu_end = FindNaluEnd(it); - return_length += DeletePacketData(it, nalu_end); - it = nalu_end; - } - PacketIterator prev_it = it; - // Take care of the rest of the NAL units. - for (; it != packets_.end(); ++it) { - bool start_of_nalu = ((*it).completeNALU == kNaluStart || - (*it).completeNALU == kNaluComplete); - if (!start_of_nalu && !InSequence(it, prev_it)) { - // Found a sequence number gap due to packet loss. - PacketIterator nalu_end = FindNaluEnd(it); - return_length += DeletePacketData(it, nalu_end); - it = nalu_end; - } - prev_it = it; - } - return return_length; -} - -bool VCMSessionInfo::HaveFirstPacket() const { - return !packets_.empty() && (first_packet_seq_num_ != -1); -} - -bool VCMSessionInfo::HaveLastPacket() const { - return !packets_.empty() && (last_packet_seq_num_ != -1); -} - -int VCMSessionInfo::InsertPacket(const VCMPacket& packet, - uint8_t* frame_buffer, - const FrameData& /* frame_data */) { - if (packet.video_header.frame_type == VideoFrameType::kEmptyFrame) { - // Update sequence number of an empty packet. - // Only media packets are inserted into the packet list. - InformOfEmptyPacket(packet.seqNum); - return 0; - } - - if (packets_.size() == kMaxPacketsInSession) { - RTC_LOG(LS_ERROR) << "Max number of packets per frame has been reached."; - return -1; - } - - // Find the position of this packet in the packet list in sequence number - // order and insert it. Loop over the list in reverse order. - ReversePacketIterator rit = packets_.rbegin(); - for (; rit != packets_.rend(); ++rit) - if (LatestSequenceNumber(packet.seqNum, (*rit).seqNum) == packet.seqNum) - break; - - // Check for duplicate packets. - if (rit != packets_.rend() && (*rit).seqNum == packet.seqNum && - (*rit).sizeBytes > 0) - return -2; - - if (packet.codec() == kVideoCodecH264) { - frame_type_ = packet.video_header.frame_type; - if (packet.is_first_packet_in_frame() && - (first_packet_seq_num_ == -1 || - IsNewerSequenceNumber(first_packet_seq_num_, packet.seqNum))) { - first_packet_seq_num_ = packet.seqNum; - } - if (packet.markerBit && - (last_packet_seq_num_ == -1 || - IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_))) { - last_packet_seq_num_ = packet.seqNum; - } - } else { - // Only insert media packets between first and last packets (when - // available). - // Placing check here, as to properly account for duplicate packets. - // Check if this is first packet (only valid for some codecs) - // Should only be set for one packet per session. - if (packet.is_first_packet_in_frame() && first_packet_seq_num_ == -1) { - // The first packet in a frame signals the frame type. - frame_type_ = packet.video_header.frame_type; - // Store the sequence number for the first packet. - first_packet_seq_num_ = static_cast(packet.seqNum); - } else if (first_packet_seq_num_ != -1 && - IsNewerSequenceNumber(first_packet_seq_num_, packet.seqNum)) { - RTC_LOG(LS_WARNING) - << "Received packet with a sequence number which is out " - "of frame boundaries"; - return -3; - } else if (frame_type_ == VideoFrameType::kEmptyFrame && - packet.video_header.frame_type != VideoFrameType::kEmptyFrame) { - // Update the frame type with the type of the first media packet. - // TODO(mikhal): Can this trigger? - frame_type_ = packet.video_header.frame_type; - } - - // Track the marker bit, should only be set for one packet per session. - if (packet.markerBit && last_packet_seq_num_ == -1) { - last_packet_seq_num_ = static_cast(packet.seqNum); - } else if (last_packet_seq_num_ != -1 && - IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_)) { - RTC_LOG(LS_WARNING) - << "Received packet with a sequence number which is out " - "of frame boundaries"; - return -3; - } - } - - // The insert operation invalidates the iterator `rit`. - PacketIterator packet_list_it = packets_.insert(rit.base(), packet); - - size_t returnLength = InsertBuffer(frame_buffer, packet_list_it); - UpdateCompleteSession(); - - return static_cast(returnLength); -} - -void VCMSessionInfo::InformOfEmptyPacket(uint16_t seq_num) { - // Empty packets may be FEC or filler packets. They are sequential and - // follow the data packets, therefore, we should only keep track of the high - // and low sequence numbers and may assume that the packets in between are - // empty packets belonging to the same frame (timestamp). - if (empty_seq_num_high_ == -1) - empty_seq_num_high_ = seq_num; - else - empty_seq_num_high_ = LatestSequenceNumber(seq_num, empty_seq_num_high_); - if (empty_seq_num_low_ == -1 || - IsNewerSequenceNumber(empty_seq_num_low_, seq_num)) - empty_seq_num_low_ = seq_num; -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/session_info.h b/modules/video_coding/deprecated/session_info.h deleted file mode 100644 index 1533f96b27c..00000000000 --- a/modules/video_coding/deprecated/session_info.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_DEPRECATED_SESSION_INFO_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_SESSION_INFO_H_ - -#include -#include - -#include -#include - -#include "api/video/video_frame_type.h" -#include "modules/video_coding/codecs/h264/include/h264_globals.h" -#include "modules/video_coding/codecs/vp9/include/vp9_globals.h" -#include "modules/video_coding/deprecated/packet.h" - -namespace webrtc { -// Used to pass data from jitter buffer to session info. -// This data is then used in determining whether a frame is decodable. -struct FrameData { - int64_t rtt_ms; - float rolling_average_packets_per_frame; -}; - -class VCMSessionInfo { - public: - VCMSessionInfo(); - ~VCMSessionInfo(); - - void UpdateDataPointers(const uint8_t* old_base_ptr, - const uint8_t* new_base_ptr); - void Reset(); - int InsertPacket(const VCMPacket& packet, - uint8_t* frame_buffer, - const FrameData& frame_data); - bool complete() const; - - // Makes the frame decodable. I.e., only contain decodable NALUs. All - // non-decodable NALUs will be deleted and packets will be moved to in - // memory to remove any empty space. - // Returns the number of bytes deleted from the session. - size_t MakeDecodable(); - - size_t SessionLength() const; - int NumPackets() const; - bool HaveFirstPacket() const; - bool HaveLastPacket() const; - VideoFrameType FrameType() const { return frame_type_; } - int LowSequenceNumber() const; - - // Returns highest sequence number, media or empty. - int HighSequenceNumber() const; - int PictureId() const; - int TemporalId() const; - bool LayerSync() const; - int Tl0PicId() const; - - std::vector GetNaluInfos() const; - - void SetGofInfo(const GofInfoVP9& gof_info, size_t idx); - - private: - enum { kMaxVP8Partitions = 9 }; - - typedef std::list PacketList; - typedef PacketList::iterator PacketIterator; - typedef PacketList::const_iterator PacketIteratorConst; - typedef PacketList::reverse_iterator ReversePacketIterator; - - void InformOfEmptyPacket(uint16_t seq_num); - - // Finds the packet of the beginning of the next VP8 partition. If - // none is found the returned iterator points to `packets_.end()`. - // `it` is expected to point to the last packet of the previous partition, - // or to the first packet of the frame. `packets_skipped` is incremented - // for each packet found which doesn't have the beginning bit set. - PacketIterator FindNextPartitionBeginning(PacketIterator it) const; - - // Returns an iterator pointing to the last packet of the partition pointed to - // by `it`. - PacketIterator FindPartitionEnd(PacketIterator it) const; - static bool InSequence(const PacketIterator& it, - const PacketIterator& prev_it); - size_t InsertBuffer(uint8_t* frame_buffer, PacketIterator packetIterator); - size_t Insert(const uint8_t* buffer, - size_t length, - bool insert_start_code, - uint8_t* frame_buffer); - void ShiftSubsequentPackets(PacketIterator it, int steps_to_shift); - PacketIterator FindNaluEnd(PacketIterator packet_iter) const; - // Deletes the data of all packets between `start` and `end`, inclusively. - // Note that this function doesn't delete the actual packets. - size_t DeletePacketData(PacketIterator start, PacketIterator end); - void UpdateCompleteSession(); - - bool complete_; - VideoFrameType frame_type_; - // Packets in this frame. - PacketList packets_; - int empty_seq_num_low_; - int empty_seq_num_high_; - - // The following two variables correspond to the first and last media packets - // in a session defined by the first packet flag and the marker bit. - // They are not necessarily equal to the front and back packets, as packets - // may enter out of order. - // TODO(mikhal): Refactor the list to use a map. - int first_packet_seq_num_; - int last_packet_seq_num_; -}; - -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_SESSION_INFO_H_ diff --git a/modules/video_coding/deprecated/session_info_unittest.cc b/modules/video_coding/deprecated/session_info_unittest.cc deleted file mode 100644 index b965fb186b2..00000000000 --- a/modules/video_coding/deprecated/session_info_unittest.cc +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/session_info.h" - -#include -#include - -#include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" -#include "modules/video_coding/deprecated/packet.h" -#include "test/gtest.h" - -namespace webrtc { - -class TestSessionInfo : public ::testing::Test { - protected: - void SetUp() override { - memset(packet_buffer_, 0, sizeof(packet_buffer_)); - memset(frame_buffer_, 0, sizeof(frame_buffer_)); - session_.Reset(); - packet_.video_header.frame_type = VideoFrameType::kVideoFrameDelta; - packet_.sizeBytes = packet_buffer_size(); - packet_.dataPtr = packet_buffer_; - packet_.seqNum = 0; - packet_.timestamp = 0; - frame_data.rtt_ms = 0; - frame_data.rolling_average_packets_per_frame = -1; - } - - void FillPacket(uint8_t start_value) { - for (size_t i = 0; i < packet_buffer_size(); ++i) - packet_buffer_[i] = start_value + i; - } - - void VerifyPacket(uint8_t* start_ptr, uint8_t start_value) { - for (size_t j = 0; j < packet_buffer_size(); ++j) { - ASSERT_EQ(start_value + j, start_ptr[j]); - } - } - - size_t packet_buffer_size() const { - return sizeof(packet_buffer_) / sizeof(packet_buffer_[0]); - } - size_t frame_buffer_size() const { - return sizeof(frame_buffer_) / sizeof(frame_buffer_[0]); - } - - enum { kPacketBufferSize = 10 }; - - uint8_t packet_buffer_[kPacketBufferSize]; - uint8_t frame_buffer_[10 * kPacketBufferSize]; - - VCMSessionInfo session_; - VCMPacket packet_; - FrameData frame_data; -}; - -class TestNalUnits : public TestSessionInfo { - protected: - void SetUp() override { - TestSessionInfo::SetUp(); - packet_.video_header.codec = kVideoCodecVP8; - } - - bool VerifyNalu(int offset, int packets_expected, int start_value) { - EXPECT_GE(session_.SessionLength(), - packets_expected * packet_buffer_size()); - for (int i = 0; i < packets_expected; ++i) { - int packet_index = (offset + i) * packet_buffer_size(); - VerifyPacket(frame_buffer_ + packet_index, start_value + i); - } - return true; - } -}; - -class TestNackList : public TestSessionInfo { - protected: - static const size_t kMaxSeqNumListLength = 30; - - void SetUp() override { - TestSessionInfo::SetUp(); - seq_num_list_length_ = 0; - memset(seq_num_list_, 0, sizeof(seq_num_list_)); - } - - void BuildSeqNumList(uint16_t low, uint16_t high) { - size_t i = 0; - while (low != high + 1) { - EXPECT_LT(i, kMaxSeqNumListLength); - if (i >= kMaxSeqNumListLength) { - seq_num_list_length_ = kMaxSeqNumListLength; - return; - } - seq_num_list_[i] = low; - low++; - i++; - } - seq_num_list_length_ = i; - } - - void VerifyAll(int value) { - for (int i = 0; i < seq_num_list_length_; ++i) - EXPECT_EQ(seq_num_list_[i], value); - } - - int seq_num_list_[kMaxSeqNumListLength]; - int seq_num_list_length_; -}; - -TEST_F(TestSessionInfo, TestSimpleAPIs) { - packet_.video_header.is_first_packet_in_frame = true; - packet_.seqNum = 0xFFFE; - packet_.sizeBytes = packet_buffer_size(); - packet_.video_header.frame_type = VideoFrameType::kVideoFrameKey; - FillPacket(0); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - EXPECT_FALSE(session_.HaveLastPacket()); - EXPECT_EQ(VideoFrameType::kVideoFrameKey, session_.FrameType()); - - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = true; - packet_.seqNum += 1; - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - EXPECT_TRUE(session_.HaveLastPacket()); - EXPECT_EQ(packet_.seqNum, session_.HighSequenceNumber()); - EXPECT_EQ(0xFFFE, session_.LowSequenceNumber()); - - // Insert empty packet which will be the new high sequence number. - // To make things more difficult we will make sure to have a wrap here. - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = true; - packet_.seqNum = 2; - packet_.sizeBytes = 0; - packet_.video_header.frame_type = VideoFrameType::kEmptyFrame; - EXPECT_EQ(0, session_.InsertPacket(packet_, frame_buffer_, frame_data)); - EXPECT_EQ(packet_.seqNum, session_.HighSequenceNumber()); -} - -TEST_F(TestSessionInfo, NormalOperation) { - packet_.seqNum = 0xFFFF; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = false; - FillPacket(0); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.video_header.is_first_packet_in_frame = false; - for (int i = 1; i < 9; ++i) { - packet_.seqNum += 1; - FillPacket(i); - ASSERT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - } - - packet_.seqNum += 1; - packet_.markerBit = true; - FillPacket(9); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - EXPECT_EQ(10 * packet_buffer_size(), session_.SessionLength()); - for (int i = 0; i < 10; ++i) { - SCOPED_TRACE("Calling VerifyPacket"); - VerifyPacket(frame_buffer_ + i * packet_buffer_size(), i); - } -} - -TEST_F(TestSessionInfo, OutOfBoundsPackets1PacketFrame) { - packet_.seqNum = 0x0001; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.seqNum = 0x0004; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); - packet_.seqNum = 0x0000; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); -} - -TEST_F(TestSessionInfo, SetMarkerBitOnce) { - packet_.seqNum = 0x0005; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - ++packet_.seqNum; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); -} - -TEST_F(TestSessionInfo, OutOfBoundsPacketsBase) { - // Allow packets in the range 5-6. - packet_.seqNum = 0x0005; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - // Insert an older packet with a first packet set. - packet_.seqNum = 0x0004; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); - packet_.seqNum = 0x0006; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - packet_.seqNum = 0x0008; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); -} - -TEST_F(TestSessionInfo, OutOfBoundsPacketsWrap) { - packet_.seqNum = 0xFFFE; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.seqNum = 0x0004; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - packet_.seqNum = 0x0002; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - ASSERT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - packet_.seqNum = 0xFFF0; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); - packet_.seqNum = 0x0006; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); -} - -TEST_F(TestSessionInfo, OutOfBoundsOutOfOrder) { - // Insert out of bound regular packets, and then the first and last packet. - // Verify that correct bounds are maintained. - packet_.seqNum = 0x0003; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - // Insert an older packet with a first packet set. - packet_.seqNum = 0x0005; - packet_.video_header.is_first_packet_in_frame = true; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - packet_.seqNum = 0x0004; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); - packet_.seqNum = 0x0010; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - packet_.seqNum = 0x0008; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = true; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.seqNum = 0x0009; - packet_.video_header.is_first_packet_in_frame = false; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(-3, session_.InsertPacket(packet_, frame_buffer_, frame_data)); -} - -TEST_F(TestNalUnits, OnlyReceivedEmptyPacket) { - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluComplete; - packet_.video_header.frame_type = VideoFrameType::kEmptyFrame; - packet_.sizeBytes = 0; - packet_.seqNum = 0; - packet_.markerBit = false; - EXPECT_EQ(0, session_.InsertPacket(packet_, frame_buffer_, frame_data)); - - EXPECT_EQ(0U, session_.MakeDecodable()); - EXPECT_EQ(0U, session_.SessionLength()); -} - -TEST_F(TestNalUnits, OneIsolatedNaluLoss) { - packet_.video_header.is_first_packet_in_frame = true; - packet_.completeNALU = kNaluComplete; - packet_.seqNum = 0; - packet_.markerBit = false; - FillPacket(0); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluComplete; - packet_.seqNum += 2; - packet_.markerBit = true; - FillPacket(2); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - EXPECT_EQ(0U, session_.MakeDecodable()); - EXPECT_EQ(2 * packet_buffer_size(), session_.SessionLength()); - SCOPED_TRACE("Calling VerifyNalu"); - EXPECT_TRUE(VerifyNalu(0, 1, 0)); - SCOPED_TRACE("Calling VerifyNalu"); - EXPECT_TRUE(VerifyNalu(1, 1, 2)); -} - -TEST_F(TestNalUnits, LossInMiddleOfNalu) { - packet_.video_header.is_first_packet_in_frame = true; - packet_.completeNALU = kNaluComplete; - packet_.seqNum = 0; - packet_.markerBit = false; - FillPacket(0); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluEnd; - packet_.seqNum += 2; - packet_.markerBit = true; - FillPacket(2); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - EXPECT_EQ(packet_buffer_size(), session_.MakeDecodable()); - EXPECT_EQ(packet_buffer_size(), session_.SessionLength()); - SCOPED_TRACE("Calling VerifyNalu"); - EXPECT_TRUE(VerifyNalu(0, 1, 0)); -} - -TEST_F(TestNalUnits, StartAndEndOfLastNalUnitLost) { - packet_.video_header.is_first_packet_in_frame = true; - packet_.completeNALU = kNaluComplete; - packet_.seqNum = 0; - packet_.markerBit = false; - FillPacket(0); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluIncomplete; - packet_.seqNum += 2; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - EXPECT_EQ(packet_buffer_size(), session_.MakeDecodable()); - EXPECT_EQ(packet_buffer_size(), session_.SessionLength()); - SCOPED_TRACE("Calling VerifyNalu"); - EXPECT_TRUE(VerifyNalu(0, 1, 0)); -} - -TEST_F(TestNalUnits, ReorderWrapNoLoss) { - packet_.seqNum = 0xFFFF; - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluIncomplete; - packet_.seqNum += 1; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.video_header.is_first_packet_in_frame = true; - packet_.completeNALU = kNaluComplete; - packet_.seqNum -= 1; - packet_.markerBit = false; - FillPacket(0); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluEnd; - packet_.seqNum += 2; - packet_.markerBit = true; - FillPacket(2); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - EXPECT_EQ(0U, session_.MakeDecodable()); - EXPECT_EQ(3 * packet_buffer_size(), session_.SessionLength()); - SCOPED_TRACE("Calling VerifyNalu"); - EXPECT_TRUE(VerifyNalu(0, 1, 0)); -} - -TEST_F(TestNalUnits, WrapLosses) { - packet_.seqNum = 0xFFFF; - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluIncomplete; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluEnd; - packet_.seqNum += 2; - packet_.markerBit = true; - FillPacket(2); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - EXPECT_EQ(2 * packet_buffer_size(), session_.MakeDecodable()); - EXPECT_EQ(0U, session_.SessionLength()); -} - -TEST_F(TestNalUnits, ReorderWrapLosses) { - packet_.seqNum = 0xFFFF; - - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluEnd; - packet_.seqNum += 2; - packet_.markerBit = true; - FillPacket(2); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - packet_.seqNum -= 2; - packet_.video_header.is_first_packet_in_frame = false; - packet_.completeNALU = kNaluIncomplete; - packet_.markerBit = false; - FillPacket(1); - EXPECT_EQ(packet_buffer_size(), static_cast(session_.InsertPacket( - packet_, frame_buffer_, frame_data))); - - EXPECT_EQ(2 * packet_buffer_size(), session_.MakeDecodable()); - EXPECT_EQ(0U, session_.SessionLength()); -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/stream_generator.cc b/modules/video_coding/deprecated/stream_generator.cc deleted file mode 100644 index d9e5cceee2d..00000000000 --- a/modules/video_coding/deprecated/stream_generator.cc +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/deprecated/stream_generator.h" - -#include -#include -#include - -#include "api/video/video_frame_type.h" -#include "modules/video_coding/deprecated/packet.h" -#include "rtc_base/checks.h" - -namespace webrtc { - -StreamGenerator::StreamGenerator(uint16_t start_seq_num, int64_t current_time) - : packets_(), sequence_number_(start_seq_num), start_time_(current_time) {} - -void StreamGenerator::Init(uint16_t start_seq_num, int64_t current_time) { - packets_.clear(); - sequence_number_ = start_seq_num; - start_time_ = current_time; - memset(packet_buffer_, 0, sizeof(packet_buffer_)); -} - -void StreamGenerator::GenerateFrame(VideoFrameType type, - int num_media_packets, - int num_empty_packets, - int64_t time_ms) { - uint32_t timestamp = 90 * (time_ms - start_time_); - for (int i = 0; i < num_media_packets; ++i) { - const int packet_size = - (kFrameSize + num_media_packets / 2) / num_media_packets; - bool marker_bit = (i == num_media_packets - 1); - packets_.push_back(GeneratePacket(sequence_number_, timestamp, packet_size, - (i == 0), marker_bit, type)); - ++sequence_number_; - } - for (int i = 0; i < num_empty_packets; ++i) { - packets_.push_back(GeneratePacket(sequence_number_, timestamp, 0, false, - false, VideoFrameType::kEmptyFrame)); - ++sequence_number_; - } -} - -VCMPacket StreamGenerator::GeneratePacket(uint16_t sequence_number, - uint32_t timestamp, - unsigned int size, - bool first_packet, - bool marker_bit, - VideoFrameType type) { - RTC_CHECK_LT(size, kMaxPacketSize); - VCMPacket packet; - packet.seqNum = sequence_number; - packet.timestamp = timestamp; - packet.video_header.frame_type = type; - packet.video_header.is_first_packet_in_frame = first_packet; - packet.markerBit = marker_bit; - packet.sizeBytes = size; - packet.dataPtr = packet_buffer_; - if (packet.is_first_packet_in_frame()) - packet.completeNALU = kNaluStart; - else if (packet.markerBit) - packet.completeNALU = kNaluEnd; - else - packet.completeNALU = kNaluIncomplete; - return packet; -} - -bool StreamGenerator::PopPacket(VCMPacket* packet, int index) { - std::list::iterator it = GetPacketIterator(index); - if (it == packets_.end()) - return false; - if (packet) - *packet = (*it); - packets_.erase(it); - return true; -} - -bool StreamGenerator::GetPacket(VCMPacket* packet, int index) { - std::list::iterator it = GetPacketIterator(index); - if (it == packets_.end()) - return false; - if (packet) - *packet = (*it); - return true; -} - -bool StreamGenerator::NextPacket(VCMPacket* packet) { - if (packets_.empty()) - return false; - if (packet != nullptr) - *packet = packets_.front(); - packets_.pop_front(); - return true; -} - -void StreamGenerator::DropLastPacket() { - packets_.pop_back(); -} - -uint16_t StreamGenerator::NextSequenceNumber() const { - if (packets_.empty()) - return sequence_number_; - return packets_.front().seqNum; -} - -int StreamGenerator::PacketsRemaining() const { - return packets_.size(); -} - -std::list::iterator StreamGenerator::GetPacketIterator(int index) { - std::list::iterator it = packets_.begin(); - for (int i = 0; i < index; ++i) { - ++it; - if (it == packets_.end()) - break; - } - return it; -} - -} // namespace webrtc diff --git a/modules/video_coding/deprecated/stream_generator.h b/modules/video_coding/deprecated/stream_generator.h deleted file mode 100644 index e6a90aa7bea..00000000000 --- a/modules/video_coding/deprecated/stream_generator.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#ifndef MODULES_VIDEO_CODING_DEPRECATED_STREAM_GENERATOR_H_ -#define MODULES_VIDEO_CODING_DEPRECATED_STREAM_GENERATOR_H_ - -#include - -#include - -#include "api/video/video_frame_type.h" -#include "modules/video_coding/deprecated/packet.h" - -namespace webrtc { - -const unsigned int kDefaultBitrateKbps = 1000; -const unsigned int kDefaultFrameRate = 25; -const unsigned int kMaxPacketSize = 1500; -const unsigned int kFrameSize = - (kDefaultBitrateKbps + kDefaultFrameRate * 4) / (kDefaultFrameRate * 8); -const int kDefaultFramePeriodMs = 1000 / kDefaultFrameRate; - -class StreamGenerator { - public: - StreamGenerator(uint16_t start_seq_num, int64_t current_time); - - StreamGenerator(const StreamGenerator&) = delete; - StreamGenerator& operator=(const StreamGenerator&) = delete; - - void Init(uint16_t start_seq_num, int64_t current_time); - - // `time_ms` denotes the timestamp you want to put on the frame, and the unit - // is millisecond. GenerateFrame will translate `time_ms` into a 90kHz - // timestamp and put it on the frame. - void GenerateFrame(VideoFrameType type, - int num_media_packets, - int num_empty_packets, - int64_t time_ms); - - bool PopPacket(VCMPacket* packet, int index); - void DropLastPacket(); - - bool GetPacket(VCMPacket* packet, int index); - - bool NextPacket(VCMPacket* packet); - - uint16_t NextSequenceNumber() const; - - int PacketsRemaining() const; - - private: - VCMPacket GeneratePacket(uint16_t sequence_number, - uint32_t timestamp, - unsigned int size, - bool first_packet, - bool marker_bit, - VideoFrameType type); - - std::list::iterator GetPacketIterator(int index); - - std::list packets_; - uint16_t sequence_number_; - int64_t start_time_; - uint8_t packet_buffer_[kMaxPacketSize]; -}; - -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_DEPRECATED_STREAM_GENERATOR_H_ diff --git a/modules/video_coding/encoded_frame.cc b/modules/video_coding/encoded_frame.cc index c69e67a4300..ce327b43af6 100644 --- a/modules/video_coding/encoded_frame.cc +++ b/modules/video_coding/encoded_frame.cc @@ -47,7 +47,7 @@ void VCMEncodedFrame::Reset() { SetSpatialIndex(std::nullopt); _renderTimeMs = -1; _payloadType = 0; - _frameType = VideoFrameType::kVideoFrameDelta; + set_frame_type(VideoFrameType::kVideoFrameDelta); _encodedWidth = 0; _encodedHeight = 0; _missingFrame = false; diff --git a/modules/video_coding/frame_dependencies_calculator.cc b/modules/video_coding/frame_dependencies_calculator.cc index ca046f24bc4..2d28a415a49 100644 --- a/modules/video_coding/frame_dependencies_calculator.cc +++ b/modules/video_coding/frame_dependencies_calculator.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include "absl/algorithm/container.h" #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "common_video/generic_frame_descriptor/generic_frame_info.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -26,7 +26,7 @@ namespace webrtc { absl::InlinedVector FrameDependenciesCalculator::FromBuffersUsage( int64_t frame_id, - ArrayView buffers_usage) { + std::span buffers_usage) { absl::InlinedVector dependencies; RTC_DCHECK_GT(buffers_usage.size(), 0); for (const CodecBufferUsage& buffer_usage : buffers_usage) { diff --git a/modules/video_coding/frame_dependencies_calculator.h b/modules/video_coding/frame_dependencies_calculator.h index 3a354c563a0..d9da4368921 100644 --- a/modules/video_coding/frame_dependencies_calculator.h +++ b/modules/video_coding/frame_dependencies_calculator.h @@ -14,9 +14,9 @@ #include #include +#include #include "absl/container/inlined_vector.h" -#include "api/array_view.h" #include "common_video/generic_frame_descriptor/generic_frame_info.h" namespace webrtc { @@ -32,7 +32,7 @@ class FrameDependenciesCalculator { // Calculates frame dependencies based on previous encoder buffer usage. absl::InlinedVector FromBuffersUsage( int64_t frame_id, - ArrayView buffers_usage); + std::span buffers_usage); private: struct BufferUsage { diff --git a/modules/video_coding/generic_decoder.cc b/modules/video_coding/generic_decoder.cc index 790d53a9087..dac95bfaa76 100644 --- a/modules/video_coding/generic_decoder.cc +++ b/modules/video_coding/generic_decoder.cc @@ -28,7 +28,6 @@ #include "api/video/encoded_image.h" #include "api/video/video_content_type.h" #include "api/video/video_frame.h" -#include "api/video/video_frame_type.h" #include "api/video/video_timing.h" #include "api/video_codecs/video_decoder.h" #include "common_video/include/corruption_score_calculator.h" @@ -237,15 +236,15 @@ void VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage, RTC_HISTOGRAM_COUNTS_1000( "WebRTC.Video.GenericDecoder.DecodeDelay", timing_frame_info.decode_finish_ms - timing_frame_info.decode_start_ms); - timing_->SetTimingFrameInfo(timing_frame_info); - decodedImage.set_timestamp_us( frame_info->render_time ? frame_info->render_time->us() : -1); + decodedImage.set_content_type(frame_info->content_type); receive_callback_->OnFrameToRender({.video_frame = decodedImage, .qp = qp, .decode_time = decode_time, .content_type = frame_info->content_type, - .frame_type = frame_info->frame_type}); + .frame_type = frame_info->frame_type, + .timing_frame_info = timing_frame_info}); if (corruption_score_calculator_ && frame_info->frame_instrumentation_data.has_value()) { @@ -348,13 +347,13 @@ int32_t VCMGenericDecoder::Decode( // Set correctly only for key frames. Thus, use latest key frame // content type. If the corresponding key frame was lost, decode will fail // and content type will be ignored. - if (frame.FrameType() == VideoFrameType::kVideoFrameKey) { + if (frame.IsKey()) { frame_info.content_type = frame.contentType(); last_keyframe_content_type_ = frame.contentType(); } else { frame_info.content_type = last_keyframe_content_type_; } - frame_info.frame_type = frame.FrameType(); + frame_info.frame_type = frame.frame_type(); callback_->Map(std::move(frame_info)); int32_t ret = decoder_->Decode(frame, render_time_ms); diff --git a/modules/video_coding/generic_decoder_unittest.cc b/modules/video_coding/generic_decoder_unittest.cc index 081c9ba4fa3..23ecf2664c7 100644 --- a/modules/video_coding/generic_decoder_unittest.cc +++ b/modules/video_coding/generic_decoder_unittest.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "api/field_trials.h" #include "api/rtp_packet_infos.h" #include "api/scoped_refptr.h" @@ -75,7 +75,7 @@ class ReceiveCallback : public VCMReceiveCallback { return ret; } - ArrayView GetAllFrames() const { return frames_; } + std::span GetAllFrames() const { return frames_; } void OnDroppedFrames(uint32_t frames_dropped) override { frames_dropped_ += frames_dropped; @@ -273,6 +273,46 @@ TEST_F(GenericDecoderTest, UsesMappedColorSpaceIfSet) { EXPECT_EQ(decoded_frame->color_space(), kMappedColorSpace); } +TEST_F(GenericDecoderTest, SetsScreenshareContentTypeIfSetInFrameInfo) { + constexpr uint32_t kRtpTimestamp = 1; + FrameInfo frame_info; + frame_info.rtp_timestamp = kRtpTimestamp; + frame_info.decode_start = Timestamp::Zero(); + frame_info.content_type = VideoContentType::SCREENSHARE; + frame_info.frame_type = VideoFrameType::kVideoFrameKey; + + VideoFrame video_frame = VideoFrame::Builder() + .set_video_frame_buffer(I420Buffer::Create(5, 5)) + .set_rtp_timestamp(kRtpTimestamp) + .build(); + vcm_callback_.Map(std::move(frame_info)); + vcm_callback_.Decoded(video_frame); + + std::optional decoded_frame = user_callback_.PopLastFrame(); + ASSERT_TRUE(decoded_frame.has_value()); + EXPECT_EQ(decoded_frame->content_type(), VideoContentType::SCREENSHARE); +} + +TEST_F(GenericDecoderTest, SetsUnspecifiedContentTypeIfSetInFrameInfo) { + constexpr uint32_t kRtpTimestamp = 1; + FrameInfo frame_info; + frame_info.rtp_timestamp = kRtpTimestamp; + frame_info.decode_start = Timestamp::Zero(); + frame_info.content_type = VideoContentType::UNSPECIFIED; + frame_info.frame_type = VideoFrameType::kVideoFrameKey; + + VideoFrame video_frame = VideoFrame::Builder() + .set_video_frame_buffer(I420Buffer::Create(5, 5)) + .set_rtp_timestamp(kRtpTimestamp) + .build(); + vcm_callback_.Map(std::move(frame_info)); + vcm_callback_.Decoded(video_frame); + + std::optional decoded_frame = user_callback_.PopLastFrame(); + ASSERT_TRUE(decoded_frame.has_value()); + EXPECT_EQ(decoded_frame->content_type(), VideoContentType::UNSPECIFIED); +} + TEST_F(GenericDecoderTest, UsesDecoderColorSpaceIfNoneMapped) { constexpr uint32_t kRtpTimestamp = 1; const ColorSpace kDecoderColorSpace( diff --git a/modules/video_coding/h264_sps_pps_tracker.cc b/modules/video_coding/h264_sps_pps_tracker.cc index 94e6411dede..ebd80357256 100644 --- a/modules/video_coding/h264_sps_pps_tracker.cc +++ b/modules/video_coding/h264_sps_pps_tracker.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "common_video/h264/h264_common.h" #include "common_video/h264/pps_parser.h" @@ -35,7 +35,7 @@ const uint8_t start_code_h264[] = {0, 0, 0, 1}; } // namespace H264SpsPpsTracker::FixedBitstream H264SpsPpsTracker::CopyAndFixBitstream( - ArrayView bitstream, + std::span bitstream, RTPVideoHeader* video_header) { RTC_DCHECK(video_header); RTC_DCHECK(video_header->codec == kVideoCodecH264); @@ -115,7 +115,7 @@ H264SpsPpsTracker::FixedBitstream H264SpsPpsTracker::CopyAndFixBitstream( } if (h264_header.packetization_type == kH264StapA) { - ByteBufferReader nalu(bitstream.subview(1)); + ByteBufferReader nalu(bitstream.subspan(1)); while (nalu.Length() > 0) { required_size += sizeof(start_code_h264); @@ -159,7 +159,7 @@ H264SpsPpsTracker::FixedBitstream H264SpsPpsTracker::CopyAndFixBitstream( // Copy the rest of the bitstream and insert start codes. if (h264_header.packetization_type == kH264StapA) { - ByteBufferReader nalu(bitstream.subview(1)); + ByteBufferReader nalu(bitstream.subspan(1)); while (nalu.Length() > 0) { fixed.bitstream.AppendData(start_code_h264); @@ -206,9 +206,9 @@ void H264SpsPpsTracker::InsertSpsPpsNalus(const std::vector& sps, return; } std::optional parsed_sps = SpsParser::ParseSps( - ArrayView(sps).subview(kNaluHeaderOffset)); + std::span(sps).subspan(kNaluHeaderOffset)); std::optional parsed_pps = PpsParser::ParsePps( - ArrayView(pps).subview(kNaluHeaderOffset)); + std::span(pps).subspan(kNaluHeaderOffset)); if (!parsed_sps) { RTC_LOG(LS_WARNING) << "Failed to parse SPS."; diff --git a/modules/video_coding/h264_sps_pps_tracker.h b/modules/video_coding/h264_sps_pps_tracker.h index 132aaa0568e..b8c04c09ab9 100644 --- a/modules/video_coding/h264_sps_pps_tracker.h +++ b/modules/video_coding/h264_sps_pps_tracker.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" #include "rtc_base/buffer.h" #include "rtc_base/copy_on_write_buffer.h" @@ -38,7 +38,7 @@ class H264SpsPpsTracker { ~H264SpsPpsTracker() = default; // Returns fixed bitstream and modifies `video_header`. - FixedBitstream CopyAndFixBitstream(ArrayView bitstream, + FixedBitstream CopyAndFixBitstream(std::span bitstream, RTPVideoHeader* video_header); void InsertSpsPpsNalus(const std::vector& sps, diff --git a/modules/video_coding/h264_sps_pps_tracker_unittest.cc b/modules/video_coding/h264_sps_pps_tracker_unittest.cc index 079049d59e1..adb23075269 100644 --- a/modules/video_coding/h264_sps_pps_tracker_unittest.cc +++ b/modules/video_coding/h264_sps_pps_tracker_unittest.cc @@ -11,9 +11,9 @@ #include "modules/video_coding/h264_sps_pps_tracker.h" #include +#include #include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "common_video/h264/h264_common.h" #include "modules/rtp_rtcp/source/rtp_video_header.h" @@ -30,7 +30,7 @@ using ::testing::SizeIs; const uint8_t start_code[] = {0, 0, 0, 1}; -ArrayView Bitstream( +std::span Bitstream( const H264SpsPpsTracker::FixedBitstream& fixed) { return fixed.bitstream; } diff --git a/modules/video_coding/h26x_packet_buffer.cc b/modules/video_coding/h26x_packet_buffer.cc index dc141e5a83e..cee203a757d 100644 --- a/modules/video_coding/h26x_packet_buffer.cc +++ b/modules/video_coding/h26x_packet_buffer.cc @@ -17,12 +17,12 @@ #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "common_video/h264/h264_common.h" @@ -77,7 +77,7 @@ bool HasSps(const H26xPacketBuffer::Packet& packet) { }); } -int64_t* GetContinuousSequence(ArrayView last_continuous, +int64_t* GetContinuousSequence(std::span last_continuous, int64_t unwrapped_seq_num) { for (int64_t& last : last_continuous) { if (unwrapped_seq_num - 1 == last) { @@ -361,9 +361,9 @@ void H26xPacketBuffer::InsertSpsPpsNalus(const std::vector& sps, return; } std::optional parsed_sps = SpsParser::ParseSps( - ArrayView(sps).subview(kNaluHeaderOffset)); + std::span(sps).subspan(kNaluHeaderOffset)); std::optional parsed_pps = PpsParser::ParsePps( - ArrayView(pps).subview(kNaluHeaderOffset)); + std::span(pps).subspan(kNaluHeaderOffset)); if (!parsed_sps) { RTC_LOG(LS_WARNING) << "Failed to parse SPS."; diff --git a/modules/video_coding/h26x_packet_buffer_unittest.cc b/modules/video_coding/h26x_packet_buffer_unittest.cc index f786776d41c..8a50b786d2a 100644 --- a/modules/video_coding/h26x_packet_buffer_unittest.cc +++ b/modules/video_coding/h26x_packet_buffer_unittest.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/video/render_resolution.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" @@ -378,7 +378,7 @@ H265Packet& H265Packet::SeqNum(int64_t rtp_seq_num) { } #endif -ArrayView PacketPayload( +std::span PacketPayload( const std::unique_ptr& packet) { return packet->video_payload; } diff --git a/modules/video_coding/include/video_coding.h b/modules/video_coding/include/video_coding.h deleted file mode 100644 index 76fe2c3ad81..00000000000 --- a/modules/video_coding/include/video_coding.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODING_H_ -#define MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODING_H_ - -#include -#include -#include - -#include "api/environment/environment.h" -#include "api/rtp_headers.h" -#include "api/video_codecs/video_decoder.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "modules/video_coding/include/video_coding_defines.h" - -namespace webrtc { - -class VideoCodingModule { - public: - [[deprecated]] static std::unique_ptr CreateDeprecated( - const Environment& env); - - virtual ~VideoCodingModule() = default; - - /* - * Receiver - */ - - // Register possible receive codecs, can be called multiple times for - // different codecs. - // The module will automatically switch between registered codecs depending on - // the - // payload type of incoming frames. The actual decoder will be created when - // needed. - // - // Input: - // - payload_type : RTP payload type - // - settings : Settings for the decoder to be registered. - // - virtual void RegisterReceiveCodec(uint8_t payload_type, - const VideoDecoder::Settings& settings) = 0; - - // Register an external decoder object. - // - // Input: - // - externalDecoder : Decoder object to be used for decoding frames. - // - payloadType : The payload type which this decoder is bound to. - virtual void RegisterExternalDecoder(VideoDecoder* externalDecoder, - uint8_t payloadType) = 0; - - // Register a receive callback. Will be called whenever there is a new frame - // ready - // for rendering. - // - // Input: - // - receiveCallback : The callback object to be used by the - // module when a - // frame is ready for rendering. - // De-register with a NULL pointer. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual int32_t RegisterReceiveCallback( - VCMReceiveCallback* receiveCallback) = 0; - - // Register a frame type request callback. This callback will be called when - // the - // module needs to request specific frame types from the send side. - // - // Input: - // - frameTypeCallback : The callback object to be used by the - // module when - // requesting a specific type of frame from - // the send side. - // De-register with a NULL pointer. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual int32_t RegisterFrameTypeCallback( - VCMFrameTypeCallback* frameTypeCallback) = 0; - - // Registers a callback which is called whenever the receive side of the VCM - // encounters holes in the packet sequence and needs packets to be - // retransmitted. - // - // Input: - // - callback : The callback to be registered in the VCM. - // - // Return value : VCM_OK, on success. - // <0, on error. - virtual int32_t RegisterPacketRequestCallback( - VCMPacketRequestCallback* callback) = 0; - - // Waits for the next frame in the jitter buffer to become complete - // (waits no longer than maxWaitTimeMs), then passes it to the decoder for - // decoding. - // Should be called as often as possible to get the most out of the decoder. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual int32_t Decode(uint16_t maxWaitTimeMs = 200) = 0; - - // Insert a parsed packet into the receiver side of the module. Will be placed - // in the - // jitter buffer waiting for the frame to become complete. Returns as soon as - // the packet - // has been placed in the jitter buffer. - // - // Input: - // - incomingPayload : Payload of the packet. - // - payloadLength : Length of the payload. - // - rtp_header : The parsed RTP header. - // - video_header : The relevant extensions and payload header. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual int32_t IncomingPacket(const uint8_t* incomingPayload, - size_t payloadLength, - const RTPHeader& rtp_header, - const RTPVideoHeader& video_header) = 0; - - // Sets the maximum number of sequence numbers that we are allowed to NACK - // and the oldest sequence number that we will consider to NACK. If a - // sequence number older than `max_packet_age_to_nack` is missing - // a key frame will be requested. A key frame will also be requested if the - // time of incomplete or non-continuous frames in the jitter buffer is above - // `max_incomplete_time_ms`. - virtual void SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms) = 0; - - // Runs delayed tasks. Expected to be called periodically. - virtual void Process() = 0; -}; - -} // namespace webrtc - -#endif // MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODING_H_ diff --git a/modules/video_coding/include/video_coding_defines.h b/modules/video_coding/include/video_coding_defines.h index 88c16ca7334..f2e852f181e 100644 --- a/modules/video_coding/include/video_coding_defines.h +++ b/modules/video_coding/include/video_coding_defines.h @@ -20,6 +20,7 @@ #include "api/video/video_content_type.h" #include "api/video/video_frame.h" #include "api/video/video_frame_type.h" +#include "api/video/video_timing.h" #include "api/video_codecs/video_decoder.h" namespace webrtc { @@ -58,6 +59,7 @@ class VCMReceiveCallback { TimeDelta decode_time; VideoContentType content_type; VideoFrameType frame_type; + TimingFrameInfo timing_frame_info; }; virtual int32_t OnFrameToRender(const FrameToRender& arguments) = 0; diff --git a/modules/video_coding/loss_notification_controller.cc b/modules/video_coding/loss_notification_controller.cc index 97f86dc57a9..4f4a0087ff0 100644 --- a/modules/video_coding/loss_notification_controller.cc +++ b/modules/video_coding/loss_notification_controller.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/sequence_checker.h" #include "modules/include/module_common_types.h" #include "rtc_base/checks.h" @@ -113,7 +113,7 @@ void LossNotificationController::OnAssembledFrame( uint16_t first_seq_num, int64_t frame_id, bool discardable, - ArrayView frame_dependencies) { + std::span frame_dependencies) { RTC_DCHECK_RUN_ON(&sequence_checker_); DiscardOldInformation(); // Prevent memory overconsumption. @@ -139,7 +139,7 @@ void LossNotificationController::DiscardOldInformation() { } bool LossNotificationController::AllDependenciesDecodable( - ArrayView frame_dependencies) const { + std::span frame_dependencies) const { RTC_DCHECK_RUN_ON(&sequence_checker_); // Due to packet reordering, frame buffering and asynchronous decoders, it is diff --git a/modules/video_coding/loss_notification_controller.h b/modules/video_coding/loss_notification_controller.h index f6b99925e21..07265842fac 100644 --- a/modules/video_coding/loss_notification_controller.h +++ b/modules/video_coding/loss_notification_controller.h @@ -15,8 +15,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/sequence_checker.h" #include "modules/include/module_common_types.h" #include "rtc_base/system/no_unique_address.h" @@ -29,7 +29,7 @@ class LossNotificationController { struct FrameDetails { bool is_keyframe; int64_t frame_id; - ArrayView frame_dependencies; + std::span frame_dependencies; }; LossNotificationController(KeyFrameRequestSender* key_frame_request_sender, @@ -45,13 +45,13 @@ class LossNotificationController { void OnAssembledFrame(uint16_t first_seq_num, int64_t frame_id, bool discardable, - ArrayView frame_dependencies); + std::span frame_dependencies); private: void DiscardOldInformation(); bool AllDependenciesDecodable( - ArrayView frame_dependencies) const; + std::span frame_dependencies) const; // When the loss of a packet or the non-decodability of a frame is detected, // produces a key frame request or a loss notification. diff --git a/modules/video_coding/packet_buffer_unittest.cc b/modules/video_coding/packet_buffer_unittest.cc index 961a2594091..29f13e4f3e6 100644 --- a/modules/video_coding/packet_buffer_unittest.cc +++ b/modules/video_coding/packet_buffer_unittest.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" #include "common_video/h264/h264_common.h" @@ -48,7 +48,7 @@ void IgnoreResult(PacketBuffer::InsertResult /*result*/) {} // Validates frame boundaries are valid and returns first sequence_number for // each frame. std::vector StartSeqNums( - ArrayView> packets) { + std::span> packets) { std::vector result; bool frame_boundary = true; for (const auto& packet : packets) { @@ -117,7 +117,7 @@ class PacketBufferTest : public ::testing::Test { IsKeyFrame keyframe, // is keyframe IsFirst first, // is first packet of frame IsLast last, // is last packet of frame - ArrayView data = {}, + std::span data = {}, uint32_t timestamp = 123u) { // rtp timestamp auto packet = std::make_unique(); packet->video_header.codec = kVideoCodecGeneric; @@ -421,7 +421,7 @@ class PacketBufferH264Test : public PacketBufferTest { IsFirst first, // is first packet of frame IsLast last, // is last packet of frame uint32_t timestamp, // rtp timestamp - ArrayView data = {}, + std::span data = {}, uint32_t width = 0, // width of frame (SPS/IDR) uint32_t height = 0, // height of frame (SPS/IDR) bool generic = false) { // has generic descriptor @@ -459,7 +459,7 @@ class PacketBufferH264Test : public PacketBufferTest { IsFirst first, // is first packet of frame IsLast last, // is last packet of frame uint32_t timestamp, // rtp timestamp - ArrayView data = {}, + std::span data = {}, uint32_t width = 0, // width of frame (SPS/IDR) uint32_t height = 0) { // height of frame (SPS/IDR) auto packet = std::make_unique(); @@ -862,9 +862,9 @@ TEST_F(PacketBufferH264FrameGap, TEST_F(PacketBufferH264FrameGap, DoesntCrashWhenTryToClearBefore1stPacket) { // Test scenario copied from the https://issues.chromium.org/370689424 - InsertH264(41087, kKeyFrame, kNotFirst, kNotLast, 123, nullptr, 0, false); + InsertH264(41087, kKeyFrame, kNotFirst, kNotLast, 123, {}, 0, false); packet_buffer_.ClearTo(30896); - InsertH264(32896, kKeyFrame, kFirst, kLast, 123, nullptr, 0, false); + InsertH264(32896, kKeyFrame, kFirst, kLast, 123, {}, 0, false); } } // namespace diff --git a/modules/video_coding/rtp_frame_id_only_ref_finder.cc b/modules/video_coding/rtp_frame_id_only_ref_finder.cc index 714571461d4..614c3e60b35 100644 --- a/modules/video_coding/rtp_frame_id_only_ref_finder.cc +++ b/modules/video_coding/rtp_frame_id_only_ref_finder.cc @@ -13,7 +13,6 @@ #include #include -#include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/frame_object.h" #include "modules/video_coding/rtp_frame_reference_finder.h" @@ -24,8 +23,7 @@ RtpFrameReferenceFinder::ReturnVector RtpFrameIdOnlyRefFinder::ManageFrame( int frame_id) { frame->SetSpatialIndex(0); frame->SetId(unwrapper_.Unwrap(frame_id & (kFrameIdLength - 1))); - frame->num_references = - frame->frame_type() == VideoFrameType::kVideoFrameKey ? 0 : 1; + frame->num_references = frame->IsKey() ? 0 : 1; frame->references[0] = frame->Id() - 1; RtpFrameReferenceFinder::ReturnVector res; diff --git a/modules/video_coding/rtp_generic_ref_finder.cc b/modules/video_coding/rtp_generic_ref_finder.cc index 85dcaf08320..42268ee4b8d 100644 --- a/modules/video_coding/rtp_generic_ref_finder.cc +++ b/modules/video_coding/rtp_generic_ref_finder.cc @@ -21,6 +21,7 @@ #include "modules/video_coding/codecs/interface/common_constants.h" #include "modules/video_coding/rtp_frame_reference_finder.h" #include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" namespace webrtc { @@ -28,7 +29,7 @@ RtpFrameReferenceFinder::ReturnVector RtpGenericFrameRefFinder::ManageFrame( std::unique_ptr frame, const RTPVideoHeader::GenericDescriptorInfo& descriptor) { RtpFrameReferenceFinder::ReturnVector res; - if (descriptor.spatial_index >= kMaxSpatialLayers) { + if (checked_cast(descriptor.spatial_index) >= kMaxSpatialLayers) { RTC_LOG(LS_WARNING) << "Spatial index " << descriptor.spatial_index << " is unsupported."; return res; diff --git a/modules/video_coding/rtp_seq_num_only_ref_finder.cc b/modules/video_coding/rtp_seq_num_only_ref_finder.cc index ab09b012c33..3eda2d74bd2 100644 --- a/modules/video_coding/rtp_seq_num_only_ref_finder.cc +++ b/modules/video_coding/rtp_seq_num_only_ref_finder.cc @@ -14,7 +14,6 @@ #include #include -#include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/frame_object.h" #include "modules/video_coding/rtp_frame_reference_finder.h" #include "rtc_base/checks.h" @@ -48,7 +47,7 @@ RtpFrameReferenceFinder::ReturnVector RtpSeqNumOnlyRefFinder::ManageFrame( RtpSeqNumOnlyRefFinder::FrameDecision RtpSeqNumOnlyRefFinder::ManageFrameInternal(RtpFrameObject* frame) { - if (frame->frame_type() == VideoFrameType::kVideoFrameKey) { + if (frame->IsKey()) { last_seq_num_gop_.insert(std::make_pair( frame->last_seq_num(), std::make_pair(frame->last_seq_num(), frame->last_seq_num()))); @@ -82,7 +81,7 @@ RtpSeqNumOnlyRefFinder::ManageFrameInternal(RtpFrameObject* frame) { // this frame. uint16_t last_picture_id_gop = seq_num_it->second.first; uint16_t last_picture_id_with_padding_gop = seq_num_it->second.second; - if (frame->frame_type() == VideoFrameType::kVideoFrameDelta) { + if (frame->IsDelta()) { uint16_t prev_seq_num = frame->first_seq_num() - 1; if (prev_seq_num != last_picture_id_with_padding_gop) @@ -94,8 +93,7 @@ RtpSeqNumOnlyRefFinder::ManageFrameInternal(RtpFrameObject* frame) { // Since keyframes can cause reordering we can't simply assign the // picture id according to some incrementing counter. frame->SetId(frame->last_seq_num()); - frame->num_references = - frame->frame_type() == VideoFrameType::kVideoFrameDelta; + frame->num_references = frame->IsDelta(); frame->references[0] = rtp_seq_num_unwrapper_.Unwrap(last_picture_id_gop); if (AheadOf(frame->Id(), last_picture_id_gop)) { seq_num_it->second.first = frame->Id(); diff --git a/modules/video_coding/rtp_vp8_ref_finder.cc b/modules/video_coding/rtp_vp8_ref_finder.cc index 995ad86d96f..c88b08a28cd 100644 --- a/modules/video_coding/rtp_vp8_ref_finder.cc +++ b/modules/video_coding/rtp_vp8_ref_finder.cc @@ -15,7 +15,6 @@ #include #include -#include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/frame_object.h" #include "modules/video_coding/codecs/interface/common_constants.h" #include "modules/video_coding/codecs/vp8/include/vp8_globals.h" @@ -96,7 +95,7 @@ RtpVp8RefFinder::FrameDecision RtpVp8RefFinder::ManageFrameInternal( auto clean_layer_info_to = layer_info_.lower_bound(old_tl0_pic_idx); layer_info_.erase(layer_info_.begin(), clean_layer_info_to); - if (frame->frame_type() == VideoFrameType::kVideoFrameKey) { + if (frame->IsKey()) { if (codec_header.temporalIdx != 0) { return kDrop; } diff --git a/modules/video_coding/rtp_vp8_ref_finder_unittest.cc b/modules/video_coding/rtp_vp8_ref_finder_unittest.cc index 0ed70b2e399..b52d1fb811e 100644 --- a/modules/video_coding/rtp_vp8_ref_finder_unittest.cc +++ b/modules/video_coding/rtp_vp8_ref_finder_unittest.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/rtp_packet_infos.h" #include "api/video/encoded_frame.h" #include "api/video/encoded_image.h" @@ -44,7 +44,7 @@ namespace { MATCHER_P2(HasIdAndRefs, id, refs, "") { return Matches(Eq(id))(arg->Id()) && Matches(UnorderedElementsAreArray(refs))( - ArrayView(arg->references, arg->num_references)); + std::span(arg->references, arg->num_references)); } Matcher>&> diff --git a/modules/video_coding/rtp_vp9_ref_finder.cc b/modules/video_coding/rtp_vp9_ref_finder.cc index b7af0ffe3ec..5ee19a9aff7 100644 --- a/modules/video_coding/rtp_vp9_ref_finder.cc +++ b/modules/video_coding/rtp_vp9_ref_finder.cc @@ -18,7 +18,6 @@ #include "api/video/encoded_frame.h" #include "api/video/video_codec_constants.h" -#include "api/video/video_frame_type.h" #include "modules/rtp_rtcp/source/frame_object.h" #include "modules/video_coding/codecs/interface/common_constants.h" #include "modules/video_coding/codecs/vp9/include/vp9_globals.h" @@ -141,14 +140,14 @@ RtpVp9RefFinder::FrameDecision RtpVp9RefFinder::ManageFrameGof( info = &gof_info_it->second; - if (frame->frame_type() == VideoFrameType::kVideoFrameKey) { + if (frame->IsKey()) { frame->num_references = 0; FrameReceivedVp9(frame->Id(), info); FlattenFrameIdAndRefs(frame, codec_header.inter_layer_predicted); return kHandOff; } } else { - if (frame->frame_type() == VideoFrameType::kVideoFrameKey) { + if (frame->IsKey()) { RTC_LOG(LS_WARNING) << "Received keyframe without scalability structure"; return kDrop; } diff --git a/modules/video_coding/rtp_vp9_ref_finder_unittest.cc b/modules/video_coding/rtp_vp9_ref_finder_unittest.cc index ab3c67bf04b..08633251a93 100644 --- a/modules/video_coding/rtp_vp9_ref_finder_unittest.cc +++ b/modules/video_coding/rtp_vp9_ref_finder_unittest.cc @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/rtp_packet_infos.h" #include "api/video/encoded_frame.h" #include "api/video/encoded_image.h" @@ -192,7 +192,7 @@ class HasFrameMatcher : public MatcherInterface { return false; } - ArrayView actual_refs((*it)->references, (*it)->num_references); + std::span actual_refs((*it)->references, (*it)->num_references); if (!Matches(UnorderedElementsAreArray(expected_refs_))(actual_refs)) { if (result_listener->IsInterested()) { *result_listener << "Frame with frame_id:" << frame_id_ << " and " diff --git a/modules/video_coding/svc/BUILD.gn b/modules/video_coding/svc/BUILD.gn index 094f44e249c..dc084415168 100644 --- a/modules/video_coding/svc/BUILD.gn +++ b/modules/video_coding/svc/BUILD.gn @@ -98,12 +98,12 @@ rtc_library("simulcast_to_svc_converter") { ":scalability_structures", ":scalable_video_controller", "..:codec_globals_headers", + "..:video_codec_interface", + "..:video_coding_utility", "../../../api/video:encoded_image", "../../../api/video:video_bitrate_allocation", "../../../api/video_codecs:scalability_mode", "../../../api/video_codecs:video_codecs_api", - "../../../modules/video_coding:video_codec_interface", - "../../../modules/video_coding:video_coding_utility", "../../../rtc_base:checks", "../../../rtc_base/system:rtc_export", ] @@ -125,14 +125,11 @@ if (rtc_include_tests) { ":scalability_mode_util", ":scalability_structures", ":scalable_video_controller", - ":simulcast_to_svc_converter", "..:chain_diff_calculator", "..:frame_dependencies_calculator", - "../../../api:array_view", "../../../api/transport/rtp:dependency_descriptor", "../../../api/video:video_bitrate_allocation", "../../../api/video:video_frame", - "../../../api/video:video_frame_type", "../../../api/video_codecs:scalability_mode", "../../../api/video_codecs:video_codecs_api", "../../../common_video/generic_frame_descriptor", @@ -178,7 +175,6 @@ if (rtc_include_tests) { "../../../api/video:video_frame", "../../../api/video_codecs:scalability_mode", "../../../api/video_codecs:video_codecs_api", - "../../../rtc_base:checks", "../../../test:test_support", ] } diff --git a/modules/video_coding/svc/scalability_structure_key_svc_unittest.cc b/modules/video_coding/svc/scalability_structure_key_svc_unittest.cc index 175b8fc2773..71d64a3f47d 100644 --- a/modules/video_coding/svc/scalability_structure_key_svc_unittest.cc +++ b/modules/video_coding/svc/scalability_structure_key_svc_unittest.cc @@ -9,9 +9,9 @@ */ #include "modules/video_coding/svc/scalability_structure_key_svc.h" +#include #include -#include "api/array_view.h" #include "common_video/generic_frame_descriptor/generic_frame_info.h" #include "modules/video_coding/svc/scalability_structure_test_helpers.h" #include "test/gmock.h" @@ -234,10 +234,10 @@ TEST(ScalabilityStructureL3T3KeyTest, ReenablingSpatialLayerTriggersKeyFrame) { EXPECT_EQ(frames[13].temporal_id, 0); EXPECT_EQ(frames[14].temporal_id, 0); EXPECT_EQ(frames[15].temporal_id, 0); - auto all_frames = MakeArrayView(frames.data(), frames.size()); - EXPECT_TRUE(wrapper.FrameReferencesAreValid(all_frames.subview(0, 13))); + auto all_frames = std::span(frames.data(), frames.size()); + EXPECT_TRUE(wrapper.FrameReferencesAreValid(all_frames.subspan(0, 13))); // Frames starting from the frame#13 should not reference any earlier frames. - EXPECT_TRUE(wrapper.FrameReferencesAreValid(all_frames.subview(13))); + EXPECT_TRUE(wrapper.FrameReferencesAreValid(all_frames.subspan(13))); } } // namespace diff --git a/modules/video_coding/svc/scalability_structure_l2t2_key_shift_unittest.cc b/modules/video_coding/svc/scalability_structure_l2t2_key_shift_unittest.cc index 153294014d8..ebf3ce42a11 100644 --- a/modules/video_coding/svc/scalability_structure_l2t2_key_shift_unittest.cc +++ b/modules/video_coding/svc/scalability_structure_l2t2_key_shift_unittest.cc @@ -9,9 +9,9 @@ */ #include "modules/video_coding/svc/scalability_structure_l2t2_key_shift.h" +#include #include -#include "api/array_view.h" #include "common_video/generic_frame_descriptor/generic_frame_info.h" #include "modules/video_coding/svc/scalability_structure_test_helpers.h" #include "test/gmock.h" @@ -236,8 +236,7 @@ TEST(ScalabilityStructureL2T2KeyShiftTest, ReenableS1TriggersKeyFrame) { EXPECT_THAT(frames[4].temporal_id, 1); // Expect frame[5] to be a key frame. - EXPECT_TRUE( - wrapper.FrameReferencesAreValid(MakeArrayView(frames.data() + 5, 4))); + EXPECT_TRUE(wrapper.FrameReferencesAreValid(std::span(frames.data() + 5, 4))); EXPECT_THAT(frames[5].spatial_id, 0); EXPECT_THAT(frames[6].spatial_id, 1); diff --git a/modules/video_coding/svc/scalability_structure_test_helpers.cc b/modules/video_coding/svc/scalability_structure_test_helpers.cc index 004b7d245ea..7b4f1e4764c 100644 --- a/modules/video_coding/svc/scalability_structure_test_helpers.cc +++ b/modules/video_coding/svc/scalability_structure_test_helpers.cc @@ -12,10 +12,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/video/video_bitrate_allocation.h" #include "common_video/generic_frame_descriptor/generic_frame_info.h" #include "modules/video_coding/chain_diff_calculator.h" @@ -66,7 +66,7 @@ void ScalabilityStructureWrapper::GenerateFrames( } bool ScalabilityStructureWrapper::FrameReferencesAreValid( - ArrayView frames) const { + std::span frames) const { bool valid = true; // VP9 and AV1 supports up to 8 buffers. Expect no more buffers are not used. std::bitset<8> buffer_contains_frame; diff --git a/modules/video_coding/svc/scalability_structure_test_helpers.h b/modules/video_coding/svc/scalability_structure_test_helpers.h index 42257b59a1f..ddc492a8a29 100644 --- a/modules/video_coding/svc/scalability_structure_test_helpers.h +++ b/modules/video_coding/svc/scalability_structure_test_helpers.h @@ -12,9 +12,9 @@ #include +#include #include -#include "api/array_view.h" #include "api/video/video_bitrate_allocation.h" #include "common_video/generic_frame_descriptor/generic_frame_info.h" #include "modules/video_coding/chain_diff_calculator.h" @@ -43,7 +43,7 @@ class ScalabilityStructureWrapper { // Returns false and ADD_FAILUREs for frames with invalid references. // In particular validates no frame frame reference to frame before frames[0]. // In error messages frames are indexed starting with 0. - bool FrameReferencesAreValid(ArrayView frames) const; + bool FrameReferencesAreValid(std::span frames) const; private: ScalableVideoController& structure_controller_; diff --git a/modules/video_coding/svc/scalability_structure_unittest.cc b/modules/video_coding/svc/scalability_structure_unittest.cc index 849f08f1478..1d95c6fffd3 100644 --- a/modules/video_coding/svc/scalability_structure_unittest.cc +++ b/modules/video_coding/svc/scalability_structure_unittest.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "api/video/video_bitrate_allocation.h" #include "api/video_codecs/scalability_mode.h" @@ -115,14 +115,12 @@ TEST_P(ScalabilityStructureTest, controller->StreamConfig(); EXPECT_EQ(config.num_spatial_layers, static_config->num_spatial_layers); EXPECT_EQ(config.num_temporal_layers, static_config->num_temporal_layers); - EXPECT_THAT( - MakeArrayView(config.scaling_factor_num, config.num_spatial_layers), - ElementsAreArray(static_config->scaling_factor_num, - static_config->num_spatial_layers)); - EXPECT_THAT( - MakeArrayView(config.scaling_factor_den, config.num_spatial_layers), - ElementsAreArray(static_config->scaling_factor_den, - static_config->num_spatial_layers)); + EXPECT_THAT(std::span(config.scaling_factor_num, config.num_spatial_layers), + ElementsAreArray(static_config->scaling_factor_num, + static_config->num_spatial_layers)); + EXPECT_THAT(std::span(config.scaling_factor_den, config.num_spatial_layers), + ElementsAreArray(static_config->scaling_factor_den, + static_config->num_spatial_layers)); } TEST_P(ScalabilityStructureTest, diff --git a/modules/video_coding/timing/BUILD.gn b/modules/video_coding/timing/BUILD.gn index ea1862f98f8..75de611cce2 100644 --- a/modules/video_coding/timing/BUILD.gn +++ b/modules/video_coding/timing/BUILD.gn @@ -35,11 +35,7 @@ rtc_library("frame_delay_variation_kalman_filter") { "frame_delay_variation_kalman_filter.cc", "frame_delay_variation_kalman_filter.h", ] - deps = [ - "../../../api/units:data_size", - "../../../api/units:time_delta", - "../../../rtc_base:checks", - ] + deps = [ "../../../rtc_base:checks" ] visibility = [ ":jitter_estimator", ":timing_unittests", @@ -63,7 +59,6 @@ rtc_library("jitter_estimator") { "../../../rtc_base:logging", "../../../rtc_base:rolling_accumulator", "../../../rtc_base:rtc_numerics", - "../../../rtc_base:safe_conversions", "../../../rtc_base/experiments:field_trial_parser", "../../../system_wrappers", "//third_party/abseil-cpp/absl/strings:string_view", @@ -92,7 +87,6 @@ rtc_library("timestamp_extrapolator") { "../../../api:field_trials_view", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../modules:module_api_public", "../../../rtc_base:logging", "../../../rtc_base:rtc_numerics", "../../../rtc_base/experiments:field_trial_parser", @@ -117,7 +111,6 @@ rtc_library("timing_module") { "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:macromagic", - "../../../rtc_base:rtc_numerics", "../../../rtc_base/experiments:field_trial_parser", "../../../rtc_base/synchronization:mutex", "../../../system_wrappers", @@ -141,14 +134,12 @@ rtc_library("timing_unittests") { ":rtt_filter", ":timestamp_extrapolator", ":timing_module", - "../../../api:array_view", "../../../api:field_trials", "../../../api/units:data_size", "../../../api/units:frequency", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:histogram_percentile_counter", - "../../../rtc_base:timeutils", "../../../system_wrappers", "../../../system_wrappers:metrics", "../../../test:create_test_field_trials", diff --git a/modules/video_coding/timing/timing.cc b/modules/video_coding/timing/timing.cc index 9485dedebb2..7bea914ac3f 100644 --- a/modules/video_coding/timing/timing.cc +++ b/modules/video_coding/timing/timing.cc @@ -53,19 +53,26 @@ void CheckDelaysValid(TimeDelta min_delay, TimeDelta max_delay) { } // namespace +void VCMTiming::VideoDelayTimings::Reset() { + minimum_delay = TimeDelta::Zero(); + estimated_max_decode_time = TimeDelta::Zero(); + render_delay = kDefaultRenderDelay; + min_playout_delay = TimeDelta::Zero(); + target_delay = TimeDelta::Zero(); + current_delay = TimeDelta::Zero(); +} + +bool VCMTiming::VideoDelayTimings::UseLowLatencyRendering() const { + return min_playout_delay.IsZero() && + max_playout_delay <= kLowLatencyStreamMaxPlayoutDelayThreshold; +} + VCMTiming::VCMTiming(Clock* clock, const FieldTrialsView& field_trials) : clock_(clock), ts_extrapolator_( std::make_unique(clock_->CurrentTime(), field_trials)), decode_time_filter_(std::make_unique()), - render_delay_(kDefaultRenderDelay), - min_playout_delay_(TimeDelta::Zero()), - max_playout_delay_(TimeDelta::Seconds(10)), - jitter_delay_(TimeDelta::Zero()), - current_delay_(TimeDelta::Zero()), - prev_frame_timestamp_(0), - num_decoded_frames_(0), zero_playout_delay_min_pacing_("min_pacing", kZeroPlayoutDelayDefaultMinPacing), last_decode_scheduled_(Timestamp::Zero()) { @@ -77,28 +84,24 @@ void VCMTiming::Reset() { MutexLock lock(&mutex_); ts_extrapolator_->Reset(clock_->CurrentTime()); decode_time_filter_ = std::make_unique(); - render_delay_ = kDefaultRenderDelay; - min_playout_delay_ = TimeDelta::Zero(); - jitter_delay_ = TimeDelta::Zero(); - current_delay_ = TimeDelta::Zero(); - prev_frame_timestamp_ = 0; + timings_.Reset(); } void VCMTiming::set_render_delay(TimeDelta render_delay) { MutexLock lock(&mutex_); - render_delay_ = render_delay; + timings_.render_delay = render_delay; } TimeDelta VCMTiming::min_playout_delay() const { MutexLock lock(&mutex_); - return min_playout_delay_; + return timings_.min_playout_delay; } void VCMTiming::set_min_playout_delay(TimeDelta min_playout_delay) { MutexLock lock(&mutex_); - if (min_playout_delay_ != min_playout_delay) { - CheckDelaysValid(min_playout_delay, max_playout_delay_); - min_playout_delay_ = min_playout_delay; + if (timings_.min_playout_delay != min_playout_delay) { + CheckDelaysValid(min_playout_delay, timings_.max_playout_delay); + timings_.min_playout_delay = min_playout_delay; } } @@ -106,60 +109,19 @@ void VCMTiming::set_playout_delay(const VideoPlayoutDelay& playout_delay) { MutexLock lock(&mutex_); // No need to call `CheckDelaysValid` as the same invariant (min <= max) // is guaranteed by the `VideoPlayoutDelay` type. - min_playout_delay_ = playout_delay.min(); - max_playout_delay_ = playout_delay.max(); + timings_.min_playout_delay = playout_delay.min(); + timings_.max_playout_delay = playout_delay.max(); } -void VCMTiming::SetJitterDelay(TimeDelta jitter_delay) { +void VCMTiming::SetJitterDelay(TimeDelta minimum_delay) { MutexLock lock(&mutex_); - if (jitter_delay != jitter_delay_) { - jitter_delay_ = jitter_delay; + if (minimum_delay != timings_.minimum_delay) { + timings_.minimum_delay = minimum_delay; // When in initial state, set current delay to minimum delay. - if (current_delay_.IsZero()) { - current_delay_ = jitter_delay_; - } - } -} - -void VCMTiming::UpdateCurrentDelay(uint32_t frame_timestamp) { - MutexLock lock(&mutex_); - TimeDelta target_delay = TargetDelayInternal(); - - if (current_delay_.IsZero()) { - // Not initialized, set current delay to target. - current_delay_ = target_delay; - } else if (target_delay != current_delay_) { - TimeDelta delay_diff = target_delay - current_delay_; - // Never change the delay with more than 100 ms every second. If we're - // changing the delay in too large steps we will get noticeable freezes. By - // limiting the change we can increase the delay in smaller steps, which - // will be experienced as the video is played in slow motion. When lowering - // the delay the video will be played at a faster pace. - TimeDelta max_change = TimeDelta::Zero(); - if (frame_timestamp < 0x0000ffff && prev_frame_timestamp_ > 0xffff0000) { - // wrap - max_change = - TimeDelta::Millis(kDelayMaxChangeMsPerS * - (frame_timestamp + (static_cast(1) << 32) - - prev_frame_timestamp_) / - 90000); - } else { - max_change = - TimeDelta::Millis(kDelayMaxChangeMsPerS * - (frame_timestamp - prev_frame_timestamp_) / 90000); + if (timings_.current_delay.IsZero()) { + timings_.current_delay = timings_.minimum_delay; } - - if (max_change <= TimeDelta::Zero()) { - // Any changes less than 1 ms are truncated and will be postponed. - // Negative change will be due to reordering and should be ignored. - return; - } - delay_diff = std::max(delay_diff, -max_change); - delay_diff = std::min(delay_diff, max_change); - - current_delay_ = current_delay_ + delay_diff; } - prev_frame_timestamp_ = frame_timestamp; } void VCMTiming::UpdateCurrentDelay(Timestamp render_time, @@ -167,16 +129,16 @@ void VCMTiming::UpdateCurrentDelay(Timestamp render_time, MutexLock lock(&mutex_); TimeDelta target_delay = TargetDelayInternal(); TimeDelta delayed = (actual_decode_time - render_time) + - EstimatedMaxDecodeTime() + render_delay_; + EstimatedMaxDecodeTime() + timings_.render_delay; // Only consider `delayed` as negative by more than a few microseconds. if (delayed.ms() < 0) { return; } - if (current_delay_ + delayed <= target_delay) { - current_delay_ += delayed; + if (timings_.current_delay + delayed <= target_delay) { + timings_.current_delay += delayed; } else { - current_delay_ = target_delay; + timings_.current_delay = target_delay; } } @@ -184,7 +146,7 @@ void VCMTiming::StopDecodeTimer(TimeDelta decode_time, Timestamp now) { MutexLock lock(&mutex_); decode_time_filter_->AddTiming(decode_time.ms(), now.ms()); RTC_DCHECK_GE(decode_time, TimeDelta::Zero()); - ++num_decoded_frames_; + ++timings_.num_decoded_frames; } void VCMTiming::IncomingTimestamp(uint32_t rtp_timestamp, Timestamp now) { @@ -205,7 +167,7 @@ void VCMTiming::SetLastDecodeScheduledTimestamp( Timestamp VCMTiming::RenderTimeInternal(uint32_t frame_timestamp, Timestamp now) const { - if (UseLowLatencyRendering()) { + if (timings_.UseLowLatencyRendering()) { // Render as soon as possible or with low-latency renderer algorithm. return Timestamp::Zero(); } @@ -218,10 +180,11 @@ Timestamp VCMTiming::RenderTimeInternal(uint32_t frame_timestamp, } Timestamp estimated_complete_time = *local_time; - // Make sure the actual delay stays in the range of `min_playout_delay_` - // and `max_playout_delay_`. + // Make sure the actual delay stays in the range of `min_playout_delay` + // and `max_playout_delay`. TimeDelta actual_delay = - std::clamp(current_delay_, min_playout_delay_, max_playout_delay_); + std::clamp(timings_.current_delay, timings_.min_playout_delay, + timings_.max_playout_delay); return estimated_complete_time + actual_delay; } @@ -237,7 +200,8 @@ TimeDelta VCMTiming::MaxWaitingTime(Timestamp render_time, MutexLock lock(&mutex_); if (render_time.IsZero() && zero_playout_delay_min_pacing_->us() > 0 && - min_playout_delay_.IsZero() && max_playout_delay_ > TimeDelta::Zero()) { + timings_.min_playout_delay.IsZero() && + timings_.max_playout_delay > TimeDelta::Zero()) { // `render_time` == 0 indicates that the frame should be decoded and // rendered as soon as possible. However, the decoder can be choked if too // many frames are sent at once. Therefore, limit the interframe delay to @@ -253,7 +217,7 @@ TimeDelta VCMTiming::MaxWaitingTime(Timestamp render_time, : earliest_next_decode_start_time - now; return max_wait_time; } - return render_time - now - EstimatedMaxDecodeTime() - render_delay_; + return render_time - now - EstimatedMaxDecodeTime() - timings_.render_delay; } TimeDelta VCMTiming::TargetVideoDelay() const { @@ -262,53 +226,32 @@ TimeDelta VCMTiming::TargetVideoDelay() const { } TimeDelta VCMTiming::TargetDelayInternal() const { - return std::max(min_playout_delay_, - jitter_delay_ + EstimatedMaxDecodeTime() + render_delay_); + return std::max(timings_.min_playout_delay, timings_.minimum_delay + + EstimatedMaxDecodeTime() + + timings_.render_delay); } // TODO(crbug.com/webrtc/15197): Centralize delay arithmetic. TimeDelta VCMTiming::StatsTargetDelayInternal() const { TimeDelta stats_target_delay = - TargetDelayInternal() - (EstimatedMaxDecodeTime() + render_delay_); + TargetDelayInternal() - + (EstimatedMaxDecodeTime() + timings_.render_delay); return std::max(TimeDelta::Zero(), stats_target_delay); } VideoFrame::RenderParameters VCMTiming::RenderParameters() const { MutexLock lock(&mutex_); - return {.use_low_latency_rendering = UseLowLatencyRendering(), + return {.use_low_latency_rendering = timings_.UseLowLatencyRendering(), .max_composition_delay_in_frames = max_composition_delay_in_frames_}; } -bool VCMTiming::UseLowLatencyRendering() const { - // min_playout_delay_==0, - // max_playout_delay_<=kLowLatencyStreamMaxPlayoutDelayThreshold indicates - // that the low-latency path should be used, which means that frames should be - // decoded and rendered as soon as possible. - return min_playout_delay_.IsZero() && - max_playout_delay_ <= kLowLatencyStreamMaxPlayoutDelayThreshold; -} - VCMTiming::VideoDelayTimings VCMTiming::GetTimings() const { MutexLock lock(&mutex_); - return VideoDelayTimings{ - .num_decoded_frames = num_decoded_frames_, - .minimum_delay = jitter_delay_, - .estimated_max_decode_time = EstimatedMaxDecodeTime(), - .render_delay = render_delay_, - .min_playout_delay = min_playout_delay_, - .max_playout_delay = max_playout_delay_, - .target_delay = StatsTargetDelayInternal(), - .current_delay = current_delay_}; -} - -void VCMTiming::SetTimingFrameInfo(const TimingFrameInfo& info) { - MutexLock lock(&mutex_); - timing_frame_info_.emplace(info); -} - -std::optional VCMTiming::GetTimingFrameInfo() { - MutexLock lock(&mutex_); - return timing_frame_info_; + VideoDelayTimings timings = timings_; + // TODO(b/493549134): Update in StopDecodeTimer. + timings.estimated_max_decode_time = EstimatedMaxDecodeTime(); + timings.target_delay = StatsTargetDelayInternal(); + return timings; } void VCMTiming::SetMaxCompositionDelayInFrames( diff --git a/modules/video_coding/timing/timing.h b/modules/video_coding/timing/timing.h index fe35e8aaf7d..5ae772a05a2 100644 --- a/modules/video_coding/timing/timing.h +++ b/modules/video_coding/timing/timing.h @@ -33,57 +33,56 @@ namespace webrtc { class VCMTiming { public: struct VideoDelayTimings { - size_t num_decoded_frames; + static constexpr TimeDelta kDefaultRenderDelay = TimeDelta::Millis(10); + + void Reset(); + // Returns whether the low-latency path should be used, i.e., frames should + // be decoded and rendered as soon as possible. + bool UseLowLatencyRendering() const; + + size_t num_decoded_frames = 0; // Pre-decode delay added to smooth out frame delay variation ("jitter") // caused by the network. The target delay will be no smaller than this // delay, thus it is called `minimum_delay`. - TimeDelta minimum_delay; + TimeDelta minimum_delay = TimeDelta::Zero(); // Estimated time needed to decode a video frame. Obtained as the 95th // percentile decode time over a recent time window. - TimeDelta estimated_max_decode_time; + TimeDelta estimated_max_decode_time = TimeDelta::Zero(); // Post-decode delay added to smooth out frame delay variation caused by // decoding and rendering. Set to a constant. - TimeDelta render_delay; + TimeDelta render_delay = kDefaultRenderDelay; // Minimum total delay used when determining render time for a frame. // Obtained from API, `playout-delay` RTP header extension, or A/V sync. - TimeDelta min_playout_delay; + TimeDelta min_playout_delay = TimeDelta::Zero(); // Maximum total delay used when determining render time for a frame. // Obtained from `playout-delay` RTP header extension. - TimeDelta max_playout_delay; + TimeDelta max_playout_delay = TimeDelta::Seconds(10); // Target total delay. Obtained from all the elements above. - TimeDelta target_delay; + TimeDelta target_delay = TimeDelta::Zero(); // Current total delay. Obtained by smoothening the `target_delay`. - TimeDelta current_delay; + TimeDelta current_delay = TimeDelta::Zero(); }; - static constexpr TimeDelta kDefaultRenderDelay = TimeDelta::Millis(10); - static constexpr int kDelayMaxChangeMsPerS = 100; - VCMTiming(Clock* clock, const FieldTrialsView& field_trials); virtual ~VCMTiming() = default; // Resets the timing to the initial state. void Reset(); - // Set the amount of time needed to render an image. Defaults to 10 ms. + // Sets the amount of time needed to render an image. Defaults to 10 ms. void set_render_delay(TimeDelta render_delay); - // Set the minimum time the video must be delayed on the receiver to + // Sets the minimum time the video must be delayed on the receiver to // get the desired jitter buffer level. void SetJitterDelay(TimeDelta required_delay); - // Set/get the minimum playout delay from capture to render. + // Sets/gets the minimum playout delay from capture to render. TimeDelta min_playout_delay() const; void set_min_playout_delay(TimeDelta min_playout_delay); - // Set the minimum and maximum playout delay from capture to render. + // Sets the minimum and maximum playout delay from capture to render. void set_playout_delay(const VideoPlayoutDelay& playout_delay); - // Increases or decreases the current delay to get closer to the target delay. - // Calculates how long it has been since the previous call to this function, - // and increases/decreases the delay in proportion to the time difference. - void UpdateCurrentDelay(uint32_t frame_timestamp); - // Increases or decreases the current delay to get closer to the target delay. // Given the actual decode time in ms and the render time in ms for a frame, // this function calculates how late the frame is and increases the delay @@ -119,12 +118,9 @@ class VCMTiming { // render delay. TimeDelta TargetVideoDelay() const; - // Return current timing information. + // Returns current timing information. VideoDelayTimings GetTimings() const; - void SetTimingFrameInfo(const TimingFrameInfo& info); - std::optional GetTimingFrameInfo(); - void SetMaxCompositionDelayInFrames( std::optional max_composition_delay_in_frames); @@ -140,7 +136,6 @@ class VCMTiming { TimeDelta TargetDelayInternal() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); TimeDelta StatsTargetDelayInternal() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - bool UseLowLatencyRendering() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); mutable Mutex mutex_; Clock* const clock_; @@ -148,19 +143,10 @@ class VCMTiming { RTC_PT_GUARDED_BY(mutex_); std::unique_ptr decode_time_filter_ RTC_GUARDED_BY(mutex_) RTC_PT_GUARDED_BY(mutex_); - TimeDelta render_delay_ RTC_GUARDED_BY(mutex_); - // Best-effort playout delay range for frames from capture to render. - // The receiver tries to keep the delay between `min_playout_delay_ms_` - // and `max_playout_delay_ms_` taking the network jitter into account. - // A special case is where min_playout_delay_ms_ = max_playout_delay_ms_ = 0, - // in which case the receiver tries to play the frames as they arrive. - TimeDelta min_playout_delay_ RTC_GUARDED_BY(mutex_); - TimeDelta max_playout_delay_ RTC_GUARDED_BY(mutex_); - TimeDelta jitter_delay_ RTC_GUARDED_BY(mutex_); - TimeDelta current_delay_ RTC_GUARDED_BY(mutex_); - uint32_t prev_frame_timestamp_ RTC_GUARDED_BY(mutex_); - std::optional timing_frame_info_ RTC_GUARDED_BY(mutex_); - size_t num_decoded_frames_ RTC_GUARDED_BY(mutex_); + + // Holds the current video delay timings. + VideoDelayTimings timings_ RTC_GUARDED_BY(mutex_); + std::optional max_composition_delay_in_frames_ RTC_GUARDED_BY(mutex_); // Set by the field trial WebRTC-ZeroPlayoutDelay. The parameter min_pacing // determines the minimum delay between frames scheduled for decoding that is diff --git a/modules/video_coding/timing/timing_unittest.cc b/modules/video_coding/timing/timing_unittest.cc index 17e1d3d6777..b2ea1ad7efc 100644 --- a/modules/video_coding/timing/timing_unittest.cc +++ b/modules/video_coding/timing/timing_unittest.cc @@ -26,6 +26,9 @@ namespace { constexpr Frequency k25Fps = Frequency::Hertz(25); constexpr Frequency k90kHz = Frequency::KiloHertz(90); +constexpr TimeDelta kJitterDelay = TimeDelta::Millis(100); +constexpr TimeDelta kDecodeTime = TimeDelta::Millis(20); +constexpr TimeDelta kRenderDelay = TimeDelta::Millis(15); MATCHER(HasConsistentVideoDelayTimings, "") { // Delays should be non-negative. @@ -79,101 +82,14 @@ MATCHER(HasConsistentVideoDelayTimings, "") { return p && m; } -} // namespace - -TEST(VCMTimingTest, JitterDelay) { - FieldTrials field_trials = CreateTestFieldTrials(); - SimulatedClock clock(0); - VCMTiming timing(&clock, field_trials); - timing.Reset(); - - uint32_t timestamp = 0; - timing.UpdateCurrentDelay(timestamp); - - timing.Reset(); - - timing.IncomingTimestamp(timestamp, clock.CurrentTime()); - TimeDelta jitter_delay = TimeDelta::Millis(20); - timing.SetJitterDelay(jitter_delay); - timing.UpdateCurrentDelay(timestamp); - timing.set_render_delay(TimeDelta::Zero()); - auto wait_time = timing.MaxWaitingTime( - timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), - /*too_many_frames_queued=*/false); - // First update initializes the render time. Since we have no decode delay - // we get wait_time = renderTime - now - renderDelay = jitter. - EXPECT_EQ(jitter_delay, wait_time); - - jitter_delay += TimeDelta::Millis(VCMTiming::kDelayMaxChangeMsPerS + 10); - timestamp += 90000; - clock.AdvanceTimeMilliseconds(1000); - timing.SetJitterDelay(jitter_delay); - timing.UpdateCurrentDelay(timestamp); - wait_time = timing.MaxWaitingTime( - timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), - /*too_many_frames_queued=*/false); - // Since we gradually increase the delay we only get 100 ms every second. - EXPECT_EQ(jitter_delay - TimeDelta::Millis(10), wait_time); - - timestamp += 90000; - clock.AdvanceTimeMilliseconds(1000); - timing.UpdateCurrentDelay(timestamp); - wait_time = timing.MaxWaitingTime( - timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), - /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay, wait_time); - - // Insert frames without jitter, verify that this gives the exact wait time. - const int kNumFrames = 300; - for (int i = 0; i < kNumFrames; i++) { - clock.AdvanceTime(1 / k25Fps); - timestamp += k90kHz / k25Fps; - timing.IncomingTimestamp(timestamp, clock.CurrentTime()); +void UpdateDecodeTimer(VCMTiming& timing, + SimulatedClock& clock, + TimeDelta decode_time) { + for (int i = 0; i < k25Fps.hertz(); ++i) { + clock.AdvanceTime(decode_time); + timing.StopDecodeTimer(decode_time, clock.CurrentTime()); + clock.AdvanceTime(1 / k25Fps - decode_time); } - timing.UpdateCurrentDelay(timestamp); - wait_time = timing.MaxWaitingTime( - timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), - /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay, wait_time); - - // Add decode time estimates for 1 second. - const TimeDelta kDecodeTime = TimeDelta::Millis(10); - for (int i = 0; i < k25Fps.hertz(); i++) { - clock.AdvanceTime(kDecodeTime); - timing.StopDecodeTimer(kDecodeTime, clock.CurrentTime()); - timestamp += k90kHz / k25Fps; - clock.AdvanceTime(1 / k25Fps - kDecodeTime); - timing.IncomingTimestamp(timestamp, clock.CurrentTime()); - } - timing.UpdateCurrentDelay(timestamp); - wait_time = timing.MaxWaitingTime( - timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), - /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay, wait_time); - - const TimeDelta kMinTotalDelay = TimeDelta::Millis(200); - timing.set_min_playout_delay(kMinTotalDelay); - clock.AdvanceTimeMilliseconds(5000); - timestamp += 5 * 90000; - timing.UpdateCurrentDelay(timestamp); - const TimeDelta kRenderDelay = TimeDelta::Millis(10); - timing.set_render_delay(kRenderDelay); - wait_time = timing.MaxWaitingTime( - timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), - /*too_many_frames_queued=*/false); - // We should at least have kMinTotalDelayMs - decodeTime (10) - renderTime - // (10) to wait. - EXPECT_EQ(kMinTotalDelay - kDecodeTime - kRenderDelay, wait_time); - // The total video delay should be equal to the min total delay. - EXPECT_EQ(kMinTotalDelay, timing.TargetVideoDelay()); - - // Reset playout delay. - timing.set_min_playout_delay(TimeDelta::Zero()); - clock.AdvanceTimeMilliseconds(5000); - timestamp += 5 * 90000; - timing.UpdateCurrentDelay(timestamp); - - EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings()); } TEST(VCMTimingTest, TimestampWrapAround) { @@ -203,7 +119,6 @@ TEST(VCMTimingTest, UseLowLatencyRenderer) { FieldTrials field_trials = CreateTestFieldTrials(); SimulatedClock clock(0); VCMTiming timing(&clock, field_trials); - timing.Reset(); // Default is false. EXPECT_FALSE(timing.RenderParameters().use_low_latency_rendering); // False if min playout delay > 0. @@ -234,7 +149,6 @@ TEST(VCMTimingTest, MaxWaitingTimeIsZeroForZeroRenderTime) { SimulatedClock clock(kStartTimeUs); FieldTrials field_trials = CreateTestFieldTrials(); VCMTiming timing(&clock, field_trials); - timing.Reset(); timing.set_playout_delay({TimeDelta::Zero(), TimeDelta::Zero()}); for (int i = 0; i < 10; ++i) { clock.AdvanceTime(kTimeDelta); @@ -274,7 +188,6 @@ TEST(VCMTimingTest, MaxWaitingTimeZeroDelayPacingExperiment) { constexpr auto kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); VCMTiming timing(&clock, field_trials); - timing.Reset(); // MaxWaitingTime() returns zero for evenly spaced video frames. for (int i = 0; i < 10; ++i) { clock.AdvanceTime(kTimeDelta); @@ -324,7 +237,6 @@ TEST(VCMTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) { const TimeDelta kTimeDelta = TimeDelta::Millis(1000.0 / 60.0); SimulatedClock clock(kStartTimeUs); VCMTiming timing(&clock, field_trials); - timing.Reset(); clock.AdvanceTime(kTimeDelta); auto now = clock.CurrentTime(); Timestamp render_time = now + TimeDelta::Millis(30); @@ -358,7 +270,6 @@ TEST(VCMTimingTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) { constexpr auto kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); VCMTiming timing(&clock, field_trials); - timing.Reset(); // MaxWaitingTime() returns zero for evenly spaced video frames. for (int i = 0; i < 10; ++i) { clock.AdvanceTime(kTimeDelta); @@ -386,11 +297,38 @@ TEST(VCMTimingTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) { EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings()); } +TEST(VCMTimingTest, MaxWaitingTime) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + timing.set_render_delay(kRenderDelay); + UpdateDecodeTimer(timing, clock, kDecodeTime); + + Timestamp on_time = clock.CurrentTime() + kDecodeTime + kRenderDelay; + + // Early frame. + Timestamp render_time = on_time + TimeDelta::Millis(1); + EXPECT_EQ(timing.MaxWaitingTime(render_time, clock.CurrentTime(), + /*too_many_frames_queued=*/false), + TimeDelta::Millis(1)); + + // Exactly on time. + render_time = on_time; + EXPECT_EQ(timing.MaxWaitingTime(render_time, clock.CurrentTime(), + /*too_many_frames_queued=*/false), + TimeDelta::Zero()); + + // Late frame. + render_time = on_time - TimeDelta::Millis(1); + EXPECT_EQ(timing.MaxWaitingTime(render_time, clock.CurrentTime(), + /*too_many_frames_queued=*/false), + TimeDelta::Millis(-1)); +} + TEST(VCMTimingTest, UpdateCurrentDelayCapsWhenOffByMicroseconds) { FieldTrials field_trials = CreateTestFieldTrials(); SimulatedClock clock(0); VCMTiming timing(&clock, field_trials); - timing.Reset(); // Set larger initial current delay. timing.set_min_playout_delay(TimeDelta::Millis(200)); @@ -409,11 +347,27 @@ TEST(VCMTimingTest, UpdateCurrentDelayCapsWhenOffByMicroseconds) { // EXPECT_THAT(timing.GetTimings(), HasConsistentVideoDelayTimings()); } +TEST(VCMTimingTest, InitialVideoDelayTimings) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + + VCMTiming::VideoDelayTimings timings = timing.GetTimings(); + EXPECT_EQ(timings.num_decoded_frames, 0u); + EXPECT_EQ(timings.minimum_delay, TimeDelta::Zero()); + EXPECT_EQ(timings.estimated_max_decode_time, TimeDelta::Zero()); + EXPECT_EQ(timings.render_delay, + VCMTiming::VideoDelayTimings::kDefaultRenderDelay); + EXPECT_EQ(timings.min_playout_delay, TimeDelta::Zero()); + EXPECT_EQ(timings.target_delay, TimeDelta::Zero()); + EXPECT_EQ(timings.current_delay, TimeDelta::Zero()); + EXPECT_THAT(timings, HasConsistentVideoDelayTimings()); +} + TEST(VCMTimingTest, GetTimings) { FieldTrials field_trials = CreateTestFieldTrials(); SimulatedClock clock(33); VCMTiming timing(&clock, field_trials); - timing.Reset(); // Setup. TimeDelta render_delay = TimeDelta::Millis(11); @@ -435,14 +389,12 @@ TEST(VCMTimingTest, GetTimings) { clock.AdvanceTimeMilliseconds(100); // On decoded. - TimeDelta decode_time = TimeDelta::Millis(4); - timing.StopDecodeTimer(decode_time, clock.CurrentTime()); + UpdateDecodeTimer(timing, clock, kDecodeTime); VCMTiming::VideoDelayTimings timings = timing.GetTimings(); - EXPECT_EQ(timings.num_decoded_frames, 1u); + EXPECT_GT(timings.num_decoded_frames, 0u); EXPECT_EQ(timings.minimum_delay, minimum_delay); - // A single decoded frame is not enough to calculate p95. - EXPECT_EQ(timings.estimated_max_decode_time, TimeDelta::Zero()); + EXPECT_EQ(timings.estimated_max_decode_time, kDecodeTime); EXPECT_EQ(timings.render_delay, render_delay); EXPECT_EQ(timings.min_playout_delay, min_playout_delay); EXPECT_EQ(timings.max_playout_delay, max_playout_delay); @@ -451,6 +403,42 @@ TEST(VCMTimingTest, GetTimings) { EXPECT_THAT(timings, HasConsistentVideoDelayTimings()); } +TEST(VCMTimingTest, Reset) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(Timestamp::Millis(33)); + VCMTiming timing(&clock, field_trials); + + timing.set_render_delay(TimeDelta::Millis(11)); + TimeDelta min_playout_delay = TimeDelta::Millis(50); + TimeDelta max_playout_delay = TimeDelta::Millis(500); + timing.set_playout_delay({min_playout_delay, max_playout_delay}); + + // On complete. + timing.IncomingTimestamp(3000, clock.CurrentTime()); + + // On decodable. + Timestamp render_time = timing.RenderTime(3000, clock.CurrentTime()); + timing.SetJitterDelay(TimeDelta::Millis(123)); + timing.UpdateCurrentDelay(render_time, clock.CurrentTime()); + + // On decoded. + UpdateDecodeTimer(timing, clock, kDecodeTime); + + timing.Reset(); + + VCMTiming::VideoDelayTimings timings = timing.GetTimings(); + EXPECT_GT(timings.num_decoded_frames, 0u); + EXPECT_EQ(timings.minimum_delay, TimeDelta::Zero()); + EXPECT_EQ(timings.estimated_max_decode_time, TimeDelta::Zero()); + EXPECT_EQ(timings.render_delay, + VCMTiming::VideoDelayTimings::kDefaultRenderDelay); + EXPECT_EQ(timings.min_playout_delay, TimeDelta::Zero()); + EXPECT_EQ(timings.max_playout_delay, max_playout_delay); + EXPECT_EQ(timings.target_delay, TimeDelta::Zero()); + EXPECT_EQ(timings.current_delay, TimeDelta::Zero()); + EXPECT_THAT(timings, HasConsistentVideoDelayTimings()); +} + TEST(VCMTimingTest, GetTimingsBeforeAndAfterValidRtpTimestamp) { SimulatedClock clock(33); FieldTrials field_trials = CreateTestFieldTrials(); @@ -492,4 +480,123 @@ TEST(VCMTimingTest, GetTimingsBeforeAndAfterValidRtpTimestamp) { min_playout_delay); } +TEST(VCMTimingTest, IncreasesCurrentDelayWhenFrameIsLate) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + timing.SetJitterDelay(kJitterDelay); + timing.set_render_delay(kRenderDelay); + + // Current delay is initialized to jitter delay. + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay); + EXPECT_EQ(timing.TargetVideoDelay(), kJitterDelay + kRenderDelay); + + const TimeDelta kFrameDelay = TimeDelta::Millis(4); + // Current delay should be increased to get closer to target delay. + Timestamp render_time = clock.CurrentTime() + kRenderDelay; + Timestamp actual_decode_time = clock.CurrentTime() + kFrameDelay; + timing.UpdateCurrentDelay(render_time, actual_decode_time); + + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay + kFrameDelay); +} + +TEST(VCMTimingTest, CapsCurrentDelayIncreaseToTarget) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + timing.SetJitterDelay(kJitterDelay); + timing.set_render_delay(kRenderDelay); + + // Current delay is initialized to jitter delay. + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay); + EXPECT_EQ(timing.TargetVideoDelay(), kJitterDelay + kRenderDelay); + + const TimeDelta kFrameDelay = TimeDelta::Millis(588); + // Current delay should be increased but not exceed target delay. + Timestamp render_time = clock.CurrentTime() + kRenderDelay; + Timestamp actual_decode_time = clock.CurrentTime() + kFrameDelay; + timing.UpdateCurrentDelay(render_time, actual_decode_time); + + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay + kRenderDelay); +} + +TEST(VCMTimingTest, KeepsCurrentDelayWhenFrameIsEarly) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + timing.SetJitterDelay(kJitterDelay); + timing.set_render_delay(kRenderDelay); + + // Current delay is initialized to jitter delay. + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay); + EXPECT_EQ(timing.TargetVideoDelay(), kJitterDelay + kRenderDelay); + + // Frame is early. + // Delay should remain unchanged. + Timestamp render_time = clock.CurrentTime() + kRenderDelay * 2; + Timestamp actual_decode_time = clock.CurrentTime(); + timing.UpdateCurrentDelay(render_time, actual_decode_time); + + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay); +} + +TEST(VCMTimingTest, IncreasesCurrentDelayWhenFrameIsLateWithDecodeTime) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + timing.SetJitterDelay(kJitterDelay); + timing.set_render_delay(kRenderDelay); + UpdateDecodeTimer(timing, clock, kDecodeTime); + + // Current delay is initialized to jitter delay. + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay); + EXPECT_EQ(timing.TargetVideoDelay(), + kJitterDelay + kDecodeTime + kRenderDelay); + + const TimeDelta kFrameDelay = TimeDelta::Millis(4); + // Current delay should be increased to get closer to target delay. + Timestamp render_time = clock.CurrentTime() + kDecodeTime + kRenderDelay; + Timestamp actual_decode_time = clock.CurrentTime() + kFrameDelay; + timing.UpdateCurrentDelay(render_time, actual_decode_time); + + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay + kFrameDelay); +} + +TEST(VCMTimingTest, DecreasesCurrentDelayToTarget) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + timing.SetJitterDelay(kJitterDelay); + timing.set_render_delay(kRenderDelay); + + // Current delay should be increased to target for late frame. + timing.UpdateCurrentDelay(clock.CurrentTime(), + clock.CurrentTime() + TimeDelta::Millis(588)); + EXPECT_EQ(timing.GetTimings().current_delay, timing.TargetVideoDelay()); + + // Reduce jitter delay. + timing.SetJitterDelay(kJitterDelay / 2); + EXPECT_EQ(timing.TargetVideoDelay(), kJitterDelay / 2 + kRenderDelay); + + // Current delay should be decreased to new target for frame on-time. + timing.UpdateCurrentDelay(clock.CurrentTime() + kRenderDelay, + clock.CurrentTime()); + EXPECT_EQ(timing.GetTimings().current_delay, kJitterDelay / 2 + kRenderDelay); +} + +TEST(VCMTimingTest, MinPlayoutDelayUpdatesTargetDelay) { + FieldTrials field_trials = CreateTestFieldTrials(); + SimulatedClock clock(0); + VCMTiming timing(&clock, field_trials); + timing.SetJitterDelay(kJitterDelay); + timing.set_render_delay(kRenderDelay); + + const TimeDelta kMinPlayout = + kJitterDelay + kRenderDelay + TimeDelta::Millis(50); + timing.set_min_playout_delay(kMinPlayout); + + EXPECT_EQ(timing.TargetVideoDelay(), kMinPlayout); +} + +} // namespace } // namespace webrtc diff --git a/modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc b/modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc index b563d358dfb..d06a11a24e6 100644 --- a/modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc +++ b/modules/video_coding/utility/bandwidth_quality_scaler_unittest.cc @@ -87,7 +87,7 @@ class BandwidthQualityScalerTest : public ::testing::Test { explicit BandwidthQualityScalerTest(VideoCodecType codec_type) : task_queue_(time_controller_.GetTaskQueueFactory()->CreateTaskQueue( "BandwidthQualityScalerTestQueue", - TaskQueueFactory::Priority::NORMAL)), + TaskQueueFactory::Priority::kNormal)), handler_(std::make_unique()), codec_type_(codec_type) { task_queue_.SendTask([this] { diff --git a/modules/video_coding/utility/encoder_speed_controller_impl.cc b/modules/video_coding/utility/encoder_speed_controller_impl.cc index 987f0ad4ae6..ca0a2ae8f3e 100644 --- a/modules/video_coding/utility/encoder_speed_controller_impl.cc +++ b/modules/video_coding/utility/encoder_speed_controller_impl.cc @@ -15,6 +15,7 @@ #include #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "api/video_codecs/encoder_speed_controller.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -46,6 +47,10 @@ constexpr double kQpFilterAlpha = 0.2; // factor in order to not over-react. constexpr double kKeyframeEncodeTimeCompensator = 3.5; +// If the current speed index (or any faster) has a min PSNR gain factor, +// re-check every (N * psnr probing interval) that the gain is still there. +constexpr int kPsnrGainRecheckingFactor = 5; + } // namespace EncoderSpeedControllerImpl::EncoderSpeedControllerImpl( @@ -57,7 +62,8 @@ EncoderSpeedControllerImpl::EncoderSpeedControllerImpl( num_samples_(0), slow_filtered_encode_time_ms_(0), fast_filtered_encode_time_ms_(0), - filtered_qp_(0) {} + filtered_qp_(0), + last_psnr_probe_(Timestamp::MinusInfinity()) {} std::unique_ptr EncoderSpeedControllerImpl::Create( @@ -93,6 +99,16 @@ EncoderSpeedControllerImpl::Create( } } + if (config.psnr_probing_settings) { + if (config.psnr_probing_settings->sampling_interval.IsInfinite() || + config.psnr_probing_settings->sampling_interval.us() <= 0) { + RTC_LOG(LS_WARNING) + << "EncoderSpeedController: Invalid PSNR sampling interval: " + << config.psnr_probing_settings->sampling_interval; + return nullptr; + } + } + if (start_frame_interval.IsInfinite() || start_frame_interval.us() <= 0) { RTC_LOG(LS_WARNING) << "EncoderSpeedController: Invalid start frame interval: " @@ -109,6 +125,14 @@ void EncoderSpeedControllerImpl::ResetStats() { slow_filtered_encode_time_ms_ = 0; fast_filtered_encode_time_ms_ = 0; filtered_qp_ = 0; + + if (last_psnr_gain_check_.has_value() && + current_speed_index_ > last_psnr_gain_check_->speed_level) { + // We have moved to a faster speed than what the last PSNR gain check was + // performed at - no need for further re-checks of the gain until the speed + // is decreased again. + last_psnr_gain_check_.reset(); + } } void EncoderSpeedControllerImpl::IncreaseSpeed() { @@ -141,17 +165,89 @@ EncoderSpeedControllerImpl::GetEncodeSettings( EncoderSpeedController::FrameEncodingInfo frame_info) { RTC_CHECK(frame_interval_.IsFinite()); EncodeSettings settings; - const Config::SpeedLevel& current_level = - config_.speed_levels[current_speed_index_]; - - settings.speed = - current_level.speeds[static_cast(frame_info.reference_type)]; + settings.speed = config_.speed_levels[current_speed_index_] + .speeds[static_cast(frame_info.reference_type)]; + settings.baseline_comparison_speed = std::nullopt; + settings.calculate_psnr = false; + + if (config_.psnr_probing_settings.has_value() && + frame_info.timestamp.IsFinite() && + (frame_info.reference_type == ReferenceClass::kMain || + frame_info.reference_type == ReferenceClass::kKey) && + !frame_info.is_repeat_frame) { + bool regular_sampling_due = + config_.psnr_probing_settings->mode == + EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kRegularBaseLayerSampling && + frame_info.timestamp >= + (last_psnr_probe_ + + config_.psnr_probing_settings->sampling_interval); + + bool probe_needed_for_speed_change = + ShouldDecreaseSpeedDisregardingPsnr() && + PsnrProbeRequiredForNextSlowerSpeed(); + + bool should_recheck_psnr_gain = ShouldRecheckPsnrGain(frame_info.timestamp); + + if (regular_sampling_due || probe_needed_for_speed_change || + should_recheck_psnr_gain) { + Timestamp earlist_probe_time = + last_psnr_probe_.IsMinusInfinity() + ? frame_info.timestamp + : (last_psnr_probe_ + + (config_.psnr_probing_settings->sampling_interval * + config_.psnr_probing_settings->average_base_layer_ratio)); + + if (frame_info.timestamp >= earlist_probe_time) { + std::optional psnr_request_at_speed_index; + if (probe_needed_for_speed_change) { + RTC_DCHECK_GT(current_speed_index_, 0); + RTC_DCHECK(config_.speed_levels[current_speed_index_ - 1] + .min_psnr_gain.has_value()); + psnr_request_at_speed_index = current_speed_index_ - 1; + } else if (should_recheck_psnr_gain) { + RTC_DCHECK(last_psnr_gain_check_.has_value()); + psnr_request_at_speed_index = last_psnr_gain_check_->speed_level; + } + + if (psnr_request_at_speed_index.has_value()) { + const Config::SpeedLevel& psnr_request = + config_.speed_levels[*psnr_request_at_speed_index]; + + settings.baseline_comparison_speed = + psnr_request.min_psnr_gain->baseline_speed; + settings.calculate_psnr = true; + // Potentially override the target speed for this frame if this is a + // PSNR re-checking event. + settings.speed = + psnr_request.speeds[static_cast(ReferenceClass::kMain)]; + last_psnr_probe_ = frame_info.timestamp; + + RTC_LOG(LS_VERBOSE) + << "EncoderSpeedController: Initiating PSNR probe " + "for speed " + << settings.speed << " vs baseline " + << *settings.baseline_comparison_speed << "."; + + last_psnr_gain_check_ = { + .speed_level = *psnr_request_at_speed_index, + .timestamp = frame_info.timestamp, + }; + } else if (regular_sampling_due) { + // Regular sampling, no speed change expected, just gather data. + settings.calculate_psnr = true; + last_psnr_probe_ = frame_info.timestamp; + } + } + } + } return settings; } void EncoderSpeedControllerImpl::OnEncodedFrame( - EncoderSpeedController::EncodeResults results) { + EncoderSpeedController::EncodeResults results, + std::optional baseline_results) { double encode_tims_ms = results.encode_time.us() / 1000.0; if (results.frame_info.reference_type == ReferenceClass::kKey) { encode_tims_ms /= kKeyframeEncodeTimeCompensator; @@ -181,12 +277,70 @@ void EncoderSpeedControllerImpl::OnEncodedFrame( (kQpFilterAlpha * results.qp) + ((1 - kQpFilterAlpha) * filtered_qp_); } + if (baseline_results.has_value()) { + // Results from a PSNR probe have arrived! + last_psnr_probe_ = results.frame_info.timestamp; + RTC_LOG(LS_VERBOSE) + << "EncoderSpeedController: PSNR Probe result: { baseline speed: " + << baseline_results->speed + << ", psnr = " << baseline_results->psnr.value_or(-1) + << ", qp = " << baseline_results->qp + << ", encode_time = " << baseline_results->encode_time.ms() + << "ms } => { speed: " << results.speed + << ", psnr = " << results.psnr.value_or(-1) << ", qp = " << results.qp + << ", encode_time = " << results.encode_time.ms() << "ms }."; + } + if (ShouldIncreaseSpeed()) { // Using too much resources or QP is good enough, try to increase the speed. IncreaseSpeed(); - } else if (ShouldDecreaseSpeed()) { - // Headroom exists to reduce speed. - DecreaseSpeed(); + } else if (current_speed_index_ > 0) { + if (baseline_results.has_value()) { + // Results from a PSNR probe have arrived! + const Config::SpeedLevel& next_speed = + config_.speed_levels[current_speed_index_ - 1]; + if (!next_speed.min_psnr_gain.has_value()) { + RTC_LOG(LS_WARNING) + << "EncoderSpeedController: PSNR probe result received but no " + "threshold set for next level. Ignoring."; + return; + } + const Config::SpeedLevel::PsnrComparison& psnr_settings = + *next_speed.min_psnr_gain; + + if (results.speed != + next_speed.speeds[static_cast(ReferenceClass::kMain)] || + baseline_results->speed != psnr_settings.baseline_speed) { + // Current speed settings have gone out of sync with requested probe, + // ignore results. + RTC_LOG(LS_WARNING) + << "EncoderSpeedController: PSNR probe result received but speeds " + "are out of sync with next exptected. Ignoring."; + return; + } + + if (!baseline_results->psnr.has_value() || !results.psnr.has_value()) { + RTC_LOG(LS_WARNING) + << "EncoderSpeedController: PSNR probe result received, but no " + "actual PSNR measurements present. Ignoring."; + return; + } + + double psnr_gain = *results.psnr - *baseline_results->psnr; + RTC_LOG(LS_VERBOSE) << "EncoderSpeedController: PSNR gain: " << psnr_gain; + if (psnr_gain >= psnr_settings.psnr_threshold) { + RTC_LOG(LS_VERBOSE) + << "EncoderSpeedController: Decreasing speed due to PSNR gain."; + DecreaseSpeed(); + } else { + RTC_LOG(LS_VERBOSE) << "EncoderSpeedController: Not decreasing speed, " + "PSNR gain too low."; + } + } else if (ShouldDecreaseSpeedDisregardingPsnr() && + !PsnrProbeRequiredForNextSlowerSpeed()) { + // Headroom exists to reduce speed, and no PSNR requirement present. + DecreaseSpeed(); + } } } @@ -229,7 +383,7 @@ bool EncoderSpeedControllerImpl::ShouldIncreaseSpeed() const { return false; } -bool EncoderSpeedControllerImpl::ShouldDecreaseSpeed() const { +bool EncoderSpeedControllerImpl::ShouldDecreaseSpeedDisregardingPsnr() const { if (current_speed_index_ <= 0) { // Already at slowest speed. return false; @@ -257,4 +411,43 @@ bool EncoderSpeedControllerImpl::ShouldDecreaseSpeed() const { return false; } + +// Returns true if the next slower speed requires a PSNR check. +bool EncoderSpeedControllerImpl::PsnrProbeRequiredForNextSlowerSpeed() const { + return current_speed_index_ > 0 && + config_.speed_levels[current_speed_index_ - 1] + .min_psnr_gain.has_value(); +} + +// Returns true if the PSNR gain should be checked again to see if the quality +// benefit is still present. This method is only called once we have already +// moved to a speed requiring PSNR checks. +bool EncoderSpeedControllerImpl::ShouldRecheckPsnrGain( + Timestamp current_time) const { + if (current_speed_index_ <= 0 || !config_.psnr_probing_settings.has_value()) { + return false; + } + const Config::SpeedLevel& current_speed_level = + config_.speed_levels[current_speed_index_]; + if (!current_speed_level.min_psnr_gain.has_value()) { + return false; + } + if (!last_psnr_gain_check_.has_value()) { + return false; + } + if (last_psnr_gain_check_->speed_level > current_speed_index_ || + last_psnr_gain_check_->timestamp.IsInfinite()) { + return false; + } + + TimeDelta rechecking_interval = + config_.psnr_probing_settings->sampling_interval * + kPsnrGainRecheckingFactor; + TimeDelta avg_base_layer_frame_interval = + frame_interval_ * + (1.0 - (1 / config_.psnr_probing_settings->average_base_layer_ratio)); + + return (current_time - last_psnr_gain_check_->timestamp) >= + rechecking_interval - avg_base_layer_frame_interval; +} } // namespace webrtc diff --git a/modules/video_coding/utility/encoder_speed_controller_impl.h b/modules/video_coding/utility/encoder_speed_controller_impl.h index 9c7cbba822e..94e861b9426 100644 --- a/modules/video_coding/utility/encoder_speed_controller_impl.h +++ b/modules/video_coding/utility/encoder_speed_controller_impl.h @@ -12,8 +12,10 @@ #define MODULES_VIDEO_CODING_UTILITY_ENCODER_SPEED_CONTROLLER_IMPL_H_ #include +#include #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "api/video_codecs/encoder_speed_controller.h" namespace webrtc { @@ -40,8 +42,11 @@ class EncoderSpeedControllerImpl : public webrtc::EncoderSpeedController { // thereafter be configured with requested settings. EncodeSettings GetEncodeSettings(FrameEncodingInfo frame_info) override; - // Should be called after each frame has completed encoding. - void OnEncodedFrame(EncodeResults results) override; + // Should be called after each frame has completed encoding. If a baseline + // comparison speed was set in the `EncodeSettings`, the `baseline_results` + // parameter should be set with the results corresponding to those settings. + void OnEncodedFrame(EncodeResults results, + std::optional baseline_results) override; const Config& config() const { return config_; } @@ -50,7 +55,9 @@ class EncoderSpeedControllerImpl : public webrtc::EncoderSpeedController { TimeDelta start_frame_interval); bool ShouldIncreaseSpeed() const; - bool ShouldDecreaseSpeed() const; + bool ShouldDecreaseSpeedDisregardingPsnr() const; + bool PsnrProbeRequiredForNextSlowerSpeed() const; + bool ShouldRecheckPsnrGain(Timestamp current_time) const; void ResetStats(); void IncreaseSpeed(); @@ -66,6 +73,20 @@ class EncoderSpeedControllerImpl : public webrtc::EncoderSpeedController { double slow_filtered_encode_time_ms_; double fast_filtered_encode_time_ms_; double filtered_qp_; + + // Timestamp of last request for a PSNR measurement, either due to periodic + // sampling or requested for speed index change. Negative infinity if not set. + Timestamp last_psnr_probe_; + + // Timestamp and speed level index of the last PSNR probing request for the + // current layer. Note that we only track comparative PSNR gain checks here + // (i.e. an alternate speed was given), single frame PSNR sampling does not + // affect this value. + struct PsnrGainCheck { + int speed_level; + Timestamp timestamp; + }; + std::optional last_psnr_gain_check_; }; } // namespace webrtc diff --git a/modules/video_coding/utility/encoder_speed_controller_impl_unittest.cc b/modules/video_coding/utility/encoder_speed_controller_impl_unittest.cc index a887961a8f7..622028d9cd0 100644 --- a/modules/video_coding/utility/encoder_speed_controller_impl_unittest.cc +++ b/modules/video_coding/utility/encoder_speed_controller_impl_unittest.cc @@ -11,6 +11,7 @@ #include #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "api/video_codecs/encoder_speed_controller.h" #include "test/gmock.h" #include "test/gtest.h" @@ -65,7 +66,7 @@ TEST(EncoderSpeedControllerTest, GetEncodeSettingsBaseLayers) { ASSERT_NE(controller, nullptr); EncoderSpeedController::FrameEncodingInfo frame_info = { - .reference_type = ReferenceClass::kMain}; + .reference_type = ReferenceClass::kMain, .timestamp = Timestamp::Zero()}; // Starts at index 1 (speed 6) EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 6); @@ -74,7 +75,8 @@ TEST(EncoderSpeedControllerTest, GetEncodeSettingsBaseLayers) { for (int i = 0; i < 10; ++i) { controller->OnEncodedFrame({.encode_time = kFrameInterval * 0.90, .qp = 30, - .frame_info = frame_info}); + .frame_info = frame_info}, + /*baseline_results=*/std::nullopt); } // Speed should increase to 7 EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 7); @@ -83,7 +85,8 @@ TEST(EncoderSpeedControllerTest, GetEncodeSettingsBaseLayers) { for (int i = 0; i < 20; ++i) { controller->OnEncodedFrame({.encode_time = kFrameInterval * 0.10, .qp = 20, - .frame_info = frame_info}); + .frame_info = frame_info}, + /*baseline_results=*/std::nullopt); } // Speed should decrease to 6 EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 6); @@ -94,10 +97,11 @@ TEST(EncoderSpeedControllerTest, GetEncodeSettingsKeyFrame) { auto controller = EncoderSpeedController::Create(config, kFrameInterval); ASSERT_NE(controller, nullptr); - EXPECT_EQ( - controller->GetEncodeSettings({.reference_type = ReferenceClass::kKey}) - .speed, - 6); + EXPECT_EQ(controller + ->GetEncodeSettings({.reference_type = ReferenceClass::kKey, + .timestamp = Timestamp::Zero()}) + .speed, + 6); } TEST(EncoderSpeedControllerTest, GetEncodeSettingsWithTemporalLayers) { @@ -107,24 +111,28 @@ TEST(EncoderSpeedControllerTest, GetEncodeSettingsWithTemporalLayers) { auto controller = EncoderSpeedController::Create(config, kFrameInterval); ASSERT_NE(controller, nullptr); + EXPECT_EQ(controller + ->GetEncodeSettings({.reference_type = ReferenceClass::kKey, + .timestamp = Timestamp::Zero()}) + .speed, + 5); + EXPECT_EQ(controller + ->GetEncodeSettings({.reference_type = ReferenceClass::kMain, + .timestamp = Timestamp::Zero()}) + .speed, + 6); EXPECT_EQ( - controller->GetEncodeSettings({.reference_type = ReferenceClass::kKey}) - .speed, - 5); - EXPECT_EQ( - controller->GetEncodeSettings({.reference_type = ReferenceClass::kMain}) + controller + ->GetEncodeSettings({.reference_type = ReferenceClass::kIntermediate, + .timestamp = Timestamp::Zero()}) .speed, - 6); + 7); EXPECT_EQ( controller - ->GetEncodeSettings({.reference_type = ReferenceClass::kIntermediate}) + ->GetEncodeSettings({.reference_type = ReferenceClass::kNoneReference, + .timestamp = Timestamp::Zero()}) .speed, - 7); - EXPECT_EQ(controller - ->GetEncodeSettings( - {.reference_type = ReferenceClass::kNoneReference}) - .speed, - 8); + 8); } TEST(EncoderSpeedControllerTest, StaysAtMaxSpeed) { @@ -134,12 +142,13 @@ TEST(EncoderSpeedControllerTest, StaysAtMaxSpeed) { ASSERT_NE(controller, nullptr); EncoderSpeedController::FrameEncodingInfo frame_info = { - .reference_type = ReferenceClass::kMain}; + .reference_type = ReferenceClass::kMain, .timestamp = Timestamp::Zero()}; for (int i = 0; i < 20; ++i) { controller->OnEncodedFrame({.encode_time = kFrameInterval * 0.95, .qp = 30, - .frame_info = frame_info}); + .frame_info = frame_info}, + /*baseline_results=*/std::nullopt); } EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, @@ -153,10 +162,11 @@ TEST(EncoderSpeedControllerTest, StaysAtMinSpeed) { ASSERT_NE(controller, nullptr); EncoderSpeedController::FrameEncodingInfo frame_info = { - .reference_type = ReferenceClass::kMain}; + .reference_type = ReferenceClass::kMain, .timestamp = Timestamp::Zero()}; for (int i = 0; i < 20; ++i) { - controller->OnEncodedFrame({.speed = 5, .frame_info = frame_info}); + controller->OnEncodedFrame({.speed = 5, .frame_info = frame_info}, + /*baseline_results=*/std::nullopt); } EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, @@ -171,7 +181,7 @@ TEST(EncoderSpeedControllerTest, IncreasesSpeedOnLowQp) { ASSERT_NE(controller, nullptr); EncoderSpeedController::FrameEncodingInfo frame_info = { - .reference_type = ReferenceClass::kMain}; + .reference_type = ReferenceClass::kMain, .timestamp = Timestamp::Zero()}; EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 6); @@ -179,31 +189,314 @@ TEST(EncoderSpeedControllerTest, IncreasesSpeedOnLowQp) { for (int i = 0; i < 20; ++i) { controller->OnEncodedFrame({.encode_time = kFrameInterval * 0.60, .qp = 10, - .frame_info = frame_info}); + .frame_info = frame_info}, + /*baseline_results=*/std::nullopt); } // Speed should increase to 7 due to low QP EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 7); } -TEST(EncoderSpeedControllerTest, DoesNotDecreaseSpeedIfQpIsTooLow) { +TEST(EncoderSpeedControllerTest, TriggersRegularPsnrSampling) { EncoderSpeedController::Config config = GetDefaultConfig(); - config.speed_levels[0].min_qp = 20; // Min QP for speed 5 is 20 - config.start_speed_index = 1; + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kRegularBaseLayerSampling, + .sampling_interval = TimeDelta::Seconds(5)}; auto controller = EncoderSpeedController::Create(config, kFrameInterval); ASSERT_NE(controller, nullptr); EncoderSpeedController::FrameEncodingInfo frame_info = { - .reference_type = ReferenceClass::kMain}; + .reference_type = ReferenceClass::kMain, .timestamp = Timestamp::Zero()}; + + // First frame should always trigger PSNR if configured. + EXPECT_TRUE(controller->GetEncodeSettings(frame_info).calculate_psnr); + // Complete the frame. + controller->OnEncodedFrame( + {.encode_time = kFrameInterval * 0.5, .qp = 30, .frame_info = frame_info}, + /*baseline_results=*/std::nullopt); + + // Next frame within interval should not trigger PSNR. + frame_info.timestamp += kFrameInterval; + EXPECT_FALSE(controller->GetEncodeSettings(frame_info).calculate_psnr); + + // Advance to sampling interval. + frame_info.timestamp += config.psnr_probing_settings->sampling_interval; + EXPECT_TRUE(controller->GetEncodeSettings(frame_info).calculate_psnr); +} + +TEST(EncoderSpeedControllerTest, TriggersPsnrProbeForSpeedChange) { + EncoderSpeedController::Config config = GetDefaultConfig(); + // Default speed levels = {5, 6, 7}. + // To move from speed 6 to 5, we check speed 5's requirements. + config.speed_levels[0].min_psnr_gain = { + .baseline_speed = 6, // Compare against current speed (6) + .psnr_threshold = 1.0, + }; + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kOnlyWhenProbing, + .sampling_interval = TimeDelta::Seconds(1)}; + config.start_speed_index = 1; // Start at speed 6. + + auto controller = EncoderSpeedController::Create(config, kFrameInterval); + ASSERT_NE(controller, nullptr); + + // Initial state: Speed 6 (index 1). + EXPECT_EQ(controller + ->GetEncodeSettings({.reference_type = ReferenceClass::kKey, + .timestamp = Timestamp::Zero()}) + .speed, + 6); + + // Simulate low utilization to trigger speed decrease attempt. + // We need multiple samples to trigger the filter. + constexpr int kNumFrames = 10; + for (int i = 0; i < kNumFrames; ++i) { + controller->OnEncodedFrame( + {.encode_time = kFrameInterval * 0.1, + .qp = 20, + .frame_info = {.reference_type = ReferenceClass::kMain, + .timestamp = + Timestamp::Zero() + (i + 1) * kFrameInterval}}, + /*baseline_results=*/std::nullopt); + } + + // Next frame should be a probe. + // We expect it to try Speed 5. + EncoderSpeedController::EncodeSettings settings = + controller->GetEncodeSettings( + {.reference_type = ReferenceClass::kMain, + .timestamp = Timestamp::Zero() + kNumFrames * kFrameInterval}); + EXPECT_EQ(settings.speed, 5); + EXPECT_TRUE(settings.calculate_psnr); + EXPECT_EQ(settings.baseline_comparison_speed, 6); +} + +TEST(EncoderSpeedControllerTest, DecreasesSpeedOnSufficientPsnrGain) { + EncoderSpeedController::Config config = GetDefaultConfig(); + // Default speed levels = {5, 6, 7}. + // To move to Speed 5, we need 1.0dB gain over Speed 6. + config.speed_levels[0].min_psnr_gain = { + .baseline_speed = 6, + .psnr_threshold = 1.0, + }; + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kOnlyWhenProbing, + .sampling_interval = TimeDelta::Seconds(1)}; + config.start_speed_index = 1; // Start at speed 6. + + auto controller = EncoderSpeedController::Create(config, kFrameInterval); + ASSERT_NE(controller, nullptr); + + // Trigger probe. + constexpr int kNumFrames = 10; + for (int i = 0; i < kNumFrames; ++i) { + controller->OnEncodedFrame( + {.encode_time = kFrameInterval * 0.1, + .qp = 20, + .frame_info = {.reference_type = ReferenceClass::kMain, + .timestamp = + Timestamp::Zero() + (i + 1) * kFrameInterval}}, + /*baseline_results=*/std::nullopt); + } + + EncoderSpeedController::FrameEncodingInfo frame_info = { + .reference_type = ReferenceClass::kMain, + .timestamp = Timestamp::Zero() + kNumFrames * kFrameInterval}; + + // Get settings (verify it's a probe). + EncoderSpeedController::EncodeSettings settings = + controller->GetEncodeSettings(frame_info); + ASSERT_TRUE(settings.baseline_comparison_speed.has_value()); + EXPECT_EQ(settings.speed, 5); + EXPECT_EQ(settings.baseline_comparison_speed, 6); + + // Feed probe results. + // Result (Speed 5): 37.0dB (Higher quality) + // Baseline (Speed 6): 35.0dB (Lower quality) + // Gain: 2.0dB >= 1.0dB threshold. + EncoderSpeedController::EncodeResults results = { + .speed = settings.speed, // 5 + .encode_time = kFrameInterval * 0.1, + .qp = 20, + .psnr = 37.0, + .frame_info = frame_info}; + EncoderSpeedController::EncodeResults baseline_results = { + .speed = *settings.baseline_comparison_speed, // 6 + .encode_time = kFrameInterval * 0.1, + .qp = 20, + .psnr = 35.0, + .frame_info = frame_info}; + + controller->OnEncodedFrame(results, baseline_results); + + // Speed should decrease to 5. + frame_info.timestamp += kFrameInterval; + EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 5); +} + +TEST(EncoderSpeedControllerTest, MaintainsSpeedOnInsufficientPsnrGain) { + EncoderSpeedController::Config config = GetDefaultConfig(); + // Default speed levels = {5, 6, 7}. + // To move to Speed 5, we need 1.0dB gain over Speed 6. + config.speed_levels[0].min_psnr_gain = { + .baseline_speed = 6, + .psnr_threshold = 1.0, + }; + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kOnlyWhenProbing, + .sampling_interval = TimeDelta::Seconds(1)}; + config.start_speed_index = 1; // Start at speed 6. + + auto controller = EncoderSpeedController::Create(config, kFrameInterval); + ASSERT_NE(controller, nullptr); + + // Trigger probe. + constexpr int kNumFrames = 10; + for (int i = 0; i < kNumFrames; ++i) { + controller->OnEncodedFrame( + {.encode_time = kFrameInterval * 0.1, + .qp = 20, + .frame_info = {.reference_type = ReferenceClass::kMain, + .timestamp = + Timestamp::Zero() + (i + 1) * kFrameInterval}}, + /*baseline_results=*/std::nullopt); + } + + EncoderSpeedController::FrameEncodingInfo frame_info = { + .reference_type = ReferenceClass::kMain, + .timestamp = Timestamp::Zero() + kNumFrames * kFrameInterval}; + + // Get settings (verify it's a probe). + EncoderSpeedController::EncodeSettings settings = + controller->GetEncodeSettings(frame_info); + ASSERT_TRUE(settings.baseline_comparison_speed.has_value()); + EXPECT_EQ(settings.speed, 5); + EXPECT_EQ(settings.baseline_comparison_speed, 6); + + // Feed probe results. + // Result (Speed 5): 35.5dB + // Baseline (Speed 6): 35.0dB + // Gain: 0.5dB < 1.0dB threshold. + EncoderSpeedController::EncodeResults results = { + .speed = settings.speed, + .encode_time = kFrameInterval * 0.1, + .qp = 20, + .psnr = 35.5, + .frame_info = frame_info}; + EncoderSpeedController::EncodeResults baseline_results = { + .speed = *settings.baseline_comparison_speed, + .encode_time = kFrameInterval * 0.1, + .qp = 20, + .psnr = 35.0, + .frame_info = frame_info}; + + controller->OnEncodedFrame(results, baseline_results); + + // Speed should stay at 6 (reset to current index because gain was + // insufficient). + frame_info.timestamp += kFrameInterval; EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 6); +} +TEST(EncoderSpeedControllerTest, CreateFailsWithInvalidPsnrSamplingInterval) { + EncoderSpeedController::Config config = GetDefaultConfig(); + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kRegularBaseLayerSampling, + .sampling_interval = TimeDelta::Zero()}; + EXPECT_EQ(EncoderSpeedController::Create(config, kFrameInterval), nullptr); - // Simulate low encode time but also low QP - for (int i = 0; i < 20; ++i) { - controller->OnEncodedFrame({.encode_time = kFrameInterval * 0.10, - .qp = 10, - .frame_info = frame_info}); + config.psnr_probing_settings->sampling_interval = TimeDelta::PlusInfinity(); + EXPECT_EQ(EncoderSpeedController::Create(config, kFrameInterval), nullptr); +} + +TEST(EncoderSpeedControllerTest, OnEncodedFrameIgnoresResultWithMissingPsnr) { + EncoderSpeedController::Config config = GetDefaultConfig(); + config.speed_levels[0].min_psnr_gain = { + .baseline_speed = 6, + .psnr_threshold = 1.0, + }; + config.psnr_probing_settings = { + .mode = EncoderSpeedController::Config::PsnrProbingSettings::Mode:: + kOnlyWhenProbing, + .sampling_interval = TimeDelta::Seconds(1)}; + config.start_speed_index = 1; + + auto controller = EncoderSpeedController::Create(config, kFrameInterval); + ASSERT_NE(controller, nullptr); + + EncoderSpeedController::FrameEncodingInfo frame_info = { + .reference_type = ReferenceClass::kMain, .timestamp = Timestamp::Zero()}; + + // Trigger probe. + constexpr int kNumFrames = 10; + for (int i = 0; i < kNumFrames; ++i) { + controller->OnEncodedFrame( + {.encode_time = kFrameInterval * 0.1, + .qp = 20, + .frame_info = {.reference_type = ReferenceClass::kMain, + .timestamp = + Timestamp::Zero() + (i + 1) * kFrameInterval}}, + /*baseline_results=*/std::nullopt); } - // Speed should NOT decrease to 5 because QP is below the next level's min_qp + + // Get settings (verify it's a probe). + EncoderSpeedController::EncodeSettings settings = + controller->GetEncodeSettings( + {.reference_type = ReferenceClass::kMain, + .timestamp = Timestamp::Zero() + kNumFrames * kFrameInterval}); + ASSERT_TRUE(settings.baseline_comparison_speed.has_value()); + EXPECT_EQ(settings.speed, 5); + + // Feed probe results with missing PSNR. + frame_info.timestamp = Timestamp::Zero() + kNumFrames * kFrameInterval; + EncoderSpeedController::EncodeResults results = { + .speed = settings.speed, + .encode_time = kFrameInterval * 0.1, + .qp = 20, + .psnr = std::nullopt, // Missing PSNR + .frame_info = frame_info}; + EncoderSpeedController::EncodeResults baseline_results = { + .speed = *settings.baseline_comparison_speed, + .encode_time = kFrameInterval * 0.1, + .qp = 20, + .psnr = 35.0, + .frame_info = frame_info}; + + controller->OnEncodedFrame(results, baseline_results); + + // Speed should stay at 6 (reset to current index because probe invalid). + frame_info.timestamp += kFrameInterval; EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 6); } + +TEST(EncoderSpeedControllerTest, WorksWithDefaultInfiniteTimestamp) { + EncoderSpeedController::Config config = GetDefaultConfig(); + auto controller = EncoderSpeedController::Create(config, kFrameInterval); + ASSERT_NE(controller, nullptr); + + // Default frame_info has timestamp = Timestamp::MinusInfinity(). + EncoderSpeedController::FrameEncodingInfo frame_info = { + .reference_type = ReferenceClass::kMain}; + EXPECT_TRUE(frame_info.timestamp.IsMinusInfinity()); + + // Should return a valid speed (start speed 6). + // PSNR calculation should be false because timestamp is not finite. + EncoderSpeedController::EncodeSettings settings = + controller->GetEncodeSettings(frame_info); + EXPECT_EQ(settings.speed, 6); + EXPECT_FALSE(settings.calculate_psnr); + + // OnEncodedFrame should also handle it gracefully. + controller->OnEncodedFrame( + {.encode_time = kFrameInterval * 0.5, .qp = 30, .frame_info = frame_info}, + /*baseline_results=*/std::nullopt); + + // Speed should remain same. + EXPECT_EQ(controller->GetEncodeSettings(frame_info).speed, 6); +} + } // namespace webrtc diff --git a/modules/video_coding/utility/ivf_file_reader.cc b/modules/video_coding/utility/ivf_file_reader.cc index c8ef46c0fbb..1bcbd56337a 100644 --- a/modules/video_coding/utility/ivf_file_reader.cc +++ b/modules/video_coding/utility/ivf_file_reader.cc @@ -177,7 +177,7 @@ std::optional IvfFileReader::NextFrame() { image.SetSpatialLayerFrameSize(static_cast(i), layer_sizes[i]); } if (is_first_frame) { - image._frameType = VideoFrameType::kVideoFrameKey; + image.set_frame_type(VideoFrameType::kVideoFrameKey); } return image; diff --git a/modules/video_coding/utility/ivf_file_reader_unittest.cc b/modules/video_coding/utility/ivf_file_reader_unittest.cc index ede43e07f40..81d456129fd 100644 --- a/modules/video_coding/utility/ivf_file_reader_unittest.cc +++ b/modules/video_coding/utility/ivf_file_reader_unittest.cc @@ -77,7 +77,7 @@ class IvfFileReaderTest : public ::testing::Test { bool use_capture_tims_ms, int spatial_layers_count) { std::unique_ptr file_writer = - IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(file_name_), 0); + IvfFileWriter::Wrap(file_name_, /*byte_limit=*/0); ASSERT_TRUE(file_writer.get()); ASSERT_TRUE(WriteDummyTestFrames(file_writer.get(), codec_type, kWidth, kHeight, kNumFrames, use_capture_tims_ms, diff --git a/modules/video_coding/utility/ivf_file_writer.cc b/modules/video_coding/utility/ivf_file_writer.cc index 422e4585a80..5da19fba4e9 100644 --- a/modules/video_coding/utility/ivf_file_writer.cc +++ b/modules/video_coding/utility/ivf_file_writer.cc @@ -61,9 +61,10 @@ std::unique_ptr IvfFileWriter::Wrap(FileWrapper file, } std::unique_ptr IvfFileWriter::Wrap(absl::string_view filename, - size_t byte_limit) { - return std::unique_ptr( - new IvfFileWriter(FileWrapper::OpenWriteOnly(filename), byte_limit)); + size_t byte_limit, + int* error /*=nullptr*/) { + return std::unique_ptr(new IvfFileWriter( + FileWrapper::OpenWriteOnly(filename, error), byte_limit)); } bool IvfFileWriter::WriteHeader() { diff --git a/modules/video_coding/utility/ivf_file_writer.h b/modules/video_coding/utility/ivf_file_writer.h index c1c088690be..f2847ff651b 100644 --- a/modules/video_coding/utility/ivf_file_writer.h +++ b/modules/video_coding/utility/ivf_file_writer.h @@ -30,10 +30,11 @@ class IvfFileWriter { // Close or ~IvfFileWriter. If writing a frame would take the file above the // `byte_limit` the file will be closed, the write (and all future writes) // will fail. A `byte_limit` of 0 is equivalent to no limit. - static std::unique_ptr Wrap(FileWrapper file, - size_t byte_limit); + [[deprecated]] static std::unique_ptr Wrap(FileWrapper file, + size_t byte_limit); static std::unique_ptr Wrap(absl::string_view filename, - size_t byte_limit); + size_t byte_limit, + int* error = nullptr); ~IvfFileWriter(); IvfFileWriter(const IvfFileWriter&) = delete; diff --git a/modules/video_coding/utility/ivf_file_writer_unittest.cc b/modules/video_coding/utility/ivf_file_writer_unittest.cc index 2b791820cb5..021887a0498 100644 --- a/modules/video_coding/utility/ivf_file_writer_unittest.cc +++ b/modules/video_coding/utility/ivf_file_writer_unittest.cc @@ -111,8 +111,7 @@ class IvfFileWriterTest : public ::testing::Test { void RunBasicFileStructureTest(VideoCodecType codec_type, const uint8_t fourcc[4], bool use_capture_tims_ms) { - file_writer_ = - IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(file_name_), 0); + file_writer_ = IvfFileWriter::Wrap(file_name_, 0); ASSERT_TRUE(file_writer_.get()); const int kWidth = 320; const int kHeight = 240; @@ -186,9 +185,8 @@ TEST_F(IvfFileWriterTest, ClosesWhenReachesLimit) { const int kNumFramesToFit = 1; file_writer_ = IvfFileWriter::Wrap( - FileWrapper::OpenWriteOnly(file_name_), - kHeaderSize + - kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + file_name_, kHeaderSize + kNumFramesToFit * + (kFrameHeaderSize + sizeof(dummy_payload))); ASSERT_TRUE(file_writer_.get()); ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, @@ -210,9 +208,8 @@ TEST_F(IvfFileWriterTest, UseDefaultValueWhenWidthAndHeightAreZero) { const int kNumFramesToFit = 1; file_writer_ = IvfFileWriter::Wrap( - FileWrapper::OpenWriteOnly(file_name_), - kHeaderSize + - kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + file_name_, kHeaderSize + kNumFramesToFit * + (kFrameHeaderSize + sizeof(dummy_payload))); ASSERT_TRUE(file_writer_.get()); ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, @@ -238,9 +235,8 @@ TEST_F(IvfFileWriterTest, UseDefaultValueWhenOnlyWidthIsZero) { const int kNumFramesToFit = 1; file_writer_ = IvfFileWriter::Wrap( - FileWrapper::OpenWriteOnly(file_name_), - kHeaderSize + - kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + file_name_, kHeaderSize + kNumFramesToFit * + (kFrameHeaderSize + sizeof(dummy_payload))); ASSERT_TRUE(file_writer_.get()); ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, @@ -266,9 +262,8 @@ TEST_F(IvfFileWriterTest, UseDefaultValueWhenOnlyHeightIsZero) { const int kNumFramesToFit = 1; file_writer_ = IvfFileWriter::Wrap( - FileWrapper::OpenWriteOnly(file_name_), - kHeaderSize + - kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + file_name_, kHeaderSize + kNumFramesToFit * + (kFrameHeaderSize + sizeof(dummy_payload))); ASSERT_TRUE(file_writer_.get()); ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, @@ -294,9 +289,8 @@ TEST_F(IvfFileWriterTest, UseDefaultValueWhenHeightAndWidthAreNotZero) { const int kNumFramesToFit = 1; file_writer_ = IvfFileWriter::Wrap( - FileWrapper::OpenWriteOnly(file_name_), - kHeaderSize + - kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload))); + file_name_, kHeaderSize + kNumFramesToFit * + (kFrameHeaderSize + sizeof(dummy_payload))); ASSERT_TRUE(file_writer_.get()); ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight, diff --git a/modules/video_coding/utility/qp_parser.cc b/modules/video_coding/utility/qp_parser.cc index 491bae8e56c..95cf4fdb7ca 100644 --- a/modules/video_coding/utility/qp_parser.cc +++ b/modules/video_coding/utility/qp_parser.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/video/video_codec_constants.h" #include "api/video/video_codec_type.h" #include "modules/video_coding/utility/vp8_header_parser.h" @@ -58,7 +58,7 @@ std::optional QpParser::H264QpParser::Parse(const uint8_t* frame_data, size_t frame_size) { MutexLock lock(&mutex_); bitstream_parser_.ParseBitstream( - ArrayView(frame_data, frame_size)); + std::span(frame_data, frame_size)); return bitstream_parser_.GetLastSliceQp(); } @@ -67,7 +67,7 @@ std::optional QpParser::H265QpParser::Parse(const uint8_t* frame_data, size_t frame_size) { MutexLock lock(&mutex_); bitstream_parser_.ParseBitstream( - ArrayView(frame_data, frame_size)); + std::span(frame_data, frame_size)); return bitstream_parser_.GetLastSliceQp(); } #endif diff --git a/modules/video_coding/utility/simulcast_test_fixture_impl.cc b/modules/video_coding/utility/simulcast_test_fixture_impl.cc index b5058ba4702..8ea24fe3eae 100644 --- a/modules/video_coding/utility/simulcast_test_fixture_impl.cc +++ b/modules/video_coding/utility/simulcast_test_fixture_impl.cc @@ -47,6 +47,7 @@ using ::testing::_; using ::testing::AllOf; using ::testing::Field; +using ::testing::Property; using ::testing::Return; namespace webrtc { @@ -101,10 +102,10 @@ class SimulcastTestFixtureImpl::TestEncodedImageCallback bool is_h264 = (codec_specific_info->codecType == kVideoCodecH264); // Only store the base layer. if (encoded_image.SimulcastIndex().value_or(0) == 0) { - if (encoded_image._frameType == VideoFrameType::kVideoFrameKey) { + if (encoded_image.IsKey()) { encoded_key_frame_.SetEncodedData(EncodedImageBuffer::Create( encoded_image.data(), encoded_image.size())); - encoded_key_frame_._frameType = VideoFrameType::kVideoFrameKey; + encoded_key_frame_.set_frame_type(VideoFrameType::kVideoFrameKey); } else { encoded_frame_.SetEncodedData(EncodedImageBuffer::Create( encoded_image.data(), encoded_image.size())); @@ -123,6 +124,11 @@ class SimulcastTestFixtureImpl::TestEncodedImageCallback } return Result(Result::OK, encoded_image.RtpTimestamp()); } + + void OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) override {} + // This method only makes sense for VP8. void GetLastEncodedFrameInfo(int* temporal_layer, bool* layer_sync, @@ -368,7 +374,7 @@ void SimulcastTestFixtureImpl::ExpectStream(VideoFrameType frame_type, int scaleResolutionDownBy) { EXPECT_CALL( encoder_callback_, - OnEncodedImage(AllOf(Field(&EncodedImage::_frameType, frame_type), + OnEncodedImage(AllOf(Property(&EncodedImage::frame_type, frame_type), Field(&EncodedImage::_encodedWidth, kDefaultWidth / scaleResolutionDownBy), Field(&EncodedImage::_encodedHeight, @@ -712,8 +718,8 @@ void SimulcastTestFixtureImpl::SwitchingToOneStream(int width, int height) { VideoFrameType::kVideoFrameDelta); EXPECT_CALL( encoder_callback_, - OnEncodedImage(AllOf(Field(&EncodedImage::_frameType, - VideoFrameType::kVideoFrameKey), + OnEncodedImage(AllOf(Property(&EncodedImage::frame_type, + VideoFrameType::kVideoFrameKey), Field(&EncodedImage::_encodedWidth, width), Field(&EncodedImage::_encodedHeight, height)), _)) @@ -944,12 +950,13 @@ void SimulcastTestFixtureImpl::TestDecodeWidthHeightSet() { .WillRepeatedly( [&](const EncodedImage& encoded_image, const CodecSpecificInfo* /* codec_specific_info */) { - EXPECT_EQ(encoded_image._frameType, VideoFrameType::kVideoFrameKey); + EXPECT_EQ(encoded_image.frame_type(), + VideoFrameType::kVideoFrameKey); size_t index = encoded_image.SimulcastIndex().value_or(0); encoded_frame[index].SetEncodedData(EncodedImageBuffer::Create( encoded_image.data(), encoded_image.size())); - encoded_frame[index]._frameType = encoded_image._frameType; + encoded_frame[index].set_frame_type(encoded_image.frame_type()); return EncodedImageCallback::Result( EncodedImageCallback::Result::OK, 0); }); diff --git a/modules/video_coding/utility/vp9_uncompressed_header_parser.cc b/modules/video_coding/utility/vp9_uncompressed_header_parser.cc index ca3c8595c46..4219ecd4dee 100644 --- a/modules/video_coding/utility/vp9_uncompressed_header_parser.cc +++ b/modules/video_coding/utility/vp9_uncompressed_header_parser.cc @@ -12,10 +12,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "modules/video_coding/utility/vp9_constants.h" #include "rtc_base/bitstream_reader.h" #include "rtc_base/logging.h" @@ -513,7 +513,7 @@ void Parse(BitstreamReader& br, } std::optional ParseUncompressedVp9Header( - ArrayView buf) { + std::span buf) { BitstreamReader reader(buf); Vp9UncompressedHeader frame_info; Parse(reader, &frame_info, /*qp_only=*/false); @@ -526,7 +526,7 @@ std::optional ParseUncompressedVp9Header( namespace vp9 { bool GetQp(const uint8_t* buf, size_t length, int* qp) { - BitstreamReader reader(MakeArrayView(buf, length)); + BitstreamReader reader(std::span(buf, length)); Vp9UncompressedHeader frame_info; Parse(reader, &frame_info, /*qp_only=*/true); if (!reader.Ok()) { diff --git a/modules/video_coding/utility/vp9_uncompressed_header_parser.h b/modules/video_coding/utility/vp9_uncompressed_header_parser.h index 0153a3b579a..400559b17de 100644 --- a/modules/video_coding/utility/vp9_uncompressed_header_parser.h +++ b/modules/video_coding/utility/vp9_uncompressed_header_parser.h @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "modules/video_coding/utility/vp9_constants.h" namespace webrtc { @@ -151,7 +151,7 @@ struct Vp9UncompressedHeader { // Parses the uncompressed header and populates (most) values in a // UncompressedHeader struct. Returns nullopt on failure. std::optional ParseUncompressedVp9Header( - ArrayView buf); + std::span buf); } // namespace webrtc diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc index 6d329a1bcec..7b3579df85c 100644 --- a/modules/video_coding/video_codec_initializer.cc +++ b/modules/video_coding/video_codec_initializer.cc @@ -80,8 +80,8 @@ VideoCodec VideoCodecInitializer::SetupCodec( config.legacy_conference_mode; video_codec.SetFrameDropEnabled(config.frame_drop_enabled); - video_codec.numberOfSimulcastStreams = - static_cast(streams.size()); + video_codec.numberOfSimulcastStreams = static_cast( + std::min(streams.size(), static_cast(kMaxSimulcastStreams))); video_codec.minBitrate = streams[0].min_bitrate_bps / 1000; bool codec_active = false; // Active configuration might not be fully copied to `streams` for SVC yet. @@ -104,7 +104,9 @@ VideoCodec VideoCodecInitializer::SetupCodec( int max_framerate = 0; std::optional scalability_mode = streams[0].scalability_mode; - for (size_t i = 0; i < streams.size(); ++i) { + const size_t num_streams = + std::min(streams.size(), static_cast(kMaxSimulcastStreams)); + for (size_t i = 0; i < num_streams; ++i) { SimulcastStream* sim_stream = &video_codec.simulcastStream[i]; RTC_DCHECK_GT(streams[i].width, 0); RTC_DCHECK_GT(streams[i].height, 0); diff --git a/modules/video_coding/video_coding_impl.cc b/modules/video_coding/video_coding_impl.cc deleted file mode 100644 index 9d261f3d772..00000000000 --- a/modules/video_coding/video_coding_impl.cc +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/video_coding_impl.h" - -#include -#include -#include -#include -#include - -#include "api/environment/environment.h" -#include "api/rtp_headers.h" -#include "api/sequence_checker.h" -#include "api/video/encoded_image.h" -#include "api/video/render_resolution.h" -#include "api/video_codecs/video_decoder.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "modules/video_coding/encoded_frame.h" -#include "modules/video_coding/generic_decoder.h" -#include "modules/video_coding/include/video_coding.h" -#include "modules/video_coding/include/video_coding_defines.h" -#include "modules/video_coding/timing/timing.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "system_wrappers/include/clock.h" - -namespace webrtc { -namespace vcm { - -int64_t VCMProcessTimer::Period() const { - return _periodMs; -} - -int64_t VCMProcessTimer::TimeUntilProcess() const { - const int64_t time_since_process = _clock->TimeInMilliseconds() - _latestMs; - const int64_t time_until_process = _periodMs - time_since_process; - return std::max(time_until_process, 0); -} - -void VCMProcessTimer::Processed() { - _latestMs = _clock->TimeInMilliseconds(); -} - -DEPRECATED_VCMDecoderDataBase::DEPRECATED_VCMDecoderDataBase() { - decoder_sequence_checker_.Detach(); -} - -VideoDecoder* DEPRECATED_VCMDecoderDataBase::DeregisterExternalDecoder( - uint8_t payload_type) { - RTC_DCHECK_RUN_ON(&decoder_sequence_checker_); - auto it = decoders_.find(payload_type); - if (it == decoders_.end()) { - return nullptr; - } - - // We can't use payload_type to check if the decoder is currently in use, - // because payload type may be out of date (e.g. before we decode the first - // frame after RegisterReceiveCodec). - if (current_decoder_ && current_decoder_->IsSameDecoder(it->second)) { - // Release it if it was registered and in use. - current_decoder_ = std::nullopt; - } - VideoDecoder* ret = it->second; - decoders_.erase(it); - return ret; -} - -// Add the external decoder object to the list of external decoders. -// Won't be registered as a receive codec until RegisterReceiveCodec is called. -void DEPRECATED_VCMDecoderDataBase::RegisterExternalDecoder( - uint8_t payload_type, - VideoDecoder* external_decoder) { - RTC_DCHECK_RUN_ON(&decoder_sequence_checker_); - // If payload value already exists, erase old and insert new. - DeregisterExternalDecoder(payload_type); - decoders_[payload_type] = external_decoder; -} - -bool DEPRECATED_VCMDecoderDataBase::IsExternalDecoderRegistered( - uint8_t payload_type) const { - RTC_DCHECK_RUN_ON(&decoder_sequence_checker_); - return payload_type == current_payload_type_ || - decoders_.find(payload_type) != decoders_.end(); -} - -void DEPRECATED_VCMDecoderDataBase::RegisterReceiveCodec( - uint8_t payload_type, - const VideoDecoder::Settings& settings) { - // If payload value already exists, erase old and insert new. - if (payload_type == current_payload_type_) { - current_payload_type_ = std::nullopt; - } - decoder_settings_[payload_type] = settings; -} - -bool DEPRECATED_VCMDecoderDataBase::DeregisterReceiveCodec( - uint8_t payload_type) { - if (decoder_settings_.erase(payload_type) == 0) { - return false; - } - if (payload_type == current_payload_type_) { - // This codec is currently in use. - current_payload_type_ = std::nullopt; - } - return true; -} - -VCMGenericDecoder* DEPRECATED_VCMDecoderDataBase::GetDecoder( - const VCMEncodedFrame& frame, - VCMDecodedFrameCallback* decoded_frame_callback) { - RTC_DCHECK_RUN_ON(&decoder_sequence_checker_); - RTC_DCHECK(decoded_frame_callback->UserReceiveCallback()); - uint8_t payload_type = frame.PayloadType(); - if (payload_type == current_payload_type_ || payload_type == 0) { - return current_decoder_.has_value() ? &*current_decoder_ : nullptr; - } - // If decoder exists - delete. - if (current_decoder_.has_value()) { - current_decoder_ = std::nullopt; - current_payload_type_ = std::nullopt; - } - - CreateAndInitDecoder(frame); - if (current_decoder_ == std::nullopt) { - return nullptr; - } - - VCMReceiveCallback* callback = decoded_frame_callback->UserReceiveCallback(); - callback->OnIncomingPayloadType(payload_type); - if (current_decoder_->RegisterDecodeCompleteCallback(decoded_frame_callback) < - 0) { - current_decoder_ = std::nullopt; - return nullptr; - } - - current_payload_type_ = payload_type; - return &*current_decoder_; -} - -void DEPRECATED_VCMDecoderDataBase::CreateAndInitDecoder( - const VCMEncodedFrame& frame) { - uint8_t payload_type = frame.PayloadType(); - RTC_LOG(LS_INFO) << "Initializing decoder with payload type '" - << int{payload_type} << "'."; - auto decoder_item = decoder_settings_.find(payload_type); - if (decoder_item == decoder_settings_.end()) { - RTC_LOG(LS_ERROR) << "Can't find a decoder associated with payload type: " - << int{payload_type}; - return; - } - auto external_dec_item = decoders_.find(payload_type); - if (external_dec_item == decoders_.end()) { - RTC_LOG(LS_ERROR) << "No decoder of this type exists."; - return; - } - current_decoder_.emplace(external_dec_item->second); - - // Copy over input resolutions to prevent codec reinitialization due to - // the first frame being of a different resolution than the database values. - // This is best effort, since there's no guarantee that width/height have been - // parsed yet (and may be zero). - RenderResolution frame_resolution(frame.EncodedImage()._encodedWidth, - frame.EncodedImage()._encodedHeight); - if (frame_resolution.Valid()) { - decoder_item->second.set_max_render_resolution(frame_resolution); - } - if (!current_decoder_->Configure(decoder_item->second)) { - current_decoder_ = std::nullopt; - RTC_LOG(LS_ERROR) << "Failed to initialize decoder."; - } -} - -} // namespace vcm - -namespace { - -class VideoCodingModuleImpl : public VideoCodingModule { - public: - explicit VideoCodingModuleImpl(const Environment& env) - : env_(env), - timing_(&env_.clock(), env_.field_trials()), - receiver_(&env_.clock(), &timing_, env_.field_trials()) {} - - ~VideoCodingModuleImpl() override = default; - - void Process() override { receiver_.Process(); } - - void RegisterReceiveCodec( - uint8_t payload_type, - const VideoDecoder::Settings& decoder_settings) override { - receiver_.RegisterReceiveCodec(payload_type, decoder_settings); - } - - void RegisterExternalDecoder(VideoDecoder* externalDecoder, - uint8_t payloadType) override { - receiver_.RegisterExternalDecoder(externalDecoder, payloadType); - } - - int32_t RegisterReceiveCallback( - VCMReceiveCallback* receiveCallback) override { - RTC_DCHECK(construction_thread_.IsCurrent()); - return receiver_.RegisterReceiveCallback(receiveCallback); - } - - int32_t RegisterFrameTypeCallback( - VCMFrameTypeCallback* frameTypeCallback) override { - return receiver_.RegisterFrameTypeCallback(frameTypeCallback); - } - - int32_t RegisterPacketRequestCallback( - VCMPacketRequestCallback* callback) override { - RTC_DCHECK(construction_thread_.IsCurrent()); - return receiver_.RegisterPacketRequestCallback(callback); - } - - int32_t Decode(uint16_t maxWaitTimeMs) override { - return receiver_.Decode(maxWaitTimeMs); - } - - int32_t IncomingPacket(const uint8_t* incomingPayload, - size_t payloadLength, - const RTPHeader& rtp_header, - const RTPVideoHeader& video_header) override { - return receiver_.IncomingPacket(incomingPayload, payloadLength, rtp_header, - video_header); - } - - void SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms) override { - return receiver_.SetNackSettings(max_nack_list_size, max_packet_age_to_nack, - max_incomplete_time_ms); - } - - private: - const Environment env_; - SequenceChecker construction_thread_; - VCMTiming timing_; - vcm::VideoReceiver receiver_; -}; -} // namespace - -// DEPRECATED. Create method for current interface, will be removed when the -// new jitter buffer is in place. -std::unique_ptr VideoCodingModule::CreateDeprecated( - const Environment& env) { - return std::make_unique(env); -} - -} // namespace webrtc diff --git a/modules/video_coding/video_coding_impl.h b/modules/video_coding/video_coding_impl.h deleted file mode 100644 index fa44e4c5b42..00000000000 --- a/modules/video_coding/video_coding_impl.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_ -#define MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_ - -#include -#include -#include -#include - -#include "api/field_trials_view.h" -#include "api/rtp_headers.h" -#include "api/sequence_checker.h" -#include "api/video_codecs/video_decoder.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "modules/video_coding/deprecated/jitter_buffer.h" -#include "modules/video_coding/deprecated/receiver.h" -#include "modules/video_coding/generic_decoder.h" -#include "modules/video_coding/include/video_coding_defines.h" -#include "modules/video_coding/timing/timing.h" -#include "rtc_base/one_time_event.h" -#include "rtc_base/synchronization/mutex.h" -#include "rtc_base/thread_annotations.h" -#include "system_wrappers/include/clock.h" - -namespace webrtc { - -class VideoBitrateAllocator; -class VideoBitrateAllocationObserver; - -namespace vcm { - -class VCMProcessTimer { - public: - static const int64_t kDefaultProcessIntervalMs = 1000; - - VCMProcessTimer(int64_t periodMs, Clock* clock) - : _clock(clock), - _periodMs(periodMs), - _latestMs(_clock->TimeInMilliseconds()) {} - int64_t Period() const; - int64_t TimeUntilProcess() const; - void Processed(); - - private: - Clock* _clock; - int64_t _periodMs; - int64_t _latestMs; -}; - -class DEPRECATED_VCMDecoderDataBase { - public: - DEPRECATED_VCMDecoderDataBase(); - DEPRECATED_VCMDecoderDataBase(const DEPRECATED_VCMDecoderDataBase&) = delete; - DEPRECATED_VCMDecoderDataBase& operator=( - const DEPRECATED_VCMDecoderDataBase&) = delete; - ~DEPRECATED_VCMDecoderDataBase() = default; - - // Returns a pointer to the previously registered decoder or nullptr if none - // was registered for the `payload_type`. - VideoDecoder* DeregisterExternalDecoder(uint8_t payload_type); - void RegisterExternalDecoder(uint8_t payload_type, - VideoDecoder* external_decoder); - bool IsExternalDecoderRegistered(uint8_t payload_type) const; - - void RegisterReceiveCodec(uint8_t payload_type, - const VideoDecoder::Settings& settings); - bool DeregisterReceiveCodec(uint8_t payload_type); - - // Returns a decoder specified by frame.PayloadType. The decoded frame - // callback of the decoder is set to `decoded_frame_callback`. If no such - // decoder already exists an instance will be created and initialized. - // nullptr is returned if no decoder with the specified payload type was found - // and the function failed to create one. - VCMGenericDecoder* GetDecoder( - const VCMEncodedFrame& frame, - VCMDecodedFrameCallback* decoded_frame_callback); - - private: - void CreateAndInitDecoder(const VCMEncodedFrame& frame) - RTC_RUN_ON(decoder_sequence_checker_); - - SequenceChecker decoder_sequence_checker_; - - std::optional current_payload_type_; - std::optional current_decoder_ - RTC_GUARDED_BY(decoder_sequence_checker_); - // Initialization paramaters for decoders keyed by payload type. - std::map decoder_settings_; - // Decoders keyed by payload type. - std::map decoders_ - RTC_GUARDED_BY(decoder_sequence_checker_); -}; - -class VideoReceiver { - public: - VideoReceiver(Clock* clock, - VCMTiming* timing, - const FieldTrialsView& field_trials); - ~VideoReceiver(); - - void RegisterReceiveCodec(uint8_t payload_type, - const VideoDecoder::Settings& settings); - - void RegisterExternalDecoder(VideoDecoder* externalDecoder, - uint8_t payloadType); - int32_t RegisterReceiveCallback(VCMReceiveCallback* receiveCallback); - int32_t RegisterFrameTypeCallback(VCMFrameTypeCallback* frameTypeCallback); - int32_t RegisterPacketRequestCallback(VCMPacketRequestCallback* callback); - - int32_t Decode(uint16_t maxWaitTimeMs); - - int32_t IncomingPacket(const uint8_t* incomingPayload, - size_t payloadLength, - const RTPHeader& rtp_header, - const RTPVideoHeader& video_header); - - void SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms); - - void Process(); - - protected: - int32_t Decode(const webrtc::VCMEncodedFrame& frame); - int32_t RequestKeyFrame(); - - private: - // Used for DCHECKing thread correctness. - // In build where DCHECKs are enabled, will return false before - // DecoderThreadStarting is called, then true until DecoderThreadStopped - // is called. - // In builds where DCHECKs aren't enabled, it will return true. - bool IsDecoderThreadRunning(); - - SequenceChecker construction_thread_checker_; - SequenceChecker decoder_thread_checker_; - SequenceChecker module_thread_checker_; - Clock* const clock_; - Mutex process_mutex_; - VCMTiming* _timing; - VCMReceiver _receiver; - VCMDecodedFrameCallback _decodedFrameCallback; - - // These callbacks are set on the construction thread before being attached - // to the module thread or decoding started, so a lock is not required. - VCMFrameTypeCallback* _frameTypeCallback; - VCMPacketRequestCallback* _packetRequestCallback; - - // Used on both the module and decoder thread. - bool _scheduleKeyRequest RTC_GUARDED_BY(process_mutex_); - bool drop_frames_until_keyframe_ RTC_GUARDED_BY(process_mutex_); - - // Modified on the construction thread while not attached to the process - // thread. Once attached to the process thread, its value is only read - // so a lock is not required. - size_t max_nack_list_size_; - - // Callbacks are set before the decoder thread starts. - // Once the decoder thread has been started, usage of `_codecDataBase` moves - // over to the decoder thread. - DEPRECATED_VCMDecoderDataBase _codecDataBase; - - VCMProcessTimer _retransmissionTimer RTC_GUARDED_BY(module_thread_checker_); - VCMProcessTimer _keyRequestTimer RTC_GUARDED_BY(module_thread_checker_); - ThreadUnsafeOneTimeEvent first_frame_received_ - RTC_GUARDED_BY(decoder_thread_checker_); -}; - -} // namespace vcm -} // namespace webrtc -#endif // MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_ diff --git a/modules/video_coding/video_receiver.cc b/modules/video_coding/video_receiver.cc deleted file mode 100644 index 53b922aa4bd..00000000000 --- a/modules/video_coding/video_receiver.cc +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "api/field_trials_view.h" -#include "api/rtp_headers.h" -#include "api/sequence_checker.h" -#include "api/units/timestamp.h" -#include "api/video/video_frame_type.h" -#include "api/video_codecs/video_decoder.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "modules/video_coding/deprecated/jitter_buffer.h" -#include "modules/video_coding/deprecated/packet.h" -#include "modules/video_coding/deprecated/receiver.h" -#include "modules/video_coding/encoded_frame.h" -#include "modules/video_coding/generic_decoder.h" -#include "modules/video_coding/include/video_coding_defines.h" -#include "modules/video_coding/internal_defines.h" -#include "modules/video_coding/timing/timing.h" -#include "modules/video_coding/video_coding_impl.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/one_time_event.h" -#include "rtc_base/synchronization/mutex.h" -#include "rtc_base/trace_event.h" -#include "system_wrappers/include/clock.h" - -namespace webrtc { -namespace vcm { - -VideoReceiver::VideoReceiver(Clock* clock, - VCMTiming* timing, - const FieldTrialsView& field_trials) - : clock_(clock), - _timing(timing), - _receiver(_timing, clock_, field_trials), - _decodedFrameCallback(_timing, - clock_, - field_trials, - /*corruption_score_calculator=*/nullptr), - _frameTypeCallback(nullptr), - _packetRequestCallback(nullptr), - _scheduleKeyRequest(false), - drop_frames_until_keyframe_(false), - max_nack_list_size_(0), - _codecDataBase(), - _retransmissionTimer(10, clock_), - _keyRequestTimer(500, clock_) { - decoder_thread_checker_.Detach(); - module_thread_checker_.Detach(); -} - -VideoReceiver::~VideoReceiver() { - RTC_DCHECK_RUN_ON(&construction_thread_checker_); -} - -void VideoReceiver::Process() { - RTC_DCHECK_RUN_ON(&module_thread_checker_); - - // Key frame requests - if (_keyRequestTimer.TimeUntilProcess() == 0) { - _keyRequestTimer.Processed(); - bool request_key_frame = _frameTypeCallback != nullptr; - if (request_key_frame) { - MutexLock lock(&process_mutex_); - request_key_frame = _scheduleKeyRequest; - } - if (request_key_frame) - RequestKeyFrame(); - } - - // Packet retransmission requests - // TODO(holmer): Add API for changing Process interval and make sure it's - // disabled when NACK is off. - if (_retransmissionTimer.TimeUntilProcess() == 0) { - _retransmissionTimer.Processed(); - bool callback_registered = _packetRequestCallback != nullptr; - uint16_t length = max_nack_list_size_; - if (callback_registered && length > 0) { - // Collect sequence numbers from the default receiver. - bool request_key_frame = false; - std::vector nackList = _receiver.NackList(&request_key_frame); - int32_t ret = VCM_OK; - if (request_key_frame) { - ret = RequestKeyFrame(); - } - if (ret == VCM_OK && !nackList.empty()) { - MutexLock lock(&process_mutex_); - if (_packetRequestCallback != nullptr) { - _packetRequestCallback->ResendPackets(&nackList[0], nackList.size()); - } - } - } - } -} - -// Register a receive callback. Will be called whenever there is a new frame -// ready for rendering. -int32_t VideoReceiver::RegisterReceiveCallback( - VCMReceiveCallback* receiveCallback) { - RTC_DCHECK_RUN_ON(&construction_thread_checker_); - // This value is set before the decoder thread starts and unset after - // the decoder thread has been stopped. - _decodedFrameCallback.SetUserReceiveCallback(receiveCallback); - return VCM_OK; -} - -// Register an externally defined decoder object. -void VideoReceiver::RegisterExternalDecoder(VideoDecoder* externalDecoder, - uint8_t payloadType) { - RTC_DCHECK_RUN_ON(&construction_thread_checker_); - if (externalDecoder == nullptr) { - RTC_CHECK(_codecDataBase.DeregisterExternalDecoder(payloadType)); - return; - } - _codecDataBase.RegisterExternalDecoder(payloadType, externalDecoder); -} - -// Register a frame type request callback. -int32_t VideoReceiver::RegisterFrameTypeCallback( - VCMFrameTypeCallback* frameTypeCallback) { - RTC_DCHECK_RUN_ON(&construction_thread_checker_); - // This callback is used on the module thread, but since we don't get - // callbacks on the module thread while the decoder thread isn't running - // (and this function must not be called when the decoder is running), - // we don't need a lock here. - _frameTypeCallback = frameTypeCallback; - return VCM_OK; -} - -int32_t VideoReceiver::RegisterPacketRequestCallback( - VCMPacketRequestCallback* callback) { - RTC_DCHECK_RUN_ON(&construction_thread_checker_); - // This callback is used on the module thread, but since we don't get - // callbacks on the module thread while the decoder thread isn't running - // (and this function must not be called when the decoder is running), - // we don't need a lock here. - _packetRequestCallback = callback; - return VCM_OK; -} - -// Decode next frame, blocking. -// Should be called as often as possible to get the most out of the decoder. -int32_t VideoReceiver::Decode(uint16_t maxWaitTimeMs) { - RTC_DCHECK_RUN_ON(&decoder_thread_checker_); - VCMEncodedFrame* frame = _receiver.FrameForDecoding(maxWaitTimeMs, true); - - if (!frame) - return VCM_FRAME_NOT_READY; - - bool drop_frame = false; - { - MutexLock lock(&process_mutex_); - if (drop_frames_until_keyframe_) { - // Still getting delta frames, schedule another keyframe request as if - // decode failed. - if (frame->FrameType() != VideoFrameType::kVideoFrameKey) { - drop_frame = true; - _scheduleKeyRequest = true; - } else { - drop_frames_until_keyframe_ = false; - } - } - } - - if (drop_frame) { - _receiver.ReleaseFrame(frame); - return VCM_FRAME_NOT_READY; - } - - // If this frame was too late, we should adjust the delay accordingly - if (frame->RenderTimeMs() > 0) - _timing->UpdateCurrentDelay(Timestamp::Millis(frame->RenderTimeMs()), - clock_->CurrentTime()); - - if (first_frame_received_()) { - RTC_LOG(LS_INFO) << "Received first complete decodable video frame"; - } - - const int32_t ret = Decode(*frame); - _receiver.ReleaseFrame(frame); - return ret; -} - -int32_t VideoReceiver::RequestKeyFrame() { - RTC_DCHECK_RUN_ON(&module_thread_checker_); - - TRACE_EVENT0("webrtc", "RequestKeyFrame"); - if (_frameTypeCallback != nullptr) { - const int32_t ret = _frameTypeCallback->RequestKeyFrame(); - if (ret < 0) { - return ret; - } - MutexLock lock(&process_mutex_); - _scheduleKeyRequest = false; - } else { - return VCM_MISSING_CALLBACK; - } - return VCM_OK; -} - -// Must be called from inside the receive side critical section. -int32_t VideoReceiver::Decode(const VCMEncodedFrame& frame) { - RTC_DCHECK_RUN_ON(&decoder_thread_checker_); - TRACE_EVENT0("webrtc", "VideoReceiver::Decode"); - // Change decoder if payload type has changed - VCMGenericDecoder* decoder = - _codecDataBase.GetDecoder(frame, &_decodedFrameCallback); - if (decoder == nullptr) { - return VCM_NO_CODEC_REGISTERED; - } - return decoder->Decode(frame, clock_->CurrentTime()); -} - -// Register possible receive codecs, can be called multiple times -void VideoReceiver::RegisterReceiveCodec( - uint8_t payload_type, - const VideoDecoder::Settings& settings) { - RTC_DCHECK_RUN_ON(&construction_thread_checker_); - _codecDataBase.RegisterReceiveCodec(payload_type, settings); -} - -// Incoming packet from network parsed and ready for decode, non blocking. -int32_t VideoReceiver::IncomingPacket(const uint8_t* incomingPayload, - size_t payloadLength, - const RTPHeader& rtp_header, - const RTPVideoHeader& video_header) { - RTC_DCHECK_RUN_ON(&module_thread_checker_); - if (video_header.frame_type == VideoFrameType::kVideoFrameKey) { - TRACE_EVENT1("webrtc", "VCM::PacketKeyFrame", "seqnum", - rtp_header.sequenceNumber); - } - if (incomingPayload == nullptr) { - // The jitter buffer doesn't handle non-zero payload lengths for packets - // without payload. - // TODO(holmer): We should fix this in the jitter buffer. - payloadLength = 0; - } - // Callers don't provide any ntp time. - const VCMPacket packet(incomingPayload, payloadLength, rtp_header, - video_header, /*ntp_time_ms=*/0, - clock_->CurrentTime()); - int32_t ret = _receiver.InsertPacket(packet); - - // TODO(holmer): Investigate if this somehow should use the key frame - // request scheduling to throttle the requests. - if (ret == VCM_FLUSH_INDICATOR) { - { - MutexLock lock(&process_mutex_); - drop_frames_until_keyframe_ = true; - } - RequestKeyFrame(); - } else if (ret < 0) { - return ret; - } - return VCM_OK; -} - -void VideoReceiver::SetNackSettings(size_t max_nack_list_size, - int max_packet_age_to_nack, - int max_incomplete_time_ms) { - RTC_DCHECK_RUN_ON(&construction_thread_checker_); - if (max_nack_list_size != 0) { - max_nack_list_size_ = max_nack_list_size; - } - _receiver.SetNackSettings(max_nack_list_size, max_packet_age_to_nack, - max_incomplete_time_ms); -} - -} // namespace vcm -} // namespace webrtc diff --git a/modules/video_coding/video_receiver_unittest.cc b/modules/video_coding/video_receiver_unittest.cc deleted file mode 100644 index 0e3036d0aa2..00000000000 --- a/modules/video_coding/video_receiver_unittest.cc +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "api/field_trials.h" -#include "api/rtp_headers.h" -#include "api/test/mock_video_decoder.h" -#include "api/video/video_codec_type.h" -#include "api/video/video_frame_type.h" -#include "api/video_codecs/video_decoder.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "modules/video_coding/codecs/vp8/include/vp8_globals.h" -#include "modules/video_coding/include/video_coding_defines.h" -#include "modules/video_coding/timing/timing.h" -#include "modules/video_coding/video_coding_impl.h" -#include "system_wrappers/include/clock.h" -#include "test/create_test_field_trials.h" -#include "test/gmock.h" -#include "test/gtest.h" - -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::NiceMock; - -namespace webrtc { -namespace vcm { -namespace { - -class MockPacketRequestCallback : public VCMPacketRequestCallback { - public: - MOCK_METHOD(int32_t, - ResendPackets, - (const uint16_t* sequenceNumbers, uint16_t length), - (override)); -}; - -class MockVCMReceiveCallback : public VCMReceiveCallback { - public: - MockVCMReceiveCallback() {} - ~MockVCMReceiveCallback() override {} - - MOCK_METHOD(int32_t, OnFrameToRender, (const FrameToRender&), (override)); - MOCK_METHOD(void, OnIncomingPayloadType, (int), (override)); - MOCK_METHOD(void, - OnDecoderInfoChanged, - (const VideoDecoder::DecoderInfo&), - (override)); -}; - -class TestVideoReceiver : public ::testing::Test { - protected: - static const int kUnusedPayloadType = 10; - static const uint16_t kMaxWaitTimeMs = 100; - - TestVideoReceiver() - : field_trials_(CreateTestFieldTrials()), - clock_(0), - timing_(&clock_, field_trials_), - receiver_(&clock_, &timing_, field_trials_) {} - - void SetUp() override { - // Register decoder. - receiver_.RegisterExternalDecoder(&decoder_, kUnusedPayloadType); - VideoDecoder::Settings settings; - settings.set_codec_type(kVideoCodecVP8); - receiver_.RegisterReceiveCodec(kUnusedPayloadType, settings); - - // Set protection mode. - const size_t kMaxNackListSize = 250; - const int kMaxPacketAgeToNack = 450; - receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, 0); - EXPECT_EQ( - 0, receiver_.RegisterPacketRequestCallback(&packet_request_callback_)); - - // Since we call Decode, we need to provide a valid receive callback. - // However, for the purposes of these tests, we ignore the callbacks. - EXPECT_CALL(receive_callback_, OnIncomingPayloadType(_)).Times(AnyNumber()); - EXPECT_CALL(receive_callback_, OnDecoderInfoChanged).Times(AnyNumber()); - receiver_.RegisterReceiveCallback(&receive_callback_); - } - - RTPHeader GetDefaultRTPHeader() const { - RTPHeader header; - header.markerBit = false; - header.payloadType = kUnusedPayloadType; - header.ssrc = 1; - header.headerLength = 12; - return header; - } - - RTPVideoHeader GetDefaultVp8Header() const { - RTPVideoHeader video_header = {}; - video_header.frame_type = VideoFrameType::kEmptyFrame; - video_header.codec = kVideoCodecVP8; - return video_header; - } - - void InsertAndVerifyPaddingFrame(const uint8_t* payload, - RTPHeader* header, - const RTPVideoHeader& video_header) { - for (int j = 0; j < 5; ++j) { - // Padding only packets are passed to the VCM with payload size 0. - EXPECT_EQ(0, receiver_.IncomingPacket(payload, 0, *header, video_header)); - ++header->sequenceNumber; - } - receiver_.Process(); - EXPECT_CALL(decoder_, Decode(_, _)).Times(0); - EXPECT_EQ(VCM_FRAME_NOT_READY, receiver_.Decode(kMaxWaitTimeMs)); - } - - void InsertAndVerifyDecodableFrame(const uint8_t* payload, - size_t length, - RTPHeader* header, - const RTPVideoHeader& video_header) { - EXPECT_EQ(0, - receiver_.IncomingPacket(payload, length, *header, video_header)); - ++header->sequenceNumber; - EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)).Times(0); - - receiver_.Process(); - EXPECT_CALL(decoder_, Decode(_, _)).Times(1); - EXPECT_EQ(0, receiver_.Decode(kMaxWaitTimeMs)); - } - - FieldTrials field_trials_; - SimulatedClock clock_; - NiceMock decoder_; - NiceMock packet_request_callback_; - VCMTiming timing_; - MockVCMReceiveCallback receive_callback_; - VideoReceiver receiver_; -}; - -TEST_F(TestVideoReceiver, PaddingOnlyFrames) { - const size_t kPaddingSize = 220; - const uint8_t kPayload[kPaddingSize] = {0}; - RTPHeader header = GetDefaultRTPHeader(); - RTPVideoHeader video_header = GetDefaultVp8Header(); - header.paddingLength = kPaddingSize; - for (int i = 0; i < 10; ++i) { - EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)).Times(0); - InsertAndVerifyPaddingFrame(kPayload, &header, video_header); - clock_.AdvanceTimeMilliseconds(33); - header.timestamp += 3000; - } -} - -TEST_F(TestVideoReceiver, PaddingOnlyFramesWithLosses) { - const size_t kFrameSize = 1200; - const size_t kPaddingSize = 220; - const uint8_t kPayload[kFrameSize] = {0}; - RTPHeader header = GetDefaultRTPHeader(); - RTPVideoHeader video_header = GetDefaultVp8Header(); - header.paddingLength = kPaddingSize; - video_header.video_type_header.emplace(); - - // Insert one video frame to get one frame decoded. - video_header.frame_type = VideoFrameType::kVideoFrameKey; - video_header.is_first_packet_in_frame = true; - header.markerBit = true; - InsertAndVerifyDecodableFrame(kPayload, kFrameSize, &header, video_header); - - clock_.AdvanceTimeMilliseconds(33); - header.timestamp += 3000; - video_header.frame_type = VideoFrameType::kEmptyFrame; - video_header.is_first_packet_in_frame = false; - header.markerBit = false; - // Insert padding frames. - for (int i = 0; i < 10; ++i) { - // Lose one packet from the 6th frame. - if (i == 5) { - ++header.sequenceNumber; - } - // Lose the 4th frame. - if (i == 3) { - header.sequenceNumber += 5; - } else { - if (i > 3 && i < 5) { - EXPECT_CALL(packet_request_callback_, ResendPackets(_, 5)).Times(1); - } else if (i >= 5) { - EXPECT_CALL(packet_request_callback_, ResendPackets(_, 6)).Times(1); - } else { - EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)).Times(0); - } - InsertAndVerifyPaddingFrame(kPayload, &header, video_header); - } - clock_.AdvanceTimeMilliseconds(33); - header.timestamp += 3000; - } -} - -TEST_F(TestVideoReceiver, PaddingOnlyAndVideo) { - const size_t kFrameSize = 1200; - const size_t kPaddingSize = 220; - const uint8_t kPayload[kFrameSize] = {0}; - RTPHeader header = GetDefaultRTPHeader(); - RTPVideoHeader video_header = GetDefaultVp8Header(); - video_header.is_first_packet_in_frame = false; - header.paddingLength = kPaddingSize; - auto& vp8_header = - video_header.video_type_header.emplace(); - vp8_header.pictureId = -1; - vp8_header.tl0PicIdx = -1; - - for (int i = 0; i < 3; ++i) { - // Insert 2 video frames. - for (int j = 0; j < 2; ++j) { - if (i == 0 && j == 0) // First frame should be a key frame. - video_header.frame_type = VideoFrameType::kVideoFrameKey; - else - video_header.frame_type = VideoFrameType::kVideoFrameDelta; - video_header.is_first_packet_in_frame = true; - header.markerBit = true; - InsertAndVerifyDecodableFrame(kPayload, kFrameSize, &header, - video_header); - clock_.AdvanceTimeMilliseconds(33); - header.timestamp += 3000; - } - - // Insert 2 padding only frames. - video_header.frame_type = VideoFrameType::kEmptyFrame; - video_header.is_first_packet_in_frame = false; - header.markerBit = false; - for (int j = 0; j < 2; ++j) { - // InsertAndVerifyPaddingFrame(kPayload, &header); - clock_.AdvanceTimeMilliseconds(33); - header.timestamp += 3000; - } - } -} - -} // namespace -} // namespace vcm -} // namespace webrtc diff --git a/net/dcsctp/DIR_METADATA b/net/dcsctp/DIR_METADATA new file mode 100644 index 00000000000..afb0cad4eee --- /dev/null +++ b/net/dcsctp/DIR_METADATA @@ -0,0 +1,3 @@ +buganizer_public: { + component_id: 1565733 # DataChannel +} \ No newline at end of file diff --git a/net/dcsctp/OWNERS b/net/dcsctp/OWNERS index 06a0f861796..af58d192470 100644 --- a/net/dcsctp/OWNERS +++ b/net/dcsctp/OWNERS @@ -1,2 +1,2 @@ boivie@webrtc.org -orphis@webrtc.org +danilchap@webrtc.org diff --git a/net/dcsctp/common/BUILD.gn b/net/dcsctp/common/BUILD.gn index 5e2a32daf74..ab4ec02ba2b 100644 --- a/net/dcsctp/common/BUILD.gn +++ b/net/dcsctp/common/BUILD.gn @@ -9,10 +9,7 @@ import("../../../webrtc.gni") rtc_source_set("internal_types") { - deps = [ - "../../../rtc_base:strong_alias", - "../public:types", - ] + deps = [ "../../../rtc_base:strong_alias" ] sources = [ "internal_types.h" ] } @@ -37,9 +34,6 @@ if (rtc_include_tests) { deps = [ ":math", ":sequence_numbers", - "../../../api:array_view", - "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../rtc_base:strong_alias", "../../../test:test_support", ] diff --git a/net/dcsctp/fuzzers/BUILD.gn b/net/dcsctp/fuzzers/BUILD.gn index a79de79803e..8daabb81e76 100644 --- a/net/dcsctp/fuzzers/BUILD.gn +++ b/net/dcsctp/fuzzers/BUILD.gn @@ -11,13 +11,10 @@ import("../../../webrtc.gni") rtc_library("dcsctp_fuzzers") { testonly = true deps = [ - "../../../api:array_view", "../../../api/task_queue", "../../../api/units:timestamp", "../../../rtc_base:checks", - "../../../rtc_base:logging", "../common:internal_types", - "../common:math", "../packet:chunk", "../packet:data", "../packet:error_cause", @@ -40,16 +37,9 @@ if (rtc_include_tests) { deps = [ ":dcsctp_fuzzers", - "../../../api:array_view", - "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", - "../../../rtc_base:logging", "../../../test:test_support", - "../packet:sctp_packet", - "../public:socket", "../public:types", "../socket:dcsctp_socket", - "../testing:testing_macros", ] sources = [ "dcsctp_fuzzers_test.cc" ] } diff --git a/net/dcsctp/fuzzers/dcsctp_fuzzers.cc b/net/dcsctp/fuzzers/dcsctp_fuzzers.cc index 90ad3f179d1..9a732db4402 100644 --- a/net/dcsctp/fuzzers/dcsctp_fuzzers.cc +++ b/net/dcsctp/fuzzers/dcsctp_fuzzers.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/abort_chunk.h" #include "net/dcsctp/packet/chunk/chunk.h" @@ -82,7 +82,7 @@ enum class StartingState : int { // State about the current fuzzing iteration class FuzzState { public: - explicit FuzzState(webrtc::ArrayView data) : data_(data) {} + explicit FuzzState(std::span data) : data_(data) {} uint8_t GetByte() { uint8_t value = 0; @@ -101,7 +101,7 @@ class FuzzState { private: uint32_t tsn_ = kRandomValue; uint32_t mid_ = 0; - webrtc::ArrayView data_; + std::span data_; size_t offset_ = 0; }; @@ -419,7 +419,7 @@ std::vector GeneratePacket(FuzzState& state) { void FuzzSocket(DcSctpSocketInterface& socket, FuzzerCallbacks& cb, - webrtc::ArrayView data) { + std::span data) { if (data.size() < kMinInputLength || data.size() > kMaxInputLength) { return; } @@ -430,7 +430,7 @@ void FuzzSocket(DcSctpSocketInterface& socket, // Set the socket in a specified valid starting state SetSocketState(socket, cb, static_cast(data[0])); - FuzzState state(data.subview(1)); + FuzzState state(data.subspan(1)); while (!state.empty()) { switch (state.GetByte()) { diff --git a/net/dcsctp/fuzzers/dcsctp_fuzzers.h b/net/dcsctp/fuzzers/dcsctp_fuzzers.h index 62f7b7f0b44..bc4afc682f5 100644 --- a/net/dcsctp/fuzzers/dcsctp_fuzzers.h +++ b/net/dcsctp/fuzzers/dcsctp_fuzzers.h @@ -17,10 +17,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "api/units/timestamp.h" #include "net/dcsctp/public/dcsctp_message.h" @@ -66,7 +66,7 @@ class FuzzerTimeout : public Timeout { class FuzzerCallbacks : public DcSctpSocketCallbacks { public: static constexpr int kRandomValue = 42; - void SendPacket(webrtc::ArrayView data) override { + void SendPacket(std::span data) override { sent_packets_.emplace_back(std::vector(data.begin(), data.end())); } std::unique_ptr CreateTimeout( @@ -86,13 +86,12 @@ class FuzzerCallbacks : public DcSctpSocketCallbacks { void OnConnected() override {} void OnClosed() override {} void OnConnectionRestarted() override {} - void OnStreamsResetFailed( - webrtc::ArrayView /* outgoing_streams */, - absl::string_view /* reason */) override {} + void OnStreamsResetFailed(std::span /* outgoing_streams */, + absl::string_view /* reason */) override {} void OnStreamsResetPerformed( - webrtc::ArrayView outgoing_streams) override {} + std::span outgoing_streams) override {} void OnIncomingStreamsReset( - webrtc::ArrayView incoming_streams) override {} + std::span incoming_streams) override {} std::vector ConsumeSentPacket() { if (sent_packets_.empty()) { @@ -125,7 +124,7 @@ class FuzzerCallbacks : public DcSctpSocketCallbacks { // API methods. void FuzzSocket(DcSctpSocketInterface& socket, FuzzerCallbacks& cb, - webrtc::ArrayView data); + std::span data); } // namespace dcsctp_fuzzers } // namespace dcsctp diff --git a/net/dcsctp/fuzzers/dcsctp_fuzzers_test.cc b/net/dcsctp/fuzzers/dcsctp_fuzzers_test.cc index bbb00d72baf..b1b0d50a6be 100644 --- a/net/dcsctp/fuzzers/dcsctp_fuzzers_test.cc +++ b/net/dcsctp/fuzzers/dcsctp_fuzzers_test.cc @@ -11,7 +11,6 @@ #include -#include "api/array_view.h" #include "net/dcsctp/public/dcsctp_options.h" #include "net/dcsctp/socket/dcsctp_socket.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/BUILD.gn b/net/dcsctp/packet/BUILD.gn index 651f9c17109..7b8d0876ac4 100644 --- a/net/dcsctp/packet/BUILD.gn +++ b/net/dcsctp/packet/BUILD.gn @@ -13,10 +13,7 @@ group("packet") { } rtc_source_set("bounded_io") { - deps = [ - "../../../api:array_view", - "../../../rtc_base:checks", - ] + deps = [ "../../../rtc_base:checks" ] sources = [ "bounded_byte_reader.h", "bounded_byte_writer.h", @@ -26,8 +23,6 @@ rtc_source_set("bounded_io") { rtc_library("tlv_trait") { deps = [ ":bounded_io", - "../../../api:array_view", - "../../../rtc_base:checks", "../../../rtc_base:logging", ] sources = [ @@ -38,7 +33,6 @@ rtc_library("tlv_trait") { rtc_source_set("data") { deps = [ - "../../../rtc_base:checks", "../../../rtc_base:strong_alias", "../common:internal_types", "../public:types", @@ -48,9 +42,9 @@ rtc_source_set("data") { rtc_library("crc32c") { deps = [ - "../../../api:array_view", - "../../../rtc_base:checks", - "//third_party/crc32c", + "//third_party/abseil-cpp/absl/crc:crc32c", + "//third_party/abseil-cpp/absl/numeric:bits", + "//third_party/abseil-cpp/absl/strings:string_view", ] sources = [ "crc32c.cc", @@ -61,10 +55,7 @@ rtc_library("crc32c") { rtc_library("parameter") { deps = [ ":bounded_io", - ":data", ":tlv_trait", - "../../../api:array_view", - "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:stringutils", "../common:internal_types", @@ -104,16 +95,12 @@ rtc_library("parameter") { rtc_library("error_cause") { deps = [ - ":data", + ":bounded_io", ":parameter", ":tlv_trait", - "../../../api:array_view", - "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:stringutils", "../common:internal_types", - "../common:math", - "../packet:bounded_io", "../public:types", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/strings:string_view", @@ -152,18 +139,15 @@ rtc_library("error_cause") { rtc_library("chunk") { deps = [ + ":bounded_io", ":data", - ":error_cause", ":parameter", ":tlv_trait", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:stringutils", "../../../rtc_base:strong_alias", "../common:internal_types", - "../common:math", - "../packet:bounded_io", "../public:types", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/strings:string_view", @@ -213,7 +197,6 @@ rtc_library("chunk") { rtc_library("chunk_validators") { deps = [ ":chunk", - "../../../rtc_base:checks", "../../../rtc_base:logging", "//third_party/abseil-cpp/absl/algorithm:container", ] @@ -228,7 +211,6 @@ rtc_library("sctp_packet") { ":bounded_io", ":chunk", ":crc32c", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:stringutils", @@ -257,10 +239,6 @@ if (rtc_include_tests) { ":parameter", ":sctp_packet", ":tlv_trait", - "../../../api:array_view", - "../../../rtc_base:buffer", - "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", "../common:internal_types", "../common:math", diff --git a/net/dcsctp/packet/bounded_byte_reader.h b/net/dcsctp/packet/bounded_byte_reader.h index 26f4ff91fcd..4b25bd71ed2 100644 --- a/net/dcsctp/packet/bounded_byte_reader.h +++ b/net/dcsctp/packet/bounded_byte_reader.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace dcsctp { @@ -39,7 +39,7 @@ inline uint32_t LoadBigEndian32(const uint8_t* data) { } } // namespace internal -// BoundedByteReader wraps an ArrayView and divides it into two parts; A fixed +// BoundedByteReader wraps an std::span and divides it into two parts; A fixed // size - which is the template parameter - and a variable size, which is what // remains in `data` after the `FixedSize`. // @@ -49,12 +49,11 @@ inline uint32_t LoadBigEndian32(const uint8_t* data) { // // The variable sized portion can either be used to create sub-readers, which // themselves would provide compile-time bounds-checking, or the entire variable -// sized portion can be retrieved as an ArrayView. +// sized portion can be retrieved as an std::span. template class BoundedByteReader { public: - explicit BoundedByteReader(webrtc::ArrayView data) - : data_(data) { + explicit BoundedByteReader(std::span data) : data_(data) { RTC_CHECK(data.size() >= FixedSize); } @@ -82,19 +81,19 @@ class BoundedByteReader { BoundedByteReader sub_reader(size_t variable_offset) const { RTC_CHECK(FixedSize + variable_offset + SubSize <= data_.size()); - webrtc::ArrayView sub_span = - data_.subview(FixedSize + variable_offset, SubSize); + std::span sub_span = + data_.subspan(FixedSize + variable_offset, SubSize); return BoundedByteReader(sub_span); } size_t variable_data_size() const { return data_.size() - FixedSize; } - webrtc::ArrayView variable_data() const { - return data_.subview(FixedSize, data_.size() - FixedSize); + std::span variable_data() const { + return data_.subspan(FixedSize); } private: - const webrtc::ArrayView data_; + const std::span data_; }; } // namespace dcsctp diff --git a/net/dcsctp/packet/bounded_byte_reader_test.cc b/net/dcsctp/packet/bounded_byte_reader_test.cc index 6aaeb01a262..9ec798aea07 100644 --- a/net/dcsctp/packet/bounded_byte_reader_test.cc +++ b/net/dcsctp/packet/bounded_byte_reader_test.cc @@ -12,7 +12,6 @@ #include -#include "api/array_view.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/bounded_byte_writer.h b/net/dcsctp/packet/bounded_byte_writer.h index 738b35bd786..e124f3300a5 100644 --- a/net/dcsctp/packet/bounded_byte_writer.h +++ b/net/dcsctp/packet/bounded_byte_writer.h @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" namespace dcsctp { @@ -45,7 +45,7 @@ inline void StoreBigEndian32(uint8_t* data, uint32_t val) { } } // namespace internal -// BoundedByteWriter wraps an ArrayView and divides it into two parts; A fixed +// BoundedByteWriter wraps an std::span and divides it into two parts; A fixed // size - which is the template parameter - and a variable size, which is what // remains in `data` after the `FixedSize`. // @@ -59,7 +59,7 @@ inline void StoreBigEndian32(uint8_t* data, uint32_t val) { template class BoundedByteWriter { public: - explicit BoundedByteWriter(webrtc::ArrayView data) : data_(data) { + explicit BoundedByteWriter(std::span data) : data_(data) { RTC_CHECK(data.size() >= FixedSize); } @@ -88,10 +88,10 @@ class BoundedByteWriter { RTC_CHECK(FixedSize + variable_offset + SubSize <= data_.size()); return BoundedByteWriter( - data_.subview(FixedSize + variable_offset, SubSize)); + data_.subspan(FixedSize + variable_offset, SubSize)); } - void CopyToVariableData(webrtc::ArrayView source) { + void CopyToVariableData(std::span source) { size_t copy_size = std::min(source.size(), data_.size() - FixedSize); if (source.data() == nullptr || copy_size == 0) { return; @@ -100,7 +100,7 @@ class BoundedByteWriter { } private: - webrtc::ArrayView data_; + std::span data_; }; } // namespace dcsctp diff --git a/net/dcsctp/packet/bounded_byte_writer_test.cc b/net/dcsctp/packet/bounded_byte_writer_test.cc index e1591897bf1..8128f768dce 100644 --- a/net/dcsctp/packet/bounded_byte_writer_test.cc +++ b/net/dcsctp/packet/bounded_byte_writer_test.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/chunk/abort_chunk.cc b/net/dcsctp/packet/chunk/abort_chunk.cc index 47daa56cf21..d15a6ec7c47 100644 --- a/net/dcsctp/packet/chunk/abort_chunk.cc +++ b/net/dcsctp/packet/chunk/abort_chunk.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -34,8 +34,7 @@ namespace dcsctp { // \ \ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -std::optional AbortChunk::Parse( - webrtc::ArrayView data) { +std::optional AbortChunk::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -51,7 +50,7 @@ std::optional AbortChunk::Parse( } void AbortChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView error_causes = error_causes_.data(); + std::span error_causes = error_causes_.data(); BoundedByteWriter writer = AllocateTLV(out, error_causes.size()); writer.Store8<1>(filled_in_verification_tag_ ? 0 : (1 << kFlagsBitT)); writer.CopyToVariableData(error_causes); diff --git a/net/dcsctp/packet/chunk/abort_chunk.h b/net/dcsctp/packet/chunk/abort_chunk.h index 772c52087ad..c7356d2663a 100644 --- a/net/dcsctp/packet/chunk/abort_chunk.h +++ b/net/dcsctp/packet/chunk/abort_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -42,7 +42,7 @@ class AbortChunk : public Chunk, public TLVTrait { AbortChunk(AbortChunk&& other) = default; AbortChunk& operator=(AbortChunk&& other) = default; - static std::optional Parse(webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/chunk.cc b/net/dcsctp/packet/chunk/chunk.cc index 9332f37c5b0..5ef0091af83 100644 --- a/net/dcsctp/packet/chunk/chunk.cc +++ b/net/dcsctp/packet/chunk/chunk.cc @@ -11,9 +11,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/abort_chunk.h" #include "net/dcsctp/packet/chunk/cookie_ack_chunk.h" #include "net/dcsctp/packet/chunk/cookie_echo_chunk.h" @@ -37,7 +37,7 @@ namespace dcsctp { template bool ParseAndPrint(uint8_t chunk_type, - webrtc::ArrayView data, + std::span data, webrtc::StringBuilder& sb) { if (chunk_type == Chunk::kType) { std::optional c = Chunk::Parse(data); @@ -51,7 +51,7 @@ bool ParseAndPrint(uint8_t chunk_type, return false; } -std::string DebugConvertChunkToString(webrtc::ArrayView data) { +std::string DebugConvertChunkToString(std::span data) { webrtc::StringBuilder sb; if (data.empty()) { diff --git a/net/dcsctp/packet/chunk/chunk.h b/net/dcsctp/packet/chunk/chunk.h index 3eac270ac99..8d5b3c787a0 100644 --- a/net/dcsctp/packet/chunk/chunk.h +++ b/net/dcsctp/packet/chunk/chunk.h @@ -10,13 +10,11 @@ #ifndef NET_DCSCTP_PACKET_CHUNK_CHUNK_H_ #define NET_DCSCTP_PACKET_CHUNK_CHUNK_H_ - #include +#include #include #include -#include "api/array_view.h" - namespace dcsctp { // Base class for all SCTP chunks @@ -40,7 +38,7 @@ class Chunk { // Introspects the chunk in `data` and returns a human readable textual // representation of it, to be used in debugging. -std::string DebugConvertChunkToString(webrtc::ArrayView data); +std::string DebugConvertChunkToString(std::span data); struct ChunkConfig { static constexpr int kTypeSizeInBytes = 1; diff --git a/net/dcsctp/packet/chunk/cookie_ack_chunk.cc b/net/dcsctp/packet/chunk/cookie_ack_chunk.cc index b01ded9acce..781bcf293ca 100644 --- a/net/dcsctp/packet/chunk/cookie_ack_chunk.cc +++ b/net/dcsctp/packet/chunk/cookie_ack_chunk.cc @@ -11,11 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" - namespace dcsctp { // https://tools.ietf.org/html/rfc4960#section-3.3.12 @@ -27,7 +26,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional CookieAckChunk::Parse( - webrtc::ArrayView data) { + std::span data) { if (!ParseTLV(data).has_value()) { return std::nullopt; } diff --git a/net/dcsctp/packet/chunk/cookie_ack_chunk.h b/net/dcsctp/packet/chunk/cookie_ack_chunk.h index 872757c73b1..d670af9d21f 100644 --- a/net/dcsctp/packet/chunk/cookie_ack_chunk.h +++ b/net/dcsctp/packet/chunk/cookie_ack_chunk.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -35,8 +35,7 @@ class CookieAckChunk : public Chunk, public TLVTrait { CookieAckChunk() {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/cookie_echo_chunk.cc b/net/dcsctp/packet/chunk/cookie_echo_chunk.cc index 4e5a80d389c..4cf309f2886 100644 --- a/net/dcsctp/packet/chunk/cookie_echo_chunk.cc +++ b/net/dcsctp/packet/chunk/cookie_echo_chunk.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -32,7 +32,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional CookieEchoChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/chunk/cookie_echo_chunk.h b/net/dcsctp/packet/chunk/cookie_echo_chunk.h index 94cb10435ca..67cee7a5fe3 100644 --- a/net/dcsctp/packet/chunk/cookie_echo_chunk.h +++ b/net/dcsctp/packet/chunk/cookie_echo_chunk.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -33,16 +33,15 @@ class CookieEchoChunk : public Chunk, public TLVTrait { public: static constexpr int kType = CookieEchoChunkConfig::kType; - explicit CookieEchoChunk(webrtc::ArrayView cookie) + explicit CookieEchoChunk(std::span cookie) : cookie_(cookie.begin(), cookie.end()) {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView cookie() const { return cookie_; } + std::span cookie() const { return cookie_; } private: std::vector cookie_; diff --git a/net/dcsctp/packet/chunk/cookie_echo_chunk_test.cc b/net/dcsctp/packet/chunk/cookie_echo_chunk_test.cc index 9f3f24cdf17..2a120c5ce88 100644 --- a/net/dcsctp/packet/chunk/cookie_echo_chunk_test.cc +++ b/net/dcsctp/packet/chunk/cookie_echo_chunk_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/chunk/data_chunk.cc b/net/dcsctp/packet/chunk/data_chunk.cc index 80da4eba790..60ffd85f8bd 100644 --- a/net/dcsctp/packet/chunk/data_chunk.cc +++ b/net/dcsctp/packet/chunk/data_chunk.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -43,8 +43,7 @@ namespace dcsctp { // \ \ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -std::optional DataChunk::Parse( - webrtc::ArrayView data) { +std::optional DataChunk::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/chunk/data_chunk.h b/net/dcsctp/packet/chunk/data_chunk.h index 54bcdfb9aa5..cfd5acab55c 100644 --- a/net/dcsctp/packet/chunk/data_chunk.h +++ b/net/dcsctp/packet/chunk/data_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/chunk/data_common.h" @@ -60,7 +60,7 @@ class DataChunk : public AnyDataChunk, public TLVTrait { DataChunk(TSN tsn, Data&& data, bool immediate_ack) : AnyDataChunk(tsn, std::move(data), immediate_ack) {} - static std::optional Parse(webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/data_chunk_test.cc b/net/dcsctp/packet/chunk/data_chunk_test.cc index e8791f74faa..4f3d37f55f3 100644 --- a/net/dcsctp/packet/chunk/data_chunk_test.cc +++ b/net/dcsctp/packet/chunk/data_chunk_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/public/types.h" #include "net/dcsctp/testing/testing_macros.h" diff --git a/net/dcsctp/packet/chunk/data_common.h b/net/dcsctp/packet/chunk/data_common.h index 5ded1e88794..361745131f6 100644 --- a/net/dcsctp/packet/chunk/data_common.h +++ b/net/dcsctp/packet/chunk/data_common.h @@ -11,10 +11,10 @@ #define NET_DCSCTP_PACKET_CHUNK_DATA_COMMON_H_ #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/data.h" @@ -54,7 +54,7 @@ class AnyDataChunk : public Chunk { MID mid() const { return data_.mid; } FSN fsn() const { return data_.fsn; } PPID ppid() const { return data_.ppid; } - webrtc::ArrayView payload() const { return data_.payload; } + std::span payload() const { return data_.payload; } // Extracts the Data from the chunk, as a destructive action. Data extract() && { return std::move(data_); } diff --git a/net/dcsctp/packet/chunk/error_chunk.cc b/net/dcsctp/packet/chunk/error_chunk.cc index 90d1dc02591..ea48248c7e9 100644 --- a/net/dcsctp/packet/chunk/error_chunk.cc +++ b/net/dcsctp/packet/chunk/error_chunk.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -34,8 +34,7 @@ namespace dcsctp { // \ \ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -std::optional ErrorChunk::Parse( - webrtc::ArrayView data) { +std::optional ErrorChunk::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -49,7 +48,7 @@ std::optional ErrorChunk::Parse( } void ErrorChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView error_causes = error_causes_.data(); + std::span error_causes = error_causes_.data(); BoundedByteWriter writer = AllocateTLV(out, error_causes.size()); writer.CopyToVariableData(error_causes); } diff --git a/net/dcsctp/packet/chunk/error_chunk.h b/net/dcsctp/packet/chunk/error_chunk.h index b395b6b1c26..ed0d4c10873 100644 --- a/net/dcsctp/packet/chunk/error_chunk.h +++ b/net/dcsctp/packet/chunk/error_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -41,7 +41,7 @@ class ErrorChunk : public Chunk, public TLVTrait { ErrorChunk(ErrorChunk&& other) = default; ErrorChunk& operator=(ErrorChunk&& other) = default; - static std::optional Parse(webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/error_chunk_test.cc b/net/dcsctp/packet/chunk/error_chunk_test.cc index 0d034da2b22..25b9434005f 100644 --- a/net/dcsctp/packet/chunk/error_chunk_test.cc +++ b/net/dcsctp/packet/chunk/error_chunk_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/testing/testing_macros.h" diff --git a/net/dcsctp/packet/chunk/forward_tsn_chunk.cc b/net/dcsctp/packet/chunk/forward_tsn_chunk.cc index dbbfa60ccc6..dcf3f04475d 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_chunk.cc +++ b/net/dcsctp/packet/chunk/forward_tsn_chunk.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -44,7 +44,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional ForwardTsnChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -69,7 +69,7 @@ std::optional ForwardTsnChunk::Parse( } void ForwardTsnChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView skipped = skipped_streams(); + std::span skipped = skipped_streams(); size_t variable_size = skipped.size() * kSkippedStreamBufferSize; BoundedByteWriter writer = AllocateTLV(out, variable_size); diff --git a/net/dcsctp/packet/chunk/forward_tsn_chunk.h b/net/dcsctp/packet/chunk/forward_tsn_chunk.h index b65d2ff1249..6260456cade 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_chunk.h +++ b/net/dcsctp/packet/chunk/forward_tsn_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -41,8 +41,7 @@ class ForwardTsnChunk : public AnyForwardTsnChunk, std::vector skipped_streams) : AnyForwardTsnChunk(new_cumulative_tsn, std::move(skipped_streams)) {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc b/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc index 982e7ce229e..1f8ed5a216f 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc +++ b/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc @@ -10,9 +10,9 @@ #include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" #include "net/dcsctp/public/types.h" diff --git a/net/dcsctp/packet/chunk/forward_tsn_common.h b/net/dcsctp/packet/chunk/forward_tsn_common.h index b2e91688359..082d2e0ed09 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_common.h +++ b/net/dcsctp/packet/chunk/forward_tsn_common.h @@ -10,10 +10,10 @@ #ifndef NET_DCSCTP_PACKET_CHUNK_FORWARD_TSN_COMMON_H_ #define NET_DCSCTP_PACKET_CHUNK_FORWARD_TSN_COMMON_H_ +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/public/types.h" @@ -51,7 +51,7 @@ class AnyForwardTsnChunk : public Chunk { TSN new_cumulative_tsn() const { return new_cumulative_tsn_; } - webrtc::ArrayView skipped_streams() const { + std::span skipped_streams() const { return skipped_streams_; } diff --git a/net/dcsctp/packet/chunk/heartbeat_ack_chunk.cc b/net/dcsctp/packet/chunk/heartbeat_ack_chunk.cc index 0de304016bd..842447f8cb6 100644 --- a/net/dcsctp/packet/chunk/heartbeat_ack_chunk.cc +++ b/net/dcsctp/packet/chunk/heartbeat_ack_chunk.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -35,7 +35,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional HeartbeatAckChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -50,7 +50,7 @@ std::optional HeartbeatAckChunk::Parse( } void HeartbeatAckChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView parameters = parameters_.data(); + std::span parameters = parameters_.data(); BoundedByteWriter writer = AllocateTLV(out, parameters.size()); writer.CopyToVariableData(parameters); } diff --git a/net/dcsctp/packet/chunk/heartbeat_ack_chunk.h b/net/dcsctp/packet/chunk/heartbeat_ack_chunk.h index 3e6ecd4e0fc..319a12f70fd 100644 --- a/net/dcsctp/packet/chunk/heartbeat_ack_chunk.h +++ b/net/dcsctp/packet/chunk/heartbeat_ack_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -43,8 +43,7 @@ class HeartbeatAckChunk : public Chunk, HeartbeatAckChunk(HeartbeatAckChunk&& other) = default; HeartbeatAckChunk& operator=(HeartbeatAckChunk&& other) = default; - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/heartbeat_ack_chunk_test.cc b/net/dcsctp/packet/chunk/heartbeat_ack_chunk_test.cc index 6e41e7b2720..9d5ae638462 100644 --- a/net/dcsctp/packet/chunk/heartbeat_ack_chunk_test.cc +++ b/net/dcsctp/packet/chunk/heartbeat_ack_chunk_test.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/testing/testing_macros.h" diff --git a/net/dcsctp/packet/chunk/heartbeat_request_chunk.cc b/net/dcsctp/packet/chunk/heartbeat_request_chunk.cc index 4b76fff7e8b..eca9bac6d36 100644 --- a/net/dcsctp/packet/chunk/heartbeat_request_chunk.cc +++ b/net/dcsctp/packet/chunk/heartbeat_request_chunk.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -35,7 +35,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional HeartbeatRequestChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -50,7 +50,7 @@ std::optional HeartbeatRequestChunk::Parse( } void HeartbeatRequestChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView parameters = parameters_.data(); + std::span parameters = parameters_.data(); BoundedByteWriter writer = AllocateTLV(out, parameters.size()); writer.CopyToVariableData(parameters); } diff --git a/net/dcsctp/packet/chunk/heartbeat_request_chunk.h b/net/dcsctp/packet/chunk/heartbeat_request_chunk.h index 31eefbcb880..04798fa1de6 100644 --- a/net/dcsctp/packet/chunk/heartbeat_request_chunk.h +++ b/net/dcsctp/packet/chunk/heartbeat_request_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -43,7 +43,7 @@ class HeartbeatRequestChunk : public Chunk, HeartbeatRequestChunk& operator=(HeartbeatRequestChunk&& other) = default; static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/heartbeat_request_chunk_test.cc b/net/dcsctp/packet/chunk/heartbeat_request_chunk_test.cc index ab50025550c..5701349c385 100644 --- a/net/dcsctp/packet/chunk/heartbeat_request_chunk_test.cc +++ b/net/dcsctp/packet/chunk/heartbeat_request_chunk_test.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/testing/testing_macros.h" diff --git a/net/dcsctp/packet/chunk/idata_chunk.cc b/net/dcsctp/packet/chunk/idata_chunk.cc index 7f0fce7a33e..815c058af60 100644 --- a/net/dcsctp/packet/chunk/idata_chunk.cc +++ b/net/dcsctp/packet/chunk/idata_chunk.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -45,8 +45,7 @@ namespace dcsctp { // \ \ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -std::optional IDataChunk::Parse( - webrtc::ArrayView data) { +std::optional IDataChunk::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/chunk/idata_chunk.h b/net/dcsctp/packet/chunk/idata_chunk.h index 23b38ef645e..d99dcd9ded3 100644 --- a/net/dcsctp/packet/chunk/idata_chunk.h +++ b/net/dcsctp/packet/chunk/idata_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/chunk/data_common.h" @@ -60,7 +60,7 @@ class IDataChunk : public AnyDataChunk, public TLVTrait { explicit IDataChunk(TSN tsn, Data&& data, bool immediate_ack) : AnyDataChunk(tsn, std::move(data), immediate_ack) {} - static std::optional Parse(webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/idata_chunk_test.cc b/net/dcsctp/packet/chunk/idata_chunk_test.cc index d65b31b9255..73c94808841 100644 --- a/net/dcsctp/packet/chunk/idata_chunk_test.cc +++ b/net/dcsctp/packet/chunk/idata_chunk_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/data.h" #include "net/dcsctp/public/types.h" diff --git a/net/dcsctp/packet/chunk/iforward_tsn_chunk.cc b/net/dcsctp/packet/chunk/iforward_tsn_chunk.cc index 47c0304a11d..1d8fdab936b 100644 --- a/net/dcsctp/packet/chunk/iforward_tsn_chunk.cc +++ b/net/dcsctp/packet/chunk/iforward_tsn_chunk.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -49,7 +49,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional IForwardTsnChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -77,7 +77,7 @@ std::optional IForwardTsnChunk::Parse( } void IForwardTsnChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView skipped = skipped_streams(); + std::span skipped = skipped_streams(); size_t variable_size = skipped.size() * kSkippedStreamBufferSize; BoundedByteWriter writer = AllocateTLV(out, variable_size); diff --git a/net/dcsctp/packet/chunk/iforward_tsn_chunk.h b/net/dcsctp/packet/chunk/iforward_tsn_chunk.h index ae60fd71eb5..b6e114c2c33 100644 --- a/net/dcsctp/packet/chunk/iforward_tsn_chunk.h +++ b/net/dcsctp/packet/chunk/iforward_tsn_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -41,8 +41,7 @@ class IForwardTsnChunk : public AnyForwardTsnChunk, std::vector skipped_streams) : AnyForwardTsnChunk(new_cumulative_tsn, std::move(skipped_streams)) {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/iforward_tsn_chunk_test.cc b/net/dcsctp/packet/chunk/iforward_tsn_chunk_test.cc index d7f34792429..909304a3d0d 100644 --- a/net/dcsctp/packet/chunk/iforward_tsn_chunk_test.cc +++ b/net/dcsctp/packet/chunk/iforward_tsn_chunk_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" #include "net/dcsctp/public/types.h" diff --git a/net/dcsctp/packet/chunk/init_ack_chunk.cc b/net/dcsctp/packet/chunk/init_ack_chunk.cc index ce837f79080..2c6f4f77369 100644 --- a/net/dcsctp/packet/chunk/init_ack_chunk.cc +++ b/net/dcsctp/packet/chunk/init_ack_chunk.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -44,8 +44,7 @@ namespace dcsctp { // \ \ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -std::optional InitAckChunk::Parse( - webrtc::ArrayView data) { +std::optional InitAckChunk::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -66,7 +65,7 @@ std::optional InitAckChunk::Parse( } void InitAckChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView parameters = parameters_.data(); + std::span parameters = parameters_.data(); BoundedByteWriter writer = AllocateTLV(out, parameters.size()); writer.Store32<4>(*initiate_tag_); diff --git a/net/dcsctp/packet/chunk/init_ack_chunk.h b/net/dcsctp/packet/chunk/init_ack_chunk.h index 19baea6776f..4220a80df98 100644 --- a/net/dcsctp/packet/chunk/init_ack_chunk.h +++ b/net/dcsctp/packet/chunk/init_ack_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -52,8 +52,7 @@ class InitAckChunk : public Chunk, public TLVTrait { InitAckChunk(InitAckChunk&& other) = default; InitAckChunk& operator=(InitAckChunk&& other) = default; - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/init_ack_chunk_test.cc b/net/dcsctp/packet/chunk/init_ack_chunk_test.cc index 2741d6757b2..11bfa7cc072 100644 --- a/net/dcsctp/packet/chunk/init_ack_chunk_test.cc +++ b/net/dcsctp/packet/chunk/init_ack_chunk_test.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/forward_tsn_supported_parameter.h" #include "net/dcsctp/packet/parameter/parameter.h" diff --git a/net/dcsctp/packet/chunk/init_chunk.cc b/net/dcsctp/packet/chunk/init_chunk.cc index f08e35b331e..9e29df276f2 100644 --- a/net/dcsctp/packet/chunk/init_chunk.cc +++ b/net/dcsctp/packet/chunk/init_chunk.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -44,8 +44,7 @@ namespace dcsctp { // \ \ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -std::optional InitChunk::Parse( - webrtc::ArrayView data) { +std::optional InitChunk::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -67,7 +66,7 @@ std::optional InitChunk::Parse( } void InitChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView parameters = parameters_.data(); + std::span parameters = parameters_.data(); BoundedByteWriter writer = AllocateTLV(out, parameters.size()); writer.Store32<4>(*initiate_tag_); diff --git a/net/dcsctp/packet/chunk/init_chunk.h b/net/dcsctp/packet/chunk/init_chunk.h index d86ca4b534c..2b40691bf21 100644 --- a/net/dcsctp/packet/chunk/init_chunk.h +++ b/net/dcsctp/packet/chunk/init_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -52,7 +52,7 @@ class InitChunk : public Chunk, public TLVTrait { InitChunk(InitChunk&& other) = default; InitChunk& operator=(InitChunk&& other) = default; - static std::optional Parse(webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/reconfig_chunk.cc b/net/dcsctp/packet/chunk/reconfig_chunk.cc index ce5d630666c..5440016d77d 100644 --- a/net/dcsctp/packet/chunk/reconfig_chunk.cc +++ b/net/dcsctp/packet/chunk/reconfig_chunk.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "net/dcsctp/packet/parameter/parameter.h" @@ -39,7 +39,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional ReConfigChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -55,7 +55,7 @@ std::optional ReConfigChunk::Parse( } void ReConfigChunk::SerializeTo(std::vector& out) const { - webrtc::ArrayView parameters = parameters_.data(); + std::span parameters = parameters_.data(); BoundedByteWriter writer = AllocateTLV(out, parameters.size()); writer.CopyToVariableData(parameters); } diff --git a/net/dcsctp/packet/chunk/reconfig_chunk.h b/net/dcsctp/packet/chunk/reconfig_chunk.h index 227a1d2991d..bf8cbcdc43f 100644 --- a/net/dcsctp/packet/chunk/reconfig_chunk.h +++ b/net/dcsctp/packet/chunk/reconfig_chunk.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -38,8 +38,7 @@ class ReConfigChunk : public Chunk, public TLVTrait { explicit ReConfigChunk(Parameters parameters) : parameters_(std::move(parameters)) {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/reconfig_chunk_test.cc b/net/dcsctp/packet/chunk/reconfig_chunk_test.cc index 296f7ec5fec..261eb6306df 100644 --- a/net/dcsctp/packet/chunk/reconfig_chunk_test.cc +++ b/net/dcsctp/packet/chunk/reconfig_chunk_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h" #include "net/dcsctp/packet/parameter/parameter.h" diff --git a/net/dcsctp/packet/chunk/sack_chunk.cc b/net/dcsctp/packet/chunk/sack_chunk.cc index ef77331bbdc..906ebdb5079 100644 --- a/net/dcsctp/packet/chunk/sack_chunk.cc +++ b/net/dcsctp/packet/chunk/sack_chunk.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -57,8 +57,7 @@ namespace dcsctp { // | Duplicate TSN X | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -std::optional SackChunk::Parse( - webrtc::ArrayView data) { +std::optional SackChunk::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/chunk/sack_chunk.h b/net/dcsctp/packet/chunk/sack_chunk.h index 026d52d8c10..7410e85ac00 100644 --- a/net/dcsctp/packet/chunk/sack_chunk.h +++ b/net/dcsctp/packet/chunk/sack_chunk.h @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -55,14 +55,14 @@ class SackChunk : public Chunk, public TLVTrait { a_rwnd_(a_rwnd), gap_ack_blocks_(std::move(gap_ack_blocks)), duplicate_tsns_(std::move(duplicate_tsns)) {} - static std::optional Parse(webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; TSN cumulative_tsn_ack() const { return cumulative_tsn_ack_; } uint32_t a_rwnd() const { return a_rwnd_; } - webrtc::ArrayView gap_ack_blocks() const { + std::span gap_ack_blocks() const { return gap_ack_blocks_; } const std::set& duplicate_tsns() const { return duplicate_tsns_; } diff --git a/net/dcsctp/packet/chunk/sack_chunk_test.cc b/net/dcsctp/packet/chunk/sack_chunk_test.cc index 2f5c0d09315..70fd96b6a89 100644 --- a/net/dcsctp/packet/chunk/sack_chunk_test.cc +++ b/net/dcsctp/packet/chunk/sack_chunk_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" diff --git a/net/dcsctp/packet/chunk/shutdown_ack_chunk.cc b/net/dcsctp/packet/chunk/shutdown_ack_chunk.cc index f78276d079e..2bedc71c735 100644 --- a/net/dcsctp/packet/chunk/shutdown_ack_chunk.cc +++ b/net/dcsctp/packet/chunk/shutdown_ack_chunk.cc @@ -11,11 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" - namespace dcsctp { // https://tools.ietf.org/html/rfc4960#section-3.3.9 @@ -27,7 +26,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional ShutdownAckChunk::Parse( - webrtc::ArrayView data) { + std::span data) { if (!ParseTLV(data).has_value()) { return std::nullopt; } diff --git a/net/dcsctp/packet/chunk/shutdown_ack_chunk.h b/net/dcsctp/packet/chunk/shutdown_ack_chunk.h index 18550f85a96..07da0a46027 100644 --- a/net/dcsctp/packet/chunk/shutdown_ack_chunk.h +++ b/net/dcsctp/packet/chunk/shutdown_ack_chunk.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -35,8 +35,7 @@ class ShutdownAckChunk : public Chunk, public TLVTrait { ShutdownAckChunk() {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/shutdown_chunk.cc b/net/dcsctp/packet/chunk/shutdown_chunk.cc index a205f0d97e6..26ac869a8a7 100644 --- a/net/dcsctp/packet/chunk/shutdown_chunk.cc +++ b/net/dcsctp/packet/chunk/shutdown_chunk.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -32,7 +32,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional ShutdownChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/chunk/shutdown_chunk.h b/net/dcsctp/packet/chunk/shutdown_chunk.h index 2dec9c172c1..d591553d55d 100644 --- a/net/dcsctp/packet/chunk/shutdown_chunk.h +++ b/net/dcsctp/packet/chunk/shutdown_chunk.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -37,8 +37,7 @@ class ShutdownChunk : public Chunk, public TLVTrait { explicit ShutdownChunk(TSN cumulative_tsn_ack) : cumulative_tsn_ack_(cumulative_tsn_ack) {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/chunk/shutdown_complete_chunk.cc b/net/dcsctp/packet/chunk/shutdown_complete_chunk.cc index 4a596d910ad..82edde6b06f 100644 --- a/net/dcsctp/packet/chunk/shutdown_complete_chunk.cc +++ b/net/dcsctp/packet/chunk/shutdown_complete_chunk.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -29,7 +29,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional ShutdownCompleteChunk::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/chunk/shutdown_complete_chunk.h b/net/dcsctp/packet/chunk/shutdown_complete_chunk.h index e8cc570d5cd..2f85602b74f 100644 --- a/net/dcsctp/packet/chunk/shutdown_complete_chunk.h +++ b/net/dcsctp/packet/chunk/shutdown_complete_chunk.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -38,7 +38,7 @@ class ShutdownCompleteChunk : public Chunk, : tag_reflected_(tag_reflected) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/crc32c.cc b/net/dcsctp/packet/crc32c.cc index 8318b0dc751..c32e28bcc94 100644 --- a/net/dcsctp/packet/crc32c.cc +++ b/net/dcsctp/packet/crc32c.cc @@ -10,21 +10,17 @@ #include "net/dcsctp/packet/crc32c.h" #include +#include -#include "api/array_view.h" -#include "third_party/crc32c/src/include/crc32c/crc32c.h" +#include "absl/crc/crc32c.h" +#include "absl/numeric/bits.h" +#include "absl/strings/string_view.h" namespace dcsctp { -uint32_t GenerateCrc32C(webrtc::ArrayView data) { - uint32_t crc32c = crc32c_value(data.data(), data.size()); - - // Byte swapping for little endian byte order: - uint8_t byte0 = crc32c; - uint8_t byte1 = crc32c >> 8; - uint8_t byte2 = crc32c >> 16; - uint8_t byte3 = crc32c >> 24; - crc32c = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); - return crc32c; +uint32_t GenerateCrc32C(std::span data) { + absl::crc32c_t crc32 = absl::ComputeCrc32c(absl::string_view( + reinterpret_cast(data.data()), data.size())); + return absl::byteswap(static_cast(crc32)); } } // namespace dcsctp diff --git a/net/dcsctp/packet/crc32c.h b/net/dcsctp/packet/crc32c.h index 71ef16ceeb1..a8e8900be49 100644 --- a/net/dcsctp/packet/crc32c.h +++ b/net/dcsctp/packet/crc32c.h @@ -11,13 +11,12 @@ #define NET_DCSCTP_PACKET_CRC32C_H_ #include - -#include "api/array_view.h" +#include namespace dcsctp { // Generates the CRC32C checksum of `data`. -uint32_t GenerateCrc32C(webrtc::ArrayView data); +uint32_t GenerateCrc32C(std::span data); } // namespace dcsctp diff --git a/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.cc b/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.cc index 629ff2e30c3..d6194120c23 100644 --- a/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.cc +++ b/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.cc @@ -11,11 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" - namespace dcsctp { // https://tools.ietf.org/html/rfc4960#section-3.3.10.10 @@ -25,8 +24,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -CookieReceivedWhileShuttingDownCause::Parse( - webrtc::ArrayView data) { +CookieReceivedWhileShuttingDownCause::Parse(std::span data) { if (!ParseTLV(data).has_value()) { return std::nullopt; } diff --git a/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.h b/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.h index 360a31664d5..8d610691278 100644 --- a/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.h +++ b/net/dcsctp/packet/error_cause/cookie_received_while_shutting_down_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -39,7 +39,7 @@ class CookieReceivedWhileShuttingDownCause CookieReceivedWhileShuttingDownCause() {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.cc b/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.cc index eb361e3596c..5b008faf803 100644 --- a/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.cc +++ b/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.cc @@ -11,11 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" - namespace dcsctp { // https://tools.ietf.org/html/rfc4960#section-3.3.10.7 @@ -25,7 +24,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -InvalidMandatoryParameterCause::Parse(webrtc::ArrayView data) { +InvalidMandatoryParameterCause::Parse(std::span data) { if (!ParseTLV(data).has_value()) { return std::nullopt; } diff --git a/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.h b/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.h index 734b72979d9..59361c63ea3 100644 --- a/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.h +++ b/net/dcsctp/packet/error_cause/invalid_mandatory_parameter_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -38,7 +38,7 @@ class InvalidMandatoryParameterCause InvalidMandatoryParameterCause() {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.cc b/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.cc index 7cba7fb7b7c..fb94f243cbd 100644 --- a/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.cc +++ b/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "net/dcsctp/public/types.h" @@ -31,7 +31,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional InvalidStreamIdentifierCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.h b/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.h index b58809e51a4..2cb977d2127 100644 --- a/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.h +++ b/net/dcsctp/packet/error_cause/invalid_stream_identifier_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" #include "net/dcsctp/public/types.h" @@ -40,7 +40,7 @@ class InvalidStreamIdentifierCause : stream_id_(stream_id) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc b/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc index db769f9c129..2cd90d90ea0 100644 --- a/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc +++ b/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.cc @@ -12,10 +12,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/logging.h" @@ -37,7 +37,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -MissingMandatoryParameterCause::Parse(webrtc::ArrayView data) { +MissingMandatoryParameterCause::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.h b/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.h index 04b33d82361..8ca8cdc2f1b 100644 --- a/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.h +++ b/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -36,17 +36,17 @@ class MissingMandatoryParameterCause static constexpr int kType = MissingMandatoryParameterCauseConfig::kType; explicit MissingMandatoryParameterCause( - webrtc::ArrayView missing_parameter_types) + std::span missing_parameter_types) : missing_parameter_types_(missing_parameter_types.begin(), missing_parameter_types.end()) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView missing_parameter_types() const { + std::span missing_parameter_types() const { return missing_parameter_types_; } diff --git a/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause_test.cc b/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause_test.cc index ade3667484b..9a83ff3ce93 100644 --- a/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause_test.cc +++ b/net/dcsctp/packet/error_cause/missing_mandatory_parameter_cause_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/error_cause/no_user_data_cause.cc b/net/dcsctp/packet/error_cause/no_user_data_cause.cc index f1f3acc40e4..379d1ba4c42 100644 --- a/net/dcsctp/packet/error_cause/no_user_data_cause.cc +++ b/net/dcsctp/packet/error_cause/no_user_data_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -32,7 +32,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional NoUserDataCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/no_user_data_cause.h b/net/dcsctp/packet/error_cause/no_user_data_cause.h index ad67b33356a..cef1ee7f81c 100644 --- a/net/dcsctp/packet/error_cause/no_user_data_cause.h +++ b/net/dcsctp/packet/error_cause/no_user_data_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -36,8 +36,7 @@ class NoUserDataCause : public Parameter, explicit NoUserDataCause(TSN tsn) : tsn_(tsn) {} - static std::optional Parse( - webrtc::ArrayView data); + static std::optional Parse(std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/out_of_resource_error_cause.cc b/net/dcsctp/packet/error_cause/out_of_resource_error_cause.cc index 91313335c07..b27d8a2e93c 100644 --- a/net/dcsctp/packet/error_cause/out_of_resource_error_cause.cc +++ b/net/dcsctp/packet/error_cause/out_of_resource_error_cause.cc @@ -11,11 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" - namespace dcsctp { // https://tools.ietf.org/html/rfc4960#section-3.3.10.4 @@ -25,7 +24,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional OutOfResourceErrorCause::Parse( - webrtc::ArrayView data) { + std::span data) { if (!ParseTLV(data).has_value()) { return std::nullopt; } diff --git a/net/dcsctp/packet/error_cause/out_of_resource_error_cause.h b/net/dcsctp/packet/error_cause/out_of_resource_error_cause.h index 6e4e9eef244..63e3dd2b303 100644 --- a/net/dcsctp/packet/error_cause/out_of_resource_error_cause.h +++ b/net/dcsctp/packet/error_cause/out_of_resource_error_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -37,7 +37,7 @@ class OutOfResourceErrorCause : public Parameter, OutOfResourceErrorCause() {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/protocol_violation_cause.cc b/net/dcsctp/packet/error_cause/protocol_violation_cause.cc index 6196472aafa..bc1a4cc5a65 100644 --- a/net/dcsctp/packet/error_cause/protocol_violation_cause.cc +++ b/net/dcsctp/packet/error_cause/protocol_violation_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/strings/string_builder.h" @@ -33,7 +33,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional ProtocolViolationCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -46,7 +46,7 @@ std::optional ProtocolViolationCause::Parse( void ProtocolViolationCause::SerializeTo(std::vector& out) const { BoundedByteWriter writer = AllocateTLV(out, additional_information_.size()); - writer.CopyToVariableData(webrtc::MakeArrayView( + writer.CopyToVariableData(std::span( reinterpret_cast(additional_information_.data()), additional_information_.size())); } diff --git a/net/dcsctp/packet/error_cause/protocol_violation_cause.h b/net/dcsctp/packet/error_cause/protocol_violation_cause.h index 9e265e93910..85e8176c2e1 100644 --- a/net/dcsctp/packet/error_cause/protocol_violation_cause.h +++ b/net/dcsctp/packet/error_cause/protocol_violation_cause.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -39,7 +39,7 @@ class ProtocolViolationCause : public Parameter, : additional_information_(additional_information) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/protocol_violation_cause_test.cc b/net/dcsctp/packet/error_cause/protocol_violation_cause_test.cc index de3ef220865..b0f9878214f 100644 --- a/net/dcsctp/packet/error_cause/protocol_violation_cause_test.cc +++ b/net/dcsctp/packet/error_cause/protocol_violation_cause_test.cc @@ -11,7 +11,6 @@ #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" diff --git a/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.cc b/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.cc index 8694ff40e0b..f15eacd880e 100644 --- a/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.cc +++ b/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -33,7 +33,7 @@ namespace dcsctp { std::optional RestartOfAnAssociationWithNewAddressesCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.h b/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.h index 14b6c4ec3c4..6cfe28adb06 100644 --- a/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.h +++ b/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -38,16 +38,16 @@ class RestartOfAnAssociationWithNewAddressesCause RestartOfAnAssociationWithNewAddressesCauseConfig::kType; explicit RestartOfAnAssociationWithNewAddressesCause( - webrtc::ArrayView new_address_tlvs) + std::span new_address_tlvs) : new_address_tlvs_(new_address_tlvs.begin(), new_address_tlvs.end()) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView new_address_tlvs() const { + std::span new_address_tlvs() const { return new_address_tlvs_; } diff --git a/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause_test.cc b/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause_test.cc index 0c3b3fcb30c..c32ada22eb1 100644 --- a/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause_test.cc +++ b/net/dcsctp/packet/error_cause/restart_of_an_association_with_new_address_cause_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/error_cause/stale_cookie_error_cause.cc b/net/dcsctp/packet/error_cause/stale_cookie_error_cause.cc index 996ca071ec8..00c9ab49a8f 100644 --- a/net/dcsctp/packet/error_cause/stale_cookie_error_cause.cc +++ b/net/dcsctp/packet/error_cause/stale_cookie_error_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/strings/string_builder.h" @@ -30,7 +30,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional StaleCookieErrorCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/stale_cookie_error_cause.h b/net/dcsctp/packet/error_cause/stale_cookie_error_cause.h index cb5bace37c0..82bf61e7eea 100644 --- a/net/dcsctp/packet/error_cause/stale_cookie_error_cause.h +++ b/net/dcsctp/packet/error_cause/stale_cookie_error_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -38,7 +38,7 @@ class StaleCookieErrorCause : public Parameter, : staleness_us_(staleness_us) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.cc b/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.cc index 6a12fff64da..59d015ce974 100644 --- a/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.cc +++ b/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/strings/string_builder.h" @@ -32,7 +32,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional UnrecognizedChunkTypeCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.h b/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.h index 48c9bc9f362..6b8c506155c 100644 --- a/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.h +++ b/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -40,12 +40,12 @@ class UnrecognizedChunkTypeCause : unrecognized_chunk_(std::move(unrecognized_chunk)) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView unrecognized_chunk() const { + std::span unrecognized_chunk() const { return unrecognized_chunk_; } diff --git a/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause_test.cc b/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause_test.cc index e058f5f8274..7a5f78c0178 100644 --- a/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause_test.cc +++ b/net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.cc b/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.cc index 73df61197ac..3dc884e1c99 100644 --- a/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.cc +++ b/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -30,7 +30,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional UnrecognizedParametersCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.h b/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.h index 3088705258c..08cb3cce9f9 100644 --- a/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.h +++ b/net/dcsctp/packet/error_cause/unrecognized_parameter_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -36,17 +36,17 @@ class UnrecognizedParametersCause static constexpr int kType = UnrecognizedParametersCauseConfig::kType; explicit UnrecognizedParametersCause( - webrtc::ArrayView unrecognized_parameters) + std::span unrecognized_parameters) : unrecognized_parameters_(unrecognized_parameters.begin(), unrecognized_parameters.end()) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView unrecognized_parameters() const { + std::span unrecognized_parameters() const { return unrecognized_parameters_; } diff --git a/net/dcsctp/packet/error_cause/unrecognized_parameter_cause_test.cc b/net/dcsctp/packet/error_cause/unrecognized_parameter_cause_test.cc index 189205c2a15..5a390343583 100644 --- a/net/dcsctp/packet/error_cause/unrecognized_parameter_cause_test.cc +++ b/net/dcsctp/packet/error_cause/unrecognized_parameter_cause_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/error_cause/unresolvable_address_cause.cc b/net/dcsctp/packet/error_cause/unresolvable_address_cause.cc index 77cb7ae3baa..af3885ded45 100644 --- a/net/dcsctp/packet/error_cause/unresolvable_address_cause.cc +++ b/net/dcsctp/packet/error_cause/unresolvable_address_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -30,7 +30,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional UnresolvableAddressCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/error_cause/unresolvable_address_cause.h b/net/dcsctp/packet/error_cause/unresolvable_address_cause.h index 3ab94eb674f..c6914533e66 100644 --- a/net/dcsctp/packet/error_cause/unresolvable_address_cause.h +++ b/net/dcsctp/packet/error_cause/unresolvable_address_cause.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -36,17 +36,17 @@ class UnresolvableAddressCause static constexpr int kType = UnresolvableAddressCauseConfig::kType; explicit UnresolvableAddressCause( - webrtc::ArrayView unresolvable_address) + std::span unresolvable_address) : unresolvable_address_(unresolvable_address.begin(), unresolvable_address.end()) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView unresolvable_address() const { + std::span unresolvable_address() const { return unresolvable_address_; } diff --git a/net/dcsctp/packet/error_cause/unresolvable_address_cause_test.cc b/net/dcsctp/packet/error_cause/unresolvable_address_cause_test.cc index 4c76683ed31..d4c60cf637c 100644 --- a/net/dcsctp/packet/error_cause/unresolvable_address_cause_test.cc +++ b/net/dcsctp/packet/error_cause/unresolvable_address_cause_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/error_cause/user_initiated_abort_cause.cc b/net/dcsctp/packet/error_cause/user_initiated_abort_cause.cc index 1b67aaa52fb..f2ad53fca89 100644 --- a/net/dcsctp/packet/error_cause/user_initiated_abort_cause.cc +++ b/net/dcsctp/packet/error_cause/user_initiated_abort_cause.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/strings/string_builder.h" @@ -33,7 +33,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional UserInitiatedAbortCause::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; @@ -49,7 +49,7 @@ std::optional UserInitiatedAbortCause::Parse( void UserInitiatedAbortCause::SerializeTo(std::vector& out) const { BoundedByteWriter writer = AllocateTLV(out, upper_layer_abort_reason_.size()); - writer.CopyToVariableData(webrtc::MakeArrayView( + writer.CopyToVariableData(std::span( reinterpret_cast(upper_layer_abort_reason_.data()), upper_layer_abort_reason_.size())); } diff --git a/net/dcsctp/packet/error_cause/user_initiated_abort_cause.h b/net/dcsctp/packet/error_cause/user_initiated_abort_cause.h index 8cea2bcf70a..2b9d21b7523 100644 --- a/net/dcsctp/packet/error_cause/user_initiated_abort_cause.h +++ b/net/dcsctp/packet/error_cause/user_initiated_abort_cause.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -39,7 +39,7 @@ class UserInitiatedAbortCause : public Parameter, : upper_layer_abort_reason_(upper_layer_abort_reason) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/error_cause/user_initiated_abort_cause_test.cc b/net/dcsctp/packet/error_cause/user_initiated_abort_cause_test.cc index 683900778f7..3a6437887d5 100644 --- a/net/dcsctp/packet/error_cause/user_initiated_abort_cause_test.cc +++ b/net/dcsctp/packet/error_cause/user_initiated_abort_cause_test.cc @@ -11,7 +11,6 @@ #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" diff --git a/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.cc b/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.cc index b0fab6a5219..9e8a68e0c24 100644 --- a/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.cc +++ b/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -35,8 +35,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -AddIncomingStreamsRequestParameter::Parse( - webrtc::ArrayView data) { +AddIncomingStreamsRequestParameter::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.h b/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.h index fe3b306af5d..298c845fb50 100644 --- a/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.h +++ b/net/dcsctp/packet/parameter/add_incoming_streams_request_parameter.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -43,7 +43,7 @@ class AddIncomingStreamsRequestParameter nbr_of_new_streams_(nbr_of_new_streams) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.cc b/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.cc index 7310474feb0..b4d09068483 100644 --- a/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.cc +++ b/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -35,8 +35,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -AddOutgoingStreamsRequestParameter::Parse( - webrtc::ArrayView data) { +AddOutgoingStreamsRequestParameter::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.h b/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.h index ac1735f95f3..44085ed4819 100644 --- a/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.h +++ b/net/dcsctp/packet/parameter/add_outgoing_streams_request_parameter.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -43,7 +43,7 @@ class AddOutgoingStreamsRequestParameter nbr_of_new_streams_(nbr_of_new_streams) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.cc b/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.cc index c095dddbbb0..0b548444ab3 100644 --- a/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.cc +++ b/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.cc @@ -11,11 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" - namespace dcsctp { // https://tools.ietf.org/html/rfc3758#section-3.1 @@ -26,7 +25,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional ForwardTsnSupportedParameter::Parse( - webrtc::ArrayView data) { + std::span data) { if (!ParseTLV(data).has_value()) { return std::nullopt; } diff --git a/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.h b/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.h index 03e492f8c75..eee26d09d89 100644 --- a/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.h +++ b/net/dcsctp/packet/parameter/forward_tsn_supported_parameter.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -38,7 +38,7 @@ class ForwardTsnSupportedParameter ForwardTsnSupportedParameter() {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/parameter/heartbeat_info_parameter.cc b/net/dcsctp/packet/parameter/heartbeat_info_parameter.cc index e6120612a73..3b6f534aa6b 100644 --- a/net/dcsctp/packet/parameter/heartbeat_info_parameter.cc +++ b/net/dcsctp/packet/parameter/heartbeat_info_parameter.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/strings/string_builder.h" @@ -34,7 +34,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional HeartbeatInfoParameter::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/heartbeat_info_parameter.h b/net/dcsctp/packet/parameter/heartbeat_info_parameter.h index 82c18dbe314..38b78f4eeee 100644 --- a/net/dcsctp/packet/parameter/heartbeat_info_parameter.h +++ b/net/dcsctp/packet/parameter/heartbeat_info_parameter.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -34,16 +34,16 @@ class HeartbeatInfoParameter : public Parameter, public: static constexpr int kType = HeartbeatInfoParameterConfig::kType; - explicit HeartbeatInfoParameter(webrtc::ArrayView info) + explicit HeartbeatInfoParameter(std::span info) : info_(info.begin(), info.end()) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView info() const { return info_; } + std::span info() const { return info_; } private: std::vector info_; diff --git a/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.cc b/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.cc index fe43dae0a9b..05d1b0464bd 100644 --- a/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.cc +++ b/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -42,7 +42,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -IncomingSSNResetRequestParameter::Parse(webrtc::ArrayView data) { +IncomingSSNResetRequestParameter::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.h b/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.h index 77e453a2e96..2569ef072a2 100644 --- a/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.h +++ b/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -45,7 +45,7 @@ class IncomingSSNResetRequestParameter stream_ids_(std::move(stream_ids)) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; @@ -53,7 +53,7 @@ class IncomingSSNResetRequestParameter ReconfigRequestSN request_sequence_number() const { return request_sequence_number_; } - webrtc::ArrayView stream_ids() const { return stream_ids_; } + std::span stream_ids() const { return stream_ids_; } private: static constexpr size_t kStreamIdSize = sizeof(uint16_t); diff --git a/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter_test.cc b/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter_test.cc index 5b10fb371f3..ede45c1577c 100644 --- a/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter_test.cc +++ b/net/dcsctp/packet/parameter/incoming_ssn_reset_request_parameter_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/public/types.h" #include "net/dcsctp/testing/testing_macros.h" diff --git a/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.cc b/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.cc index 39f45fd00cf..bc3ae9ae6f6 100644 --- a/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.cc +++ b/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -46,7 +46,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -OutgoingSSNResetRequestParameter::Parse(webrtc::ArrayView data) { +OutgoingSSNResetRequestParameter::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h b/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h index 71399afb6a8..d3c2b5e4fb6 100644 --- a/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h +++ b/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -49,7 +49,7 @@ class OutgoingSSNResetRequestParameter stream_ids_(std::move(stream_ids)) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; @@ -61,7 +61,7 @@ class OutgoingSSNResetRequestParameter return response_sequence_number_; } TSN sender_last_assigned_tsn() const { return sender_last_assigned_tsn_; } - webrtc::ArrayView stream_ids() const { return stream_ids_; } + std::span stream_ids() const { return stream_ids_; } private: static constexpr size_t kStreamIdSize = sizeof(uint16_t); diff --git a/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter_test.cc b/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter_test.cc index f61b8a0cec2..0ab234d0282 100644 --- a/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter_test.cc +++ b/net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/public/types.h" #include "net/dcsctp/testing/testing_macros.h" diff --git a/net/dcsctp/packet/parameter/parameter.cc b/net/dcsctp/packet/parameter/parameter.cc index fadcbd4a6cc..3cece5effed 100644 --- a/net/dcsctp/packet/parameter/parameter.cc +++ b/net/dcsctp/packet/parameter/parameter.cc @@ -12,9 +12,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/common/math.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "rtc_base/logging.h" @@ -37,26 +37,25 @@ Parameters::Builder& Parameters::Builder::Add(const Parameter& p) { } std::vector Parameters::descriptors() const { - webrtc::ArrayView span(data_); + std::span span(data_); std::vector result; while (!span.empty()) { BoundedByteReader header(span); uint16_t type = header.Load16<0>(); uint16_t length = header.Load16<2>(); - result.emplace_back(type, span.subview(0, length)); + result.emplace_back(type, span.subspan(0, length)); size_t length_with_padding = RoundUpTo4(length); if (length_with_padding > span.size()) { break; } - span = span.subview(length_with_padding); + span = span.subspan(length_with_padding); } return result; } -std::optional Parameters::Parse( - webrtc::ArrayView data) { +std::optional Parameters::Parse(std::span data) { // Validate the parameter descriptors - webrtc::ArrayView span(data); + std::span span(data); while (!span.empty()) { if (span.size() < kParameterHeaderSize) { RTC_DLOG(LS_WARNING) << "Insufficient parameter length"; @@ -72,7 +71,7 @@ std::optional Parameters::Parse( if (length_with_padding > span.size()) { break; } - span = span.subview(length_with_padding); + span = span.subspan(length_with_padding); } return Parameters(std::vector(data.begin(), data.end())); } diff --git a/net/dcsctp/packet/parameter/parameter.h b/net/dcsctp/packet/parameter/parameter.h index e64de971701..30a2feb4b8b 100644 --- a/net/dcsctp/packet/parameter/parameter.h +++ b/net/dcsctp/packet/parameter/parameter.h @@ -10,16 +10,14 @@ #ifndef NET_DCSCTP_PACKET_PARAMETER_PARAMETER_H_ #define NET_DCSCTP_PACKET_PARAMETER_PARAMETER_H_ - #include #include +#include #include #include #include #include -#include "api/array_view.h" - namespace dcsctp { class Parameter { @@ -35,10 +33,10 @@ class Parameter { }; struct ParameterDescriptor { - ParameterDescriptor(uint16_t type, webrtc::ArrayView data) + ParameterDescriptor(uint16_t type, std::span data) : type(type), data(data) {} uint16_t type; - webrtc::ArrayView data; + std::span data; }; class Parameters { @@ -53,13 +51,13 @@ class Parameters { std::vector data_; }; - static std::optional Parse(webrtc::ArrayView data); + static std::optional Parse(std::span data); Parameters() {} Parameters(Parameters&& other) = default; Parameters& operator=(Parameters&& other) = default; - webrtc::ArrayView data() const { return data_; } + std::span data() const { return data_; } std::vector descriptors() const; template diff --git a/net/dcsctp/packet/parameter/parameter_test.cc b/net/dcsctp/packet/parameter/parameter_test.cc index 366a740dba8..97046cd1de0 100644 --- a/net/dcsctp/packet/parameter/parameter_test.cc +++ b/net/dcsctp/packet/parameter/parameter_test.cc @@ -10,9 +10,9 @@ #include "net/dcsctp/packet/parameter/parameter.h" #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h" #include "net/dcsctp/public/types.h" @@ -33,7 +33,7 @@ TEST(ParameterTest, SerializeDeserializeParameter) { TSN(789), {StreamID(42)})) .Build(); - webrtc::ArrayView serialized = parameters.data(); + std::span serialized = parameters.data(); ASSERT_HAS_VALUE_AND_ASSIGN(Parameters parsed, Parameters::Parse(serialized)); auto descriptors = parsed.descriptors(); diff --git a/net/dcsctp/packet/parameter/reconfiguration_response_parameter.cc b/net/dcsctp/packet/parameter/reconfiguration_response_parameter.cc index d4460f75821..ffdef5cfaeb 100644 --- a/net/dcsctp/packet/parameter/reconfiguration_response_parameter.cc +++ b/net/dcsctp/packet/parameter/reconfiguration_response_parameter.cc @@ -12,12 +12,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -63,7 +63,7 @@ absl::string_view ToString(ReconfigurationResponseParameter::Result result) { } std::optional -ReconfigurationResponseParameter::Parse(webrtc::ArrayView data) { +ReconfigurationResponseParameter::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/reconfiguration_response_parameter.h b/net/dcsctp/packet/parameter/reconfiguration_response_parameter.h index 6fb4b357d83..f719dee53fb 100644 --- a/net/dcsctp/packet/parameter/reconfiguration_response_parameter.h +++ b/net/dcsctp/packet/parameter/reconfiguration_response_parameter.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -65,7 +65,7 @@ class ReconfigurationResponseParameter receiver_next_tsn_(receiver_next_tsn) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.cc b/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.cc index ee0b7e3a746..e29bb6fc309 100644 --- a/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.cc +++ b/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -33,7 +33,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional SSNTSNResetRequestParameter::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.h b/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.h index 1beb0b867a3..468c18c42df 100644 --- a/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.h +++ b/net/dcsctp/packet/parameter/ssn_tsn_reset_request_parameter.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -41,7 +41,7 @@ class SSNTSNResetRequestParameter : request_sequence_number_(request_sequence_number) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/parameter/state_cookie_parameter.cc b/net/dcsctp/packet/parameter/state_cookie_parameter.cc index cc1c59758be..e1a59245e3a 100644 --- a/net/dcsctp/packet/parameter/state_cookie_parameter.cc +++ b/net/dcsctp/packet/parameter/state_cookie_parameter.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/strings/string_builder.h" @@ -24,7 +24,7 @@ namespace dcsctp { // https://tools.ietf.org/html/rfc4960#section-3.3.3.1 std::optional StateCookieParameter::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/state_cookie_parameter.h b/net/dcsctp/packet/parameter/state_cookie_parameter.h index 30de131402a..fe723f114bc 100644 --- a/net/dcsctp/packet/parameter/state_cookie_parameter.h +++ b/net/dcsctp/packet/parameter/state_cookie_parameter.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -34,16 +34,16 @@ class StateCookieParameter : public Parameter, public: static constexpr int kType = StateCookieParameterConfig::kType; - explicit StateCookieParameter(webrtc::ArrayView data) + explicit StateCookieParameter(std::span data) : data_(data.begin(), data.end()) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; - webrtc::ArrayView data() const { return data_; } + std::span data() const { return data_; } private: std::vector data_; diff --git a/net/dcsctp/packet/parameter/state_cookie_parameter_test.cc b/net/dcsctp/packet/parameter/state_cookie_parameter_test.cc index 40937647688..6ce1f92a35d 100644 --- a/net/dcsctp/packet/parameter/state_cookie_parameter_test.cc +++ b/net/dcsctp/packet/parameter/state_cookie_parameter_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/parameter/supported_extensions_parameter.cc b/net/dcsctp/packet/parameter/supported_extensions_parameter.cc index edbcc98bc30..4da5f2a77ae 100644 --- a/net/dcsctp/packet/parameter/supported_extensions_parameter.cc +++ b/net/dcsctp/packet/parameter/supported_extensions_parameter.cc @@ -11,11 +11,11 @@ #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "rtc_base/strings/str_join.h" @@ -38,7 +38,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional SupportedExtensionsParameter::Parse( - webrtc::ArrayView data) { + std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/supported_extensions_parameter.h b/net/dcsctp/packet/parameter/supported_extensions_parameter.h index 1953391a61a..81b3da4a533 100644 --- a/net/dcsctp/packet/parameter/supported_extensions_parameter.h +++ b/net/dcsctp/packet/parameter/supported_extensions_parameter.h @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -41,7 +41,7 @@ class SupportedExtensionsParameter : chunk_types_(std::move(chunk_types)) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; @@ -51,7 +51,7 @@ class SupportedExtensionsParameter chunk_types_.end(); } - webrtc::ArrayView chunk_types() const { return chunk_types_; } + std::span chunk_types() const { return chunk_types_; } private: std::vector chunk_types_; diff --git a/net/dcsctp/packet/parameter/supported_extensions_parameter_test.cc b/net/dcsctp/packet/parameter/supported_extensions_parameter_test.cc index e76d3b0fe2f..76f6376cb5a 100644 --- a/net/dcsctp/packet/parameter/supported_extensions_parameter_test.cc +++ b/net/dcsctp/packet/parameter/supported_extensions_parameter_test.cc @@ -12,7 +12,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/testing/testing_macros.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.cc b/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.cc index adad39377ae..23f1b395412 100644 --- a/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.cc +++ b/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.cc @@ -11,10 +11,10 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "net/dcsctp/public/types.h" @@ -32,8 +32,7 @@ namespace dcsctp { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ std::optional -ZeroChecksumAcceptableChunkParameter::Parse( - webrtc::ArrayView data) { +ZeroChecksumAcceptableChunkParameter::Parse(std::span data) { std::optional> reader = ParseTLV(data); if (!reader.has_value()) { return std::nullopt; diff --git a/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.h b/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.h index 139b6f612f5..a9084a8aabb 100644 --- a/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.h +++ b/net/dcsctp/packet/parameter/zero_checksum_acceptable_chunk_parameter.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/tlv_trait.h" #include "net/dcsctp/public/types.h" @@ -42,7 +42,7 @@ class ZeroChecksumAcceptableChunkParameter : error_detection_method_(error_detection_method) {} static std::optional Parse( - webrtc::ArrayView data); + std::span data); void SerializeTo(std::vector& out) const override; std::string ToString() const override; diff --git a/net/dcsctp/packet/sctp_packet.cc b/net/dcsctp/packet/sctp_packet.cc index 0440f3627f3..1b5893a5792 100644 --- a/net/dcsctp/packet/sctp_packet.cc +++ b/net/dcsctp/packet/sctp_packet.cc @@ -12,10 +12,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/math.h" #include "net/dcsctp/packet/bounded_byte_reader.h" @@ -105,9 +105,8 @@ std::vector SctpPacket::Builder::Build(bool write_checksum) { return out; } -std::optional SctpPacket::Parse( - webrtc::ArrayView data, - const DcSctpOptions& options) { +std::optional SctpPacket::Parse(std::span data, + const DcSctpOptions& options) { if (data.size() < kHeaderSize + kChunkTlvHeaderSize || data.size() > kMaxUdpPacketSize) { RTC_DLOG(LS_WARNING) << "Invalid packet size"; @@ -162,8 +161,8 @@ std::optional SctpPacket::Parse( std::vector descriptors; descriptors.reserve(kExpectedDescriptorCount); - webrtc::ArrayView descriptor_data = - webrtc::ArrayView(data_copy).subview(kHeaderSize); + std::span descriptor_data = + std::span(data_copy).subspan(kHeaderSize); while (!descriptor_data.empty()) { if (descriptor_data.size() < kChunkTlvHeaderSize) { RTC_DLOG(LS_WARNING) << "Too small chunk"; @@ -183,8 +182,8 @@ std::optional SctpPacket::Parse( return std::nullopt; } descriptors.emplace_back(type, flags, - descriptor_data.subview(0, padded_length)); - descriptor_data = descriptor_data.subview(padded_length); + descriptor_data.subspan(0, padded_length)); + descriptor_data = descriptor_data.subspan(padded_length); } // Note that iterators (and pointer) are guaranteed to be stable when moving a diff --git a/net/dcsctp/packet/sctp_packet.h b/net/dcsctp/packet/sctp_packet.h index bd602eddcc2..2c4db51b6c3 100644 --- a/net/dcsctp/packet/sctp_packet.h +++ b/net/dcsctp/packet/sctp_packet.h @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/chunk.h" #include "net/dcsctp/public/dcsctp_options.h" @@ -38,13 +38,11 @@ class SctpPacket { static constexpr size_t kHeaderSize = 12; struct ChunkDescriptor { - ChunkDescriptor(uint8_t type, - uint8_t flags, - webrtc::ArrayView data) + ChunkDescriptor(uint8_t type, uint8_t flags, std::span data) : type(type), flags(flags), data(data) {} uint8_t type; uint8_t flags; - webrtc::ArrayView data; + std::span data; }; SctpPacket(SctpPacket&& other) = default; @@ -87,16 +85,14 @@ class SctpPacket { }; // Parses `data` as an SCTP packet and returns it if it validates. - static std::optional Parse(webrtc::ArrayView data, + static std::optional Parse(std::span data, const DcSctpOptions& options); // Returns the SCTP common header. const CommonHeader& common_header() const { return common_header_; } // Returns the chunks (types and offsets) within the packet. - webrtc::ArrayView descriptors() const { - return descriptors_; - } + std::span descriptors() const { return descriptors_; } private: SctpPacket(const CommonHeader& common_header, diff --git a/net/dcsctp/packet/sctp_packet_test.cc b/net/dcsctp/packet/sctp_packet_test.cc index 2209f21bea2..2ccaa0ab6d4 100644 --- a/net/dcsctp/packet/sctp_packet_test.cc +++ b/net/dcsctp/packet/sctp_packet_test.cc @@ -13,7 +13,6 @@ #include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/math.h" #include "net/dcsctp/packet/chunk/abort_chunk.h" diff --git a/net/dcsctp/packet/tlv_trait.h b/net/dcsctp/packet/tlv_trait.h index cbe047dcfbe..ef18e6be8c9 100644 --- a/net/dcsctp/packet/tlv_trait.h +++ b/net/dcsctp/packet/tlv_trait.h @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -77,7 +77,7 @@ class TLVTrait { // Validates the data with regards to size, alignment and type. // If valid, returns a bounded buffer. static std::optional> ParseTLV( - webrtc::ArrayView data) { + std::span data) { if (data.size() < Config::kHeaderSize) { tlv_trait_impl::ReportInvalidSize(data.size(), Config::kHeaderSize); return std::nullopt; @@ -119,7 +119,7 @@ class TLVTrait { return std::nullopt; } } - return BoundedByteReader(data.subview(0, length)); + return BoundedByteReader(data.subspan(0, length)); } // Allocates space for data with a static header size, as defined by @@ -133,7 +133,7 @@ class TLVTrait { out.resize(offset + size); BoundedByteWriter tlv_header( - webrtc::ArrayView(out.data() + offset, kTlvHeaderSize)); + std::span(out.data() + offset, kTlvHeaderSize)); if (Config::kTypeSizeInBytes == 1) { tlv_header.template Store8<0>(static_cast(Config::kType)); } else { @@ -142,7 +142,7 @@ class TLVTrait { tlv_header.template Store16<2>(size); return BoundedByteWriter( - webrtc::ArrayView(out.data() + offset, size)); + std::span(out.data() + offset, size)); } private: diff --git a/net/dcsctp/packet/tlv_trait_test.cc b/net/dcsctp/packet/tlv_trait_test.cc index 1c43ee65b23..fd992d33e39 100644 --- a/net/dcsctp/packet/tlv_trait_test.cc +++ b/net/dcsctp/packet/tlv_trait_test.cc @@ -12,9 +12,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" #include "test/gmock.h" @@ -44,11 +44,11 @@ class OneByteChunk : public TLVTrait { writer.Store16<10>(0x0708); uint8_t variable_data[kVariableSize] = {0xDE, 0xAD, 0xBE, 0xEF}; - writer.CopyToVariableData(webrtc::ArrayView(variable_data)); + writer.CopyToVariableData(std::span(variable_data)); } static std::optional> Parse( - webrtc::ArrayView data) { + std::span data) { return ParseTLV(data); } }; @@ -93,11 +93,11 @@ class TwoByteChunk : public TLVTrait { writer.Store32<4>(0x01020304U); uint8_t variable_data[] = {0x05, 0x06, 0x07, 0x08, 0xDE, 0xAD, 0xBE, 0xEF}; - writer.CopyToVariableData(webrtc::ArrayView(variable_data)); + writer.CopyToVariableData(std::span(variable_data)); } static std::optional> Parse( - webrtc::ArrayView data) { + std::span data) { return ParseTLV(data); } }; diff --git a/net/dcsctp/public/BUILD.gn b/net/dcsctp/public/BUILD.gn index b42d53ef019..af542e7792e 100644 --- a/net/dcsctp/public/BUILD.gn +++ b/net/dcsctp/public/BUILD.gn @@ -10,7 +10,6 @@ import("../../../webrtc.gni") rtc_source_set("types") { deps = [ - "../../../api:array_view", "../../../api/units:time_delta", "../../../rtc_base:strong_alias", ] @@ -24,10 +23,8 @@ rtc_source_set("types") { rtc_library("socket") { deps = [ ":types", - "../../../api:array_view", "../../../api/task_queue", "../../../api/units:timestamp", - "../../../rtc_base:checks", "../../../rtc_base:strong_alias", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/strings:string_view", @@ -64,7 +61,6 @@ rtc_source_set("mocks") { ":factory", ":socket", ":types", - "../../../api:array_view", "../../../test:test_support", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -74,10 +70,9 @@ rtc_library("utils") { deps = [ ":socket", ":types", - "../../../api:array_view", "../../../rtc_base:logging", "../../../rtc_base:stringutils", - "../socket:dcsctp_socket", + "../../../rtc_base:text2pcap", "//third_party/abseil-cpp/absl/strings:string_view", ] sources = [ @@ -93,8 +88,6 @@ if (rtc_include_tests) { deps = [ ":mocks", ":types", - "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", ] sources = [ diff --git a/net/dcsctp/public/dcsctp_message.h b/net/dcsctp/public/dcsctp_message.h index bcf4486f2da..6d002f0291b 100644 --- a/net/dcsctp/public/dcsctp_message.h +++ b/net/dcsctp/public/dcsctp_message.h @@ -11,10 +11,10 @@ #define NET_DCSCTP_PUBLIC_DCSCTP_MESSAGE_H_ #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/public/types.h" namespace dcsctp { @@ -39,7 +39,7 @@ class DcSctpMessage { PPID ppid() const { return ppid_; } // The payload of the message. - webrtc::ArrayView payload() const { return payload_; } + std::span payload() const { return payload_; } // When destructing the message, extracts the payload. std::vector ReleasePayload() && { return std::move(payload_); } diff --git a/net/dcsctp/public/dcsctp_socket.h b/net/dcsctp/public/dcsctp_socket.h index 24a50ea0cdd..d3991972591 100644 --- a/net/dcsctp/public/dcsctp_socket.h +++ b/net/dcsctp/public/dcsctp_socket.h @@ -14,11 +14,11 @@ #include #include #include +#include #include #include "absl/base/attributes.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "api/units/timestamp.h" #include "net/dcsctp/public/dcsctp_handover_state.h" @@ -287,14 +287,13 @@ class DcSctpSocketCallbacks { // // Note that it's NOT ALLOWED to call into this library from within this // callback. - virtual void SendPacket(webrtc::ArrayView /* data */) {} + virtual void SendPacket(std::span /* data */) {} // Called when the library wants the packet serialized as `data` to be sent. // // Note that it's NOT ALLOWED to call into this library from within this // callback. - virtual SendPacketStatus SendPacketWithStatus( - webrtc::ArrayView data) { + virtual SendPacketStatus SendPacketWithStatus(std::span data) { SendPacket(data); return SendPacketStatus::kSuccess; } @@ -410,22 +409,21 @@ class DcSctpSocketCallbacks { // Indicates that a stream reset request has failed. // // It is allowed to call into this library from within this callback. - virtual void OnStreamsResetFailed( - webrtc::ArrayView outgoing_streams, - absl::string_view reason) = 0; + virtual void OnStreamsResetFailed(std::span outgoing_streams, + absl::string_view reason) = 0; // Indicates that a stream reset request has been performed. // // It is allowed to call into this library from within this callback. virtual void OnStreamsResetPerformed( - webrtc::ArrayView outgoing_streams) = 0; + std::span outgoing_streams) = 0; // When a peer has reset some of its outgoing streams, this will be called. An // empty list indicates that all streams have been reset. // // It is allowed to call into this library from within this callback. virtual void OnIncomingStreamsReset( - webrtc::ArrayView incoming_streams) = 0; + std::span incoming_streams) = 0; // Will be called when the amount of data buffered to be sent falls to or // below the threshold set when calling `SetBufferedAmountLowThreshold`. @@ -531,7 +529,7 @@ class DcSctpSocketInterface { virtual ~DcSctpSocketInterface() = default; // To be called when an incoming SCTP packet is to be processed. - virtual void ReceivePacket(webrtc::ArrayView data) = 0; + virtual void ReceivePacket(std::span data) = 0; // Returns the number of received messages that can be retrieved by calling // calling `::GetNextMessage`. @@ -558,9 +556,8 @@ class DcSctpSocketInterface { // Finishes the out-of-bands connection sequence and returns `true` if this // was successful. This will also trigger // `DcSctpSocketCallbacks::OnConnected`. - virtual bool ConnectWithConnectionToken( - webrtc::ArrayView my_data, - webrtc::ArrayView peer_data) { + virtual bool ConnectWithConnectionToken(std::span my_data, + std::span peer_data) { return false; } @@ -615,9 +612,8 @@ class DcSctpSocketInterface { // // This has identical semantics to Send, except that it may coalesce many // messages into a single SCTP packet if they would fit. - virtual std::vector SendMany( - webrtc::ArrayView messages, - const SendOptions& send_options) = 0; + virtual std::vector SendMany(std::span messages, + const SendOptions& send_options) = 0; // Resetting streams is an asynchronous operation and the results will // be notified using `DcSctpSocketCallbacks::OnStreamsResetDone()` on success @@ -635,7 +631,7 @@ class DcSctpSocketInterface { // supports stream resetting. Calling this method on e.g. a closed association // or streams that don't support resetting will not perform any operation. virtual ResetStreamsStatus ResetStreams( - webrtc::ArrayView outgoing_streams) = 0; + std::span outgoing_streams) = 0; // Returns the number of bytes of data currently queued to be sent on a given // stream. diff --git a/net/dcsctp/public/mock_dcsctp_socket.h b/net/dcsctp/public/mock_dcsctp_socket.h index ce8de2ac328..8a49a94d740 100644 --- a/net/dcsctp/public/mock_dcsctp_socket.h +++ b/net/dcsctp/public/mock_dcsctp_socket.h @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/public/dcsctp_handover_state.h" #include "net/dcsctp/public/dcsctp_message.h" #include "net/dcsctp/public/dcsctp_options.h" @@ -27,10 +27,7 @@ namespace dcsctp { class MockDcSctpSocket : public DcSctpSocketInterface { public: - MOCK_METHOD(void, - ReceivePacket, - (webrtc::ArrayView data), - (override)); + MOCK_METHOD(void, ReceivePacket, (std::span data), (override)); MOCK_METHOD(size_t, MessagesReady, (), (const, override)); MOCK_METHOD(std::optional, GetNextMessage, (), (override)); @@ -40,8 +37,7 @@ class MockDcSctpSocket : public DcSctpSocketInterface { MOCK_METHOD(void, Connect, (), (override)); MOCK_METHOD(bool, ConnectWithConnectionToken, - (webrtc::ArrayView, - webrtc::ArrayView), + (std::span, std::span), (override)); MOCK_METHOD(void, @@ -76,13 +72,13 @@ class MockDcSctpSocket : public DcSctpSocketInterface { MOCK_METHOD(std::vector, SendMany, - (webrtc::ArrayView messages, + (std::span messages, const SendOptions& send_options), (override)); MOCK_METHOD(ResetStreamsStatus, ResetStreams, - (webrtc::ArrayView outgoing_streams), + (std::span outgoing_streams), (override)); MOCK_METHOD(size_t, buffered_amount, (StreamID stream_id), (const, override)); diff --git a/net/dcsctp/public/packet_observer.h b/net/dcsctp/public/packet_observer.h index c0fa6b937de..68affb3f386 100644 --- a/net/dcsctp/public/packet_observer.h +++ b/net/dcsctp/public/packet_observer.h @@ -11,8 +11,8 @@ #define NET_DCSCTP_PUBLIC_PACKET_OBSERVER_H_ #include +#include -#include "api/array_view.h" #include "net/dcsctp/public/types.h" namespace dcsctp { @@ -24,13 +24,12 @@ class PacketObserver { virtual ~PacketObserver() = default; // Called when a packet is sent, with the current time (in milliseconds) as // `now`, and the packet payload as `payload`. - virtual void OnSentPacket(TimeMs now, - webrtc::ArrayView payload) = 0; + virtual void OnSentPacket(TimeMs now, std::span payload) = 0; // Called when a packet is received, with the current time (in milliseconds) // as `now`, and the packet payload as `payload`. virtual void OnReceivedPacket(TimeMs now, - webrtc::ArrayView payload) = 0; + std::span payload) = 0; }; } // namespace dcsctp diff --git a/net/dcsctp/public/text_pcap_packet_observer.cc b/net/dcsctp/public/text_pcap_packet_observer.cc index 467112dd9a9..93092bd2980 100644 --- a/net/dcsctp/public/text_pcap_packet_observer.cc +++ b/net/dcsctp/public/text_pcap_packet_observer.cc @@ -10,48 +10,29 @@ #include "net/dcsctp/public/text_pcap_packet_observer.h" #include +#include -#include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/public/types.h" #include "rtc_base/logging.h" -#include "rtc_base/strings/string_builder.h" +#include "rtc_base/text2pcap.h" namespace dcsctp { -void TextPcapPacketObserver::OnSentPacket( - dcsctp::TimeMs now, - webrtc::ArrayView payload) { - PrintPacket("O ", name_, now, payload); +void TextPcapPacketObserver::OnSentPacket(dcsctp::TimeMs now, + std::span payload) { + RTC_LOG(LS_VERBOSE) << "\n" + << webrtc::Text2Pcap::DumpPacket(/*outbound=*/true, + payload, *now) + << " # SCTP_PACKET " << name_; } void TextPcapPacketObserver::OnReceivedPacket( dcsctp::TimeMs now, - webrtc::ArrayView payload) { - PrintPacket("I ", name_, now, payload); -} - -void TextPcapPacketObserver::PrintPacket( - absl::string_view prefix, - absl::string_view socket_name, - dcsctp::TimeMs now, - webrtc::ArrayView payload) { - webrtc::StringBuilder s; - s << "\n" << prefix; - int64_t remaining = *now % (24 * 60 * 60 * 1000); - int hours = remaining / (60 * 60 * 1000); - remaining = remaining % (60 * 60 * 1000); - int minutes = remaining / (60 * 1000); - remaining = remaining % (60 * 1000); - int seconds = remaining / 1000; - int ms = remaining % 1000; - s.AppendFormat("%02d:%02d:%02d.%03d", hours, minutes, seconds, ms); - s << " 0000"; - for (uint8_t byte : payload) { - s.AppendFormat(" %02x", byte); - } - s << " # SCTP_PACKET " << socket_name; - RTC_LOG(LS_VERBOSE) << s.str(); + std::span payload) { + RTC_LOG(LS_VERBOSE) << "\n" + << webrtc::Text2Pcap::DumpPacket(/*outbound=*/false, + payload, *now) + << " # SCTP_PACKET " << name_; } } // namespace dcsctp diff --git a/net/dcsctp/public/text_pcap_packet_observer.h b/net/dcsctp/public/text_pcap_packet_observer.h index c368b3da74d..8c8d07c9675 100644 --- a/net/dcsctp/public/text_pcap_packet_observer.h +++ b/net/dcsctp/public/text_pcap_packet_observer.h @@ -11,10 +11,10 @@ #define NET_DCSCTP_PUBLIC_TEXT_PCAP_PACKET_OBSERVER_H_ #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/public/packet_observer.h" #include "net/dcsctp/public/types.h" @@ -27,17 +27,17 @@ class TextPcapPacketObserver : public dcsctp::PacketObserver { // Implementation of `dcsctp::PacketObserver`. void OnSentPacket(dcsctp::TimeMs now, - webrtc::ArrayView payload) override; + std::span payload) override; void OnReceivedPacket(dcsctp::TimeMs now, - webrtc::ArrayView payload) override; + std::span payload) override; // Prints a packet to the log. Exposed to allow it to be used in compatibility // tests suites that don't use PacketObserver. static void PrintPacket(absl::string_view prefix, absl::string_view socket_name, dcsctp::TimeMs now, - webrtc::ArrayView payload); + std::span payload); private: const std::string name_; diff --git a/net/dcsctp/rx/BUILD.gn b/net/dcsctp/rx/BUILD.gn index b2045ff3ea3..6545ac71eaf 100644 --- a/net/dcsctp/rx/BUILD.gn +++ b/net/dcsctp/rx/BUILD.gn @@ -10,14 +10,11 @@ import("../../../webrtc.gni") rtc_library("data_tracker") { deps = [ - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:logging", - "../../../rtc_base:stringutils", "../common:internal_types", "../common:sequence_numbers", "../packet:chunk", - "../packet:data", "../public:socket", "../timer", "//third_party/abseil-cpp/absl/algorithm:container", @@ -31,7 +28,6 @@ rtc_library("data_tracker") { rtc_source_set("reassembly_streams") { deps = [ - "../../../api:array_view", "../common:sequence_numbers", "../packet:chunk", "../packet:data", @@ -45,7 +41,6 @@ rtc_source_set("reassembly_streams") { rtc_library("interleaved_reassembly_streams") { deps = [ ":reassembly_streams", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:logging", "../common:internal_types", @@ -65,7 +60,6 @@ rtc_library("interleaved_reassembly_streams") { rtc_library("traditional_reassembly_streams") { deps = [ ":reassembly_streams", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:logging", "../common:internal_types", @@ -88,7 +82,6 @@ rtc_library("reassembly_queue") { ":interleaved_reassembly_streams", ":reassembly_streams", ":traditional_reassembly_streams", - "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:stringutils", @@ -97,7 +90,6 @@ rtc_library("reassembly_queue") { "../common:sequence_numbers", "../packet:chunk", "../packet:data", - "../packet:parameter", "../public:socket", "../public:types", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -119,12 +111,9 @@ if (rtc_include_tests) { ":reassembly_queue", ":reassembly_streams", ":traditional_reassembly_streams", - "../../../api:array_view", "../../../api/task_queue", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", "../common:handover_testing", "../common:internal_types", diff --git a/net/dcsctp/rx/interleaved_reassembly_streams.cc b/net/dcsctp/rx/interleaved_reassembly_streams.cc index 0e4dbe21fe8..69500825016 100644 --- a/net/dcsctp/rx/interleaved_reassembly_streams.cc +++ b/net/dcsctp/rx/interleaved_reassembly_streams.cc @@ -13,13 +13,13 @@ #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -224,8 +224,7 @@ int InterleavedReassemblyStreams::Add(UnwrappedTSN tsn, Data data) { size_t InterleavedReassemblyStreams::HandleForwardTsn( UnwrappedTSN /* new_cumulative_ack_tsn */, - webrtc::ArrayView - skipped_streams) { + std::span skipped_streams) { size_t removed_bytes = 0; for (const auto& skipped : skipped_streams) { removed_bytes += @@ -236,7 +235,7 @@ size_t InterleavedReassemblyStreams::HandleForwardTsn( } void InterleavedReassemblyStreams::ResetStreams( - webrtc::ArrayView stream_ids) { + std::span stream_ids) { if (stream_ids.empty()) { for (auto& entry : streams_) { entry.second.Reset(); diff --git a/net/dcsctp/rx/interleaved_reassembly_streams.h b/net/dcsctp/rx/interleaved_reassembly_streams.h index 45d661ed73e..c956b1b9e31 100644 --- a/net/dcsctp/rx/interleaved_reassembly_streams.h +++ b/net/dcsctp/rx/interleaved_reassembly_streams.h @@ -12,11 +12,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -36,12 +36,11 @@ class InterleavedReassemblyStreams : public ReassemblyStreams { int Add(UnwrappedTSN tsn, Data data) override; - size_t HandleForwardTsn( - UnwrappedTSN new_cumulative_ack_tsn, - webrtc::ArrayView - skipped_streams) override; + size_t HandleForwardTsn(UnwrappedTSN new_cumulative_ack_tsn, + std::span + skipped_streams) override; - void ResetStreams(webrtc::ArrayView stream_ids) override; + void ResetStreams(std::span stream_ids) override; HandoverReadinessStatus GetHandoverReadiness() const override; void AddHandoverState(DcSctpSocketHandoverState& state) override; diff --git a/net/dcsctp/rx/reassembly_queue.cc b/net/dcsctp/rx/reassembly_queue.cc index 5607221ca43..1d28f686a0d 100644 --- a/net/dcsctp/rx/reassembly_queue.cc +++ b/net/dcsctp/rx/reassembly_queue.cc @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -56,8 +56,7 @@ ReassemblyQueue::ReassemblyQueue(absl::string_view log_prefix, watermark_bytes_(max_size_bytes * kHighWatermarkLimit), streams_(CreateStreams( log_prefix_, - [this](webrtc::ArrayView tsns, - DcSctpMessage message) { + [this](std::span tsns, DcSctpMessage message) { AddReassembledMessage(tsns, std::move(message)); }, use_message_interleaving)) {} @@ -110,7 +109,7 @@ void ReassemblyQueue::Add(TSN tsn, Data data) { } void ReassemblyQueue::ResetStreamsAndLeaveDeferredReset( - webrtc::ArrayView stream_ids) { + std::span stream_ids) { RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Resetting streams: [" << webrtc::StrJoin(stream_ids, ",", [](webrtc::StringBuilder& sb, @@ -141,9 +140,8 @@ void ReassemblyQueue::ResetStreamsAndLeaveDeferredReset( RTC_DCHECK(IsConsistent()); } -void ReassemblyQueue::EnterDeferredReset( - TSN sender_last_assigned_tsn, - webrtc::ArrayView streams) { +void ReassemblyQueue::EnterDeferredReset(TSN sender_last_assigned_tsn, + std::span streams) { if (!deferred_reset_streams_.has_value()) { RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Entering deferred reset; sender_last_assigned_tsn=" @@ -165,9 +163,8 @@ std::optional ReassemblyQueue::GetNextMessage() { return ret; } -void ReassemblyQueue::AddReassembledMessage( - webrtc::ArrayView tsns, - DcSctpMessage message) { +void ReassemblyQueue::AddReassembledMessage(std::span tsns, + DcSctpMessage message) { RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Assembled message from TSN=[" << webrtc::StrJoin( tsns, ",", @@ -184,8 +181,7 @@ void ReassemblyQueue::AddReassembledMessage( void ReassemblyQueue::HandleForwardTsn( TSN new_cumulative_tsn, - webrtc::ArrayView - skipped_streams) { + std::span skipped_streams) { UnwrappedTSN tsn = tsn_unwrapper_.Unwrap(new_cumulative_tsn); if (deferred_reset_streams_.has_value() && diff --git a/net/dcsctp/rx/reassembly_queue.h b/net/dcsctp/rx/reassembly_queue.h index 504584243ce..f35ab565f9d 100644 --- a/net/dcsctp/rx/reassembly_queue.h +++ b/net/dcsctp/rx/reassembly_queue.h @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -93,17 +93,15 @@ class ReassemblyQueue { // partial reliability. void HandleForwardTsn( TSN new_cumulative_tsn, - webrtc::ArrayView - skipped_streams); + std::span skipped_streams); // Resets the provided streams and leaves deferred reset processing, if // enabled. - void ResetStreamsAndLeaveDeferredReset( - webrtc::ArrayView stream_ids); + void ResetStreamsAndLeaveDeferredReset(std::span stream_ids); // Enters deferred reset processing. void EnterDeferredReset(TSN sender_last_assigned_tsn, - webrtc::ArrayView streams); + std::span streams); // The number of payload bytes that have been queued. Note that the actual // memory usage is higher due to additional overhead of tracking received @@ -142,7 +140,7 @@ class ReassemblyQueue { }; bool IsConsistent() const; - void AddReassembledMessage(webrtc::ArrayView tsns, + void AddReassembledMessage(std::span tsns, DcSctpMessage message); const absl::string_view log_prefix_; diff --git a/net/dcsctp/rx/reassembly_queue_test.cc b/net/dcsctp/rx/reassembly_queue_test.cc index ff936257658..51910546790 100644 --- a/net/dcsctp/rx/reassembly_queue_test.cc +++ b/net/dcsctp/rx/reassembly_queue_test.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/common/handover_testing.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -103,12 +103,12 @@ TEST_F(ReassemblyQueueTest, SingleUnorderedChunkMessage) { TEST_F(ReassemblyQueueTest, LargeUnorderedChunkAllPermutations) { std::vector tsns = {10, 11, 12, 13}; - webrtc::ArrayView payload(kLongPayload); + std::span payload(kLongPayload); do { ReassemblyQueue reasm("log: ", kBufferSize); for (size_t i = 0; i < tsns.size(); i++) { - auto span = payload.subview((tsns[i] - 10) * 4, 4); + auto span = payload.subspan((tsns[i] - 10) * 4, 4); Data::IsBeginning is_beginning(tsns[i] == 10); Data::IsEnd is_end(tsns[i] == 13); @@ -138,11 +138,11 @@ TEST_F(ReassemblyQueueTest, SingleOrderedChunkMessage) { TEST_F(ReassemblyQueueTest, ManySmallOrderedMessages) { std::vector tsns = {10, 11, 12, 13}; - webrtc::ArrayView payload(kLongPayload); + std::span payload(kLongPayload); do { ReassemblyQueue reasm("log: ", kBufferSize); for (size_t i = 0; i < tsns.size(); i++) { - auto span = payload.subview((tsns[i] - 10) * 4, 4); + auto span = payload.subspan((tsns[i] - 10) * 4, 4); Data::IsBeginning is_beginning(true); Data::IsEnd is_end(true); @@ -154,10 +154,10 @@ TEST_F(ReassemblyQueueTest, ManySmallOrderedMessages) { } EXPECT_THAT( FlushMessages(reasm), - ElementsAre(SctpMessageIs(kStreamID, kPPID, payload.subview(0, 4)), - SctpMessageIs(kStreamID, kPPID, payload.subview(4, 4)), - SctpMessageIs(kStreamID, kPPID, payload.subview(8, 4)), - SctpMessageIs(kStreamID, kPPID, payload.subview(12, 4)))); + ElementsAre(SctpMessageIs(kStreamID, kPPID, payload.subspan(0, 4)), + SctpMessageIs(kStreamID, kPPID, payload.subspan(4, 4)), + SctpMessageIs(kStreamID, kPPID, payload.subspan(8, 4)), + SctpMessageIs(kStreamID, kPPID, payload.subspan(12, 4)))); } while (std::next_permutation(std::begin(tsns), std::end(tsns))); } @@ -348,12 +348,12 @@ TEST_F(ReassemblyQueueTest, UnorderedInterleavedMessagesAllPermutations) { StreamID stream_ids[] = {StreamID(1), StreamID(2), StreamID(1), StreamID(1), StreamID(2), StreamID(2)}; FSN fsns[] = {FSN(0), FSN(0), FSN(1), FSN(2), FSN(1), FSN(2)}; - webrtc::ArrayView payload(kSixBytePayload); + std::span payload(kSixBytePayload); do { ReassemblyQueue reasm("log: ", kBufferSize, /*use_message_interleaving=*/true); for (int i : indexes) { - auto span = payload.subview(*fsns[i] * 2, 2); + auto span = payload.subspan(*fsns[i] * 2, 2); Data::IsBeginning is_beginning(fsns[i] == FSN(0)); Data::IsEnd is_end(fsns[i] == FSN(2)); reasm.Add(tsns[i], Data(stream_ids[i], SSN(0), MID(0), fsns[i], kPPID, diff --git a/net/dcsctp/rx/reassembly_streams.h b/net/dcsctp/rx/reassembly_streams.h index d801ef729e0..5d10a08026d 100644 --- a/net/dcsctp/rx/reassembly_streams.h +++ b/net/dcsctp/rx/reassembly_streams.h @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" #include "net/dcsctp/packet/data.h" @@ -43,7 +43,7 @@ class ReassemblyStreams { // message has been assembled as well as indicating from which TSNs this // message was assembled from. using OnAssembledMessage = - std::function tsns, + std::function tsns, DcSctpMessage message)>; virtual ~ReassemblyStreams() = default; @@ -68,13 +68,12 @@ class ReassemblyStreams { // this operation. virtual size_t HandleForwardTsn( UnwrappedTSN new_cumulative_ack_tsn, - webrtc::ArrayView - skipped_streams) = 0; + std::span skipped_streams) = 0; // Called for incoming (possibly deferred) RE_CONFIG chunks asking for // either a few streams, or all streams (when the list is empty) to be // reset - to have their next SSN or Message ID to be zero. - virtual void ResetStreams(webrtc::ArrayView stream_ids) = 0; + virtual void ResetStreams(std::span stream_ids) = 0; virtual HandoverReadinessStatus GetHandoverReadiness() const = 0; virtual void AddHandoverState(DcSctpSocketHandoverState& state) = 0; diff --git a/net/dcsctp/rx/traditional_reassembly_streams.cc b/net/dcsctp/rx/traditional_reassembly_streams.cc index cae1d7dc61f..53932503dec 100644 --- a/net/dcsctp/rx/traditional_reassembly_streams.cc +++ b/net/dcsctp/rx/traditional_reassembly_streams.cc @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -288,8 +288,7 @@ int TraditionalReassemblyStreams::Add(UnwrappedTSN tsn, Data data) { size_t TraditionalReassemblyStreams::HandleForwardTsn( UnwrappedTSN new_cumulative_ack_tsn, - webrtc::ArrayView - skipped_streams) { + std::span skipped_streams) { size_t bytes_removed = 0; // The `skipped_streams` only cover ordered messages - need to // iterate all unordered streams manually to remove those chunks. @@ -307,7 +306,7 @@ size_t TraditionalReassemblyStreams::HandleForwardTsn( } void TraditionalReassemblyStreams::ResetStreams( - webrtc::ArrayView stream_ids) { + std::span stream_ids) { if (stream_ids.empty()) { for (auto& [stream_id, stream] : ordered_streams_) { RTC_DLOG(LS_VERBOSE) << log_prefix_ diff --git a/net/dcsctp/rx/traditional_reassembly_streams.h b/net/dcsctp/rx/traditional_reassembly_streams.h index dca34fbed5d..0338291fcfc 100644 --- a/net/dcsctp/rx/traditional_reassembly_streams.h +++ b/net/dcsctp/rx/traditional_reassembly_streams.h @@ -12,9 +12,9 @@ #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/forward_tsn_common.h" @@ -35,12 +35,11 @@ class TraditionalReassemblyStreams : public ReassemblyStreams { int Add(UnwrappedTSN tsn, Data data) override; - size_t HandleForwardTsn( - UnwrappedTSN new_cumulative_ack_tsn, - webrtc::ArrayView - skipped_streams) override; + size_t HandleForwardTsn(UnwrappedTSN new_cumulative_ack_tsn, + std::span + skipped_streams) override; - void ResetStreams(webrtc::ArrayView stream_ids) override; + void ResetStreams(std::span stream_ids) override; HandoverReadinessStatus GetHandoverReadiness() const override; void AddHandoverState(DcSctpSocketHandoverState& state) override; diff --git a/net/dcsctp/socket/BUILD.gn b/net/dcsctp/socket/BUILD.gn index f4107557774..fdb255a8f84 100644 --- a/net/dcsctp/socket/BUILD.gn +++ b/net/dcsctp/socket/BUILD.gn @@ -15,7 +15,6 @@ rtc_source_set("context") { "../common:internal_types", "../packet:sctp_packet", "../public:socket", - "../public:types", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -23,7 +22,6 @@ rtc_source_set("context") { rtc_library("heartbeat_handler") { deps = [ ":context", - "../../../api:array_view", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", @@ -47,18 +45,15 @@ rtc_library("heartbeat_handler") { rtc_library("stream_reset_handler") { deps = [ ":context", - "../../../api:array_view", "../../../api/units:time_delta", "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:stringutils", - "../../../rtc_base/containers:flat_set", "../common:internal_types", "../common:sequence_numbers", "../packet:chunk", "../packet:parameter", "../packet:sctp_packet", - "../packet:tlv_trait", "../public:socket", "../public:types", "../rx:data_tracker", @@ -76,11 +71,8 @@ rtc_library("stream_reset_handler") { rtc_library("packet_sender") { deps = [ - "../../../api:array_view", "../packet:sctp_packet", "../public:socket", - "../public:types", - "../timer", ] sources = [ "packet_sender.cc", @@ -94,7 +86,6 @@ rtc_library("transmission_control_block") { ":heartbeat_handler", ":packet_sender", ":stream_reset_handler", - "../../../api:array_view", "../../../api/task_queue", "../../../api/units:time_delta", "../../../api/units:timestamp", @@ -102,7 +93,6 @@ rtc_library("transmission_control_block") { "../../../rtc_base:logging", "../../../rtc_base:stringutils", "../common:internal_types", - "../common:sequence_numbers", "../packet:chunk", "../packet:sctp_packet", "../public:socket", @@ -126,15 +116,10 @@ rtc_library("transmission_control_block") { rtc_library("dcsctp_socket") { deps = [ - ":context", ":heartbeat_handler", ":packet_sender", ":stream_reset_handler", ":transmission_control_block", - "../../../api:array_view", - "../../../api:make_ref_counted", - "../../../api:refcountedbase", - "../../../api:scoped_refptr", "../../../api/task_queue", "../../../api/units:time_delta", "../../../api/units:timestamp", @@ -149,17 +134,13 @@ rtc_library("dcsctp_socket") { "../packet:error_cause", "../packet:parameter", "../packet:sctp_packet", - "../packet:tlv_trait", "../public:socket", "../public:types", "../rx:data_tracker", "../rx:reassembly_queue", "../timer", - "../tx:retransmission_error_counter", "../tx:retransmission_queue", - "../tx:retransmission_timeout", "../tx:rr_send_queue", - "../tx:send_queue", "//third_party/abseil-cpp/absl/functional:bind_front", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings:string_view", @@ -179,7 +160,6 @@ if (rtc_include_tests) { testonly = true sources = [ "mock_dcsctp_socket_callbacks.h" ] deps = [ - "../../../api:array_view", "../../../api/task_queue", "../../../api/units:time_delta", "../../../api/units:timestamp", @@ -220,7 +200,6 @@ if (rtc_include_tests) { ":packet_sender", ":stream_reset_handler", ":transmission_control_block", - "../../../api:array_view", "../../../api:create_network_emulation_manager", "../../../api:network_emulation_manager_api", "../../../api:simulated_network_api", @@ -230,16 +209,14 @@ if (rtc_include_tests) { "../../../api/units:data_rate", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../rtc_base:checks", "../../../rtc_base:copy_on_write_buffer", - "../../../rtc_base:gunit_helpers", "../../../rtc_base:logging", "../../../rtc_base:random", - "../../../rtc_base:rtc_base_tests_utils", "../../../rtc_base:socket_address", "../../../rtc_base:stringutils", "../../../rtc_base:threading", "../../../rtc_base:timeutils", + "../../../system_wrappers", "../../../test:test_support", "../common:handover_testing", "../common:internal_types", @@ -261,6 +238,7 @@ if (rtc_include_tests) { "../timer:task_queue_timeout", "../tx:mock_send_queue", "../tx:retransmission_queue", + "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings:string_view", diff --git a/net/dcsctp/socket/DEPS b/net/dcsctp/socket/DEPS index d4966290e33..07eee1dd575 100644 --- a/net/dcsctp/socket/DEPS +++ b/net/dcsctp/socket/DEPS @@ -1,5 +1,6 @@ specific_include_rules = { - "dcsctp_socket_network_test.cc": [ + "dcsctp_socket_network_test\\.cc": [ "+call", + "+system_wrappers/include/clock.h", ] } diff --git a/net/dcsctp/socket/callback_deferrer.cc b/net/dcsctp/socket/callback_deferrer.cc index 595732a21ea..5ed033acc30 100644 --- a/net/dcsctp/socket/callback_deferrer.cc +++ b/net/dcsctp/socket/callback_deferrer.cc @@ -11,13 +11,13 @@ #include #include +#include #include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "net/dcsctp/public/dcsctp_message.h" #include "net/dcsctp/public/dcsctp_socket.h" @@ -51,7 +51,7 @@ void CallbackDeferrer::TriggerDeferred() { } SendPacketStatus CallbackDeferrer::SendPacketWithStatus( - webrtc::ArrayView data) { + std::span data) { // Will not be deferred - call directly. return underlying_.SendPacketWithStatus(data); } @@ -140,7 +140,7 @@ void CallbackDeferrer::OnConnectionRestarted() { } void CallbackDeferrer::OnStreamsResetFailed( - webrtc::ArrayView outgoing_streams, + std::span outgoing_streams, absl::string_view reason) { RTC_DCHECK(prepared_); deferred_.emplace_back( @@ -154,7 +154,7 @@ void CallbackDeferrer::OnStreamsResetFailed( } void CallbackDeferrer::OnStreamsResetPerformed( - webrtc::ArrayView outgoing_streams) { + std::span outgoing_streams) { RTC_DCHECK(prepared_); deferred_.emplace_back( +[](CallbackData data, DcSctpSocketCallbacks& cb) { @@ -166,7 +166,7 @@ void CallbackDeferrer::OnStreamsResetPerformed( } void CallbackDeferrer::OnIncomingStreamsReset( - webrtc::ArrayView incoming_streams) { + std::span incoming_streams) { RTC_DCHECK(prepared_); deferred_.emplace_back( +[](CallbackData data, DcSctpSocketCallbacks& cb) { diff --git a/net/dcsctp/socket/callback_deferrer.h b/net/dcsctp/socket/callback_deferrer.h index 83e41528a8f..c0ea134bbcc 100644 --- a/net/dcsctp/socket/callback_deferrer.h +++ b/net/dcsctp/socket/callback_deferrer.h @@ -12,13 +12,13 @@ #include #include +#include #include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "api/units/timestamp.h" #include "net/dcsctp/public/dcsctp_message.h" @@ -61,8 +61,7 @@ class CallbackDeferrer : public DcSctpSocketCallbacks { : underlying_(underlying) {} // Implementation of DcSctpSocketCallbacks - SendPacketStatus SendPacketWithStatus( - webrtc::ArrayView data) override; + SendPacketStatus SendPacketWithStatus(std::span data) override; std::unique_ptr CreateTimeout( webrtc::TaskQueueBase::DelayPrecision precision) override; TimeMs TimeMillis() override; @@ -75,12 +74,12 @@ class CallbackDeferrer : public DcSctpSocketCallbacks { void OnConnected() override; void OnClosed() override; void OnConnectionRestarted() override; - void OnStreamsResetFailed(webrtc::ArrayView outgoing_streams, + void OnStreamsResetFailed(std::span outgoing_streams, absl::string_view reason) override; void OnStreamsResetPerformed( - webrtc::ArrayView outgoing_streams) override; + std::span outgoing_streams) override; void OnIncomingStreamsReset( - webrtc::ArrayView incoming_streams) override; + std::span incoming_streams) override; void OnBufferedAmountLow(StreamID stream_id) override; void OnTotalBufferedAmountLow() override; diff --git a/net/dcsctp/socket/dcsctp_socket.cc b/net/dcsctp/socket/dcsctp_socket.cc index 5b27f9a8bfe..0bcdbd5dd63 100644 --- a/net/dcsctp/socket/dcsctp_socket.cc +++ b/net/dcsctp/socket/dcsctp_socket.cc @@ -16,13 +16,13 @@ #include #include #include +#include #include #include #include #include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -172,7 +172,7 @@ TieTag MakeTieTag(DcSctpSocketCallbacks& cb) { } SctpImplementation DeterminePeerImplementation( - webrtc::ArrayView cookie) { + std::span cookie) { if (cookie.size() > 8) { absl::string_view magic(reinterpret_cast(cookie.data()), 8); if (magic == "dcSCTP00") { @@ -365,8 +365,8 @@ std::vector DcSctpSocket::GenerateConnectionToken( } bool DcSctpSocket::ConnectWithConnectionToken( - webrtc::ArrayView my_data, - webrtc::ArrayView peer_data) { + std::span my_data, + std::span peer_data) { CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); std::optional my_init = InitChunk::Parse(my_data); @@ -552,7 +552,7 @@ SendStatus DcSctpSocket::Send(DcSctpMessage message, } std::vector DcSctpSocket::SendMany( - webrtc::ArrayView messages, + std::span messages, const SendOptions& send_options) { CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); Timestamp now = callbacks_.Now(); @@ -618,7 +618,7 @@ SendStatus DcSctpSocket::InternalSend(const DcSctpMessage& message, } ResetStreamsStatus DcSctpSocket::ResetStreams( - webrtc::ArrayView outgoing_streams) { + std::span outgoing_streams) { CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); if (tcb_ == nullptr) { @@ -678,6 +678,7 @@ std::optional DcSctpSocket::GetMetrics() const { return std::nullopt; } + // Note: `metrics_` has some pre-calculated (negotiated) values set already. Metrics metrics = metrics_; metrics.cwnd_bytes = tcb_->cwnd(); metrics.srtt_ms = tcb_->current_srtt().ms(); @@ -688,10 +689,6 @@ std::optional DcSctpSocket::GetMetrics() const { (send_queue_.total_buffered_amount() + packet_payload_size - 1) / packet_payload_size; metrics.peer_rwnd_bytes = tcb_->retransmission_queue().rwnd(); - metrics.negotiated_maximum_incoming_streams = - tcb_->capabilities().negotiated_maximum_incoming_streams; - metrics.negotiated_maximum_incoming_streams = - tcb_->capabilities().negotiated_maximum_incoming_streams; metrics.rtx_packets_count = tcb_->retransmission_queue().rtx_packets_count(); metrics.rtx_bytes_count = tcb_->retransmission_queue().rtx_bytes_count(); @@ -840,7 +837,7 @@ void DcSctpSocket::HandleTimeout(TimeoutID timeout_id) { RTC_DCHECK(IsConsistent()); } -void DcSctpSocket::ReceivePacket(webrtc::ArrayView data) { +void DcSctpSocket::ReceivePacket(std::span data) { CallbackDeferrer::ScopedDeferrer deferrer(callbacks_); ++metrics_.rx_packets_count; @@ -890,8 +887,7 @@ void DcSctpSocket::ReceivePacket(webrtc::ArrayView data) { RTC_DCHECK(IsConsistent()); } -void DcSctpSocket::DebugPrintOutgoing( - webrtc::ArrayView payload) { +void DcSctpSocket::DebugPrintOutgoing(std::span payload) { auto packet = SctpPacket::Parse(payload, options_); RTC_DCHECK(packet.has_value()); @@ -1070,7 +1066,7 @@ TimeDelta DcSctpSocket::OnShutdownTimerExpiry() { return tcb_->current_rto(); } -void DcSctpSocket::OnSentPacket(webrtc::ArrayView packet, +void DcSctpSocket::OnSentPacket(std::span packet, SendPacketStatus status) { // The packet observer is invoked even if the packet was failed to be sent, to // indicate an attempt was made. diff --git a/net/dcsctp/socket/dcsctp_socket.h b/net/dcsctp/socket/dcsctp_socket.h index a3e3cb3f5fb..5f78957b05a 100644 --- a/net/dcsctp/socket/dcsctp_socket.h +++ b/net/dcsctp/socket/dcsctp_socket.h @@ -15,11 +15,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/time_delta.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/data_common.h" @@ -66,7 +66,7 @@ class DcSctpSocket : public DcSctpSocketInterface { DcSctpSocket& operator=(const DcSctpSocket&) = delete; // Implementation of `DcSctpSocketInterface`. - void ReceivePacket(webrtc::ArrayView data) override; + void ReceivePacket(std::span data) override; size_t MessagesReady() const override; std::optional GetNextMessage() override; void HandleTimeout(TimeoutID timeout_id) override; @@ -74,19 +74,18 @@ class DcSctpSocket : public DcSctpSocketInterface { static std::vector GenerateConnectionToken( const DcSctpOptions& options, std::function get_random_uint32); - bool ConnectWithConnectionToken( - webrtc::ArrayView my_data, - webrtc::ArrayView peer_data) override; + bool ConnectWithConnectionToken(std::span my_data, + std::span peer_data) override; void RestoreFromState(const DcSctpSocketHandoverState& state) override; void Shutdown() override; void Close() override; SendStatus Send(DcSctpMessage message, const SendOptions& send_options) override; - std::vector SendMany(webrtc::ArrayView messages, + std::vector SendMany(std::span messages, const SendOptions& send_options) override; ResetStreamsStatus ResetStreams( - webrtc::ArrayView outgoing_streams) override; + std::span outgoing_streams) override; SocketState state() const override; const DcSctpOptions& options() const override { return options_; } void SetMaxMessageSize(size_t max_message_size) override; @@ -151,8 +150,7 @@ class DcSctpSocket : public DcSctpSocketInterface { webrtc::TimeDelta OnInitTimerExpiry(); webrtc::TimeDelta OnCookieTimerExpiry(); webrtc::TimeDelta OnShutdownTimerExpiry(); - void OnSentPacket(webrtc::ArrayView packet, - SendPacketStatus status); + void OnSentPacket(std::span packet, SendPacketStatus status); // Sends SHUTDOWN or SHUTDOWN-ACK if the socket is shutting down and if all // outstanding data has been acknowledged. void MaybeSendShutdownOrAck(); @@ -174,7 +172,7 @@ class DcSctpSocket : public DcSctpSocketInterface { bool ValidatePacket(const SctpPacket& packet); // Parses `payload`, which is a serialized packet that is just going to be // sent and prints all chunks. - void DebugPrintOutgoing(webrtc::ArrayView payload); + void DebugPrintOutgoing(std::span payload); // Called whenever data has been received, or the cumulative acknowledgment // TSN has moved, that may result in delivering messages. void MaybeDeliverMessages(); diff --git a/net/dcsctp/socket/dcsctp_socket_network_test.cc b/net/dcsctp/socket/dcsctp_socket_network_test.cc index ff8d68ae6c1..3aa1efca205 100644 --- a/net/dcsctp/socket/dcsctp_socket_network_test.cc +++ b/net/dcsctp/socket/dcsctp_socket_network_test.cc @@ -12,12 +12,13 @@ #include #include #include +#include #include #include #include +#include "absl/base/nullability.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/test/create_network_emulation_manager.h" @@ -42,7 +43,7 @@ #include "rtc_base/strings/string_builder.h" #include "rtc_base/strings/string_format.h" #include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" +#include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" @@ -125,7 +126,7 @@ class BoundSocket : public webrtc::EmulatedNetworkReceiverInterface { receiver_ = std::move(receiver); } - void SendPacket(webrtc::ArrayView data) { + void SendPacket(std::span data) { endpoint_->SendPacket(source_address_, dest_address_, webrtc::CopyOnWriteBuffer(data.data(), data.size())); } @@ -147,10 +148,12 @@ class SctpActor : public DcSctpSocketCallbacks { public: SctpActor(absl::string_view name, BoundSocket& emulated_socket, - const DcSctpOptions& sctp_options) + const DcSctpOptions& sctp_options, + webrtc::Clock* absl_nonnull clock) : log_prefix_(std::string(name) + ": "), thread_(webrtc::Thread::Current()), emulated_socket_(emulated_socket), + clock_(*clock), timeout_factory_( *thread_, [this]() { return TimeMs(Now().ms()); }, @@ -188,7 +191,7 @@ class SctpActor : public DcSctpSocketCallbacks { } } - void SendPacket(webrtc::ArrayView data) override { + void SendPacket(std::span data) override { emulated_socket_.SendPacket(data); } @@ -197,7 +200,7 @@ class SctpActor : public DcSctpSocketCallbacks { return timeout_factory_.CreateTimeout(precision); } - Timestamp Now() override { return Timestamp::Millis(webrtc::TimeMillis()); } + Timestamp Now() override { return clock_.CurrentTime(); } uint32_t GetRandomInt(uint32_t low, uint32_t high) override { return random_.Rand(low, high); @@ -224,15 +227,14 @@ class SctpActor : public DcSctpSocketCallbacks { void OnConnectionRestarted() override {} - void OnStreamsResetFailed( - webrtc::ArrayView /* outgoing_streams */, - absl::string_view /* reason */) override {} + void OnStreamsResetFailed(std::span /* outgoing_streams */, + absl::string_view /* reason */) override {} void OnStreamsResetPerformed( - webrtc::ArrayView /* outgoing_streams */) override {} + std::span /* outgoing_streams */) override {} void OnIncomingStreamsReset( - webrtc::ArrayView /* incoming_streams */) override {} + std::span /* incoming_streams */) override {} void NotifyOutgoingMessageBufferEmpty() override {} @@ -313,7 +315,7 @@ class SctpActor : public DcSctpSocketCallbacks { std::string log_prefix() const { webrtc::StringBuilder sb; sb << log_prefix_; - sb << webrtc::TimeMillis(); + sb << clock_.CurrentTime().ms(); sb << ": "; return sb.Release(); } @@ -322,6 +324,7 @@ class SctpActor : public DcSctpSocketCallbacks { const std::string log_prefix_; webrtc::Thread* thread_; BoundSocket& emulated_socket_; + webrtc::Clock& clock_; TaskQueueTimeoutFactory timeout_factory_; webrtc::Random random_; DcSctpSocket sctp_socket_; @@ -376,8 +379,10 @@ TEST_F(DcSctpSocketNetworkTest, CanConnectAndShutdown) { webrtc::BuiltInNetworkBehaviorConfig pipe_config; MakeNetwork(pipe_config); - SctpActor sender("A", emulated_socket_a_, options_); - SctpActor receiver("Z", emulated_socket_z_, options_); + SctpActor sender("A", emulated_socket_a_, options_, + emulation_->time_controller()->GetClock()); + SctpActor receiver("Z", emulated_socket_z_, options_, + emulation_->time_controller()->GetClock()); EXPECT_THAT(sender.sctp_socket().state(), SocketState::kClosed); sender.sctp_socket().Connect(); @@ -394,8 +399,10 @@ TEST_F(DcSctpSocketNetworkTest, CanSendLargeMessage) { pipe_config.queue_delay_ms = 30; MakeNetwork(pipe_config); - SctpActor sender("A", emulated_socket_a_, options_); - SctpActor receiver("Z", emulated_socket_z_, options_); + SctpActor sender("A", emulated_socket_a_, options_, + emulation_->time_controller()->GetClock()); + SctpActor receiver("Z", emulated_socket_z_, options_, + emulation_->time_controller()->GetClock()); sender.sctp_socket().Connect(); constexpr size_t kPayloadSize = 100 * 1024; @@ -421,8 +428,10 @@ TEST_F(DcSctpSocketNetworkTest, CanSendMessagesReliablyWithLowBandwidth) { pipe_config.link_capacity = DataRate::KilobitsPerSec(1000); MakeNetwork(pipe_config); - SctpActor sender("A", emulated_socket_a_, options_); - SctpActor receiver("Z", emulated_socket_z_, options_); + SctpActor sender("A", emulated_socket_a_, options_, + emulation_->time_controller()->GetClock()); + SctpActor receiver("Z", emulated_socket_z_, options_, + emulation_->time_controller()->GetClock()); sender.sctp_socket().Connect(); sender.SetActorMode(ActorMode::kThroughputSender); @@ -450,8 +459,10 @@ TEST_F(DcSctpSocketNetworkTest, pipe_config.link_capacity = DataRate::KilobitsPerSec(18000); MakeNetwork(pipe_config); - SctpActor sender("A", emulated_socket_a_, options_); - SctpActor receiver("Z", emulated_socket_z_, options_); + SctpActor sender("A", emulated_socket_a_, options_, + emulation_->time_controller()->GetClock()); + SctpActor receiver("Z", emulated_socket_z_, options_, + emulation_->time_controller()->GetClock()); sender.sctp_socket().Connect(); sender.SetActorMode(ActorMode::kThroughputSender); @@ -478,8 +489,10 @@ TEST_F(DcSctpSocketNetworkTest, CanSendMessagesReliablyWithMuchPacketLoss) { config.loss_percent = 1; MakeNetwork(config); - SctpActor sender("A", emulated_socket_a_, options_); - SctpActor receiver("Z", emulated_socket_z_, options_); + SctpActor sender("A", emulated_socket_a_, options_, + emulation_->time_controller()->GetClock()); + SctpActor receiver("Z", emulated_socket_z_, options_, + emulation_->time_controller()->GetClock()); sender.sctp_socket().Connect(); sender.SetActorMode(ActorMode::kThroughputSender); @@ -507,8 +520,10 @@ TEST_F(DcSctpSocketNetworkTest, DCSCTP_NDEBUG_TEST(HasHighBandwidth)) { pipe_config.queue_delay_ms = 30; MakeNetwork(pipe_config); - SctpActor sender("A", emulated_socket_a_, options_); - SctpActor receiver("Z", emulated_socket_z_, options_); + SctpActor sender("A", emulated_socket_a_, options_, + emulation_->time_controller()->GetClock()); + SctpActor receiver("Z", emulated_socket_z_, options_, + emulation_->time_controller()->GetClock()); sender.sctp_socket().Connect(); sender.SetActorMode(ActorMode::kThroughputSender); diff --git a/net/dcsctp/socket/dcsctp_socket_test.cc b/net/dcsctp/socket/dcsctp_socket_test.cc index 88aef3dbbff..fcab2677ec2 100644 --- a/net/dcsctp/socket/dcsctp_socket_test.cc +++ b/net/dcsctp/socket/dcsctp_socket_test.cc @@ -20,7 +20,6 @@ #include "absl/flags/flag.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "net/dcsctp/common/handover_testing.h" diff --git a/net/dcsctp/socket/heartbeat_handler.cc b/net/dcsctp/socket/heartbeat_handler.cc index 1b52900d130..3162662b240 100644 --- a/net/dcsctp/socket/heartbeat_handler.cc +++ b/net/dcsctp/socket/heartbeat_handler.cc @@ -13,12 +13,12 @@ #include #include #include +#include #include #include #include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "net/dcsctp/packet/bounded_byte_reader.h" @@ -66,7 +66,7 @@ class HeartbeatInfo { } static std::optional Deserialize( - webrtc::ArrayView data) { + std::span data) { if (data.size() != kBufferSize) { RTC_LOG(LS_WARNING) << "Invalid heartbeat info: " << data.size() << " bytes"; diff --git a/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h b/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h index 7e405e5379e..c80f37331ad 100644 --- a/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h +++ b/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -60,7 +60,7 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks { random_(internal::GetUniqueSeed()), timeout_manager_([this]() { return now_; }) { ON_CALL(*this, SendPacketWithStatus) - .WillByDefault([this](webrtc::ArrayView data) { + .WillByDefault([this](std::span data) { sent_packets_.emplace_back( std::vector(data.begin(), data.end())); return SendPacketStatus::kSuccess; @@ -87,7 +87,7 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks { MOCK_METHOD(SendPacketStatus, SendPacketWithStatus, - (webrtc::ArrayView data), + (std::span data), (override)); std::unique_ptr CreateTimeout( @@ -116,16 +116,16 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks { MOCK_METHOD(void, OnConnectionRestarted, (), (override)); MOCK_METHOD(void, OnStreamsResetFailed, - (webrtc::ArrayView outgoing_streams, + (std::span outgoing_streams, absl::string_view reason), (override)); MOCK_METHOD(void, OnStreamsResetPerformed, - (webrtc::ArrayView outgoing_streams), + (std::span outgoing_streams), (override)); MOCK_METHOD(void, OnIncomingStreamsReset, - (webrtc::ArrayView incoming_streams), + (std::span incoming_streams), (override)); MOCK_METHOD(void, OnBufferedAmountLow, (StreamID stream_id), (override)); MOCK_METHOD(void, OnTotalBufferedAmountLow, (), (override)); diff --git a/net/dcsctp/socket/packet_sender.cc b/net/dcsctp/socket/packet_sender.cc index 9abe1b48d79..3f4de4d9068 100644 --- a/net/dcsctp/socket/packet_sender.cc +++ b/net/dcsctp/socket/packet_sender.cc @@ -11,17 +11,17 @@ #include #include +#include #include #include -#include "api/array_view.h" #include "net/dcsctp/packet/sctp_packet.h" #include "net/dcsctp/public/dcsctp_socket.h" namespace dcsctp { PacketSender::PacketSender(DcSctpSocketCallbacks& callbacks, - std::function, + std::function, SendPacketStatus)> on_sent_packet) : callbacks_(callbacks), on_sent_packet_(std::move(on_sent_packet)) {} diff --git a/net/dcsctp/socket/packet_sender.h b/net/dcsctp/socket/packet_sender.h index 69a74f9ff8d..d0039d0d21c 100644 --- a/net/dcsctp/socket/packet_sender.h +++ b/net/dcsctp/socket/packet_sender.h @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "net/dcsctp/packet/sctp_packet.h" #include "net/dcsctp/public/dcsctp_socket.h" @@ -25,8 +25,8 @@ namespace dcsctp { class PacketSender { public: PacketSender(DcSctpSocketCallbacks& callbacks, - std::function, - SendPacketStatus)> on_sent_packet); + std::function, SendPacketStatus)> + on_sent_packet); // Sends the packet, and returns true if it was sent successfully. bool Send(SctpPacket::Builder& builder, bool write_checksum = true); @@ -36,7 +36,7 @@ class PacketSender { // Callback that will be triggered for every send attempt, indicating the // status of the operation. - std::function, SendPacketStatus)> + std::function, SendPacketStatus)> on_sent_packet_; }; } // namespace dcsctp diff --git a/net/dcsctp/socket/packet_sender_test.cc b/net/dcsctp/socket/packet_sender_test.cc index be545809087..620072a57cf 100644 --- a/net/dcsctp/socket/packet_sender_test.cc +++ b/net/dcsctp/socket/packet_sender_test.cc @@ -10,8 +10,8 @@ #include "net/dcsctp/socket/packet_sender.h" #include +#include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/cookie_ack_chunk.h" #include "net/dcsctp/packet/sctp_packet.h" @@ -37,8 +37,7 @@ class PacketSenderTest : public testing::Test { DcSctpOptions options_; testing::NiceMock callbacks_; - testing::MockFunction, - SendPacketStatus)> + testing::MockFunction, SendPacketStatus)> on_send_fn_; PacketSender sender_; }; diff --git a/net/dcsctp/socket/state_cookie.cc b/net/dcsctp/socket/state_cookie.cc index 68a237b7aef..d5b05b54084 100644 --- a/net/dcsctp/socket/state_cookie.cc +++ b/net/dcsctp/socket/state_cookie.cc @@ -12,9 +12,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/bounded_byte_reader.h" #include "net/dcsctp/packet/bounded_byte_writer.h" @@ -50,7 +50,7 @@ std::vector StateCookie::Serialize() { } std::optional StateCookie::Deserialize( - webrtc::ArrayView cookie) { + std::span cookie) { if (cookie.size() != kCookieSize) { RTC_DLOG(LS_WARNING) << "Invalid state cookie: " << cookie.size() << " bytes"; diff --git a/net/dcsctp/socket/state_cookie.h b/net/dcsctp/socket/state_cookie.h index 90073586ab5..752f05157b5 100644 --- a/net/dcsctp/socket/state_cookie.h +++ b/net/dcsctp/socket/state_cookie.h @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/socket/capabilities.h" @@ -50,7 +50,7 @@ class StateCookie { // Deserializes the cookie, and returns std::nullopt if that failed. static std::optional Deserialize( - webrtc::ArrayView cookie); + std::span cookie); VerificationTag peer_tag() const { return peer_tag_; } VerificationTag my_tag() const { return my_tag_; } diff --git a/net/dcsctp/socket/stream_reset_handler.cc b/net/dcsctp/socket/stream_reset_handler.cc index 5a26bfca605..c4573e7a43d 100644 --- a/net/dcsctp/socket/stream_reset_handler.cc +++ b/net/dcsctp/socket/stream_reset_handler.cc @@ -12,9 +12,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/packet/chunk/reconfig_chunk.h" @@ -290,8 +290,8 @@ void StreamResetHandler::HandleResponse(const ParameterDescriptor& descriptor) { << webrtc::StrJoin(current_request_->streams(), ",", [](webrtc::StringBuilder& sb, StreamID stream_id) { sb << *stream_id; }); - // Force this request to be sent again, but with new req_seq_nbr. - current_request_->PrepareRetransmission(); + // Force this request to be sent again, but with the same req_seq_nbr. + current_request_->set_deferred(true); reconfig_timer_->set_duration(ctx_->current_rto()); reconfig_timer_->Start(); break; @@ -355,7 +355,7 @@ ReConfigChunk StreamResetHandler::MakeReconfigChunk() { } void StreamResetHandler::ResetStreams( - webrtc::ArrayView outgoing_streams) { + std::span outgoing_streams) { for (StreamID stream_id : outgoing_streams) { retransmission_queue_->PrepareResetStream(stream_id); } @@ -363,11 +363,17 @@ void StreamResetHandler::ResetStreams( TimeDelta StreamResetHandler::OnReconfigTimerExpiry() { if (current_request_->has_been_sent()) { - // There is an outstanding request, which timed out while waiting for a - // response. - if (!ctx_->IncrementTxErrorCounter("RECONFIG timeout")) { - // Timed out. The connection will close after processing the timers. - return TimeDelta::Zero(); + if (current_request_->is_deferred()) { + // The request was deferred (received "In Progress"). This is not a + // timeout, but just time to retry. + current_request_->set_deferred(false); + } else { + // There is an outstanding request, which timed out while waiting for a + // response. + if (!ctx_->IncrementTxErrorCounter("RECONFIG timeout")) { + // Timed out. The connection will close after processing the timers. + return TimeDelta::Zero(); + } } } else { // There is no outstanding request, but there is a prepared one. This means diff --git a/net/dcsctp/socket/stream_reset_handler.h b/net/dcsctp/socket/stream_reset_handler.h index cc8e0991347..06c1379f64f 100644 --- a/net/dcsctp/socket/stream_reset_handler.h +++ b/net/dcsctp/socket/stream_reset_handler.h @@ -12,12 +12,12 @@ #include #include +#include #include #include #include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/time_delta.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/sequence_numbers.h" @@ -97,7 +97,7 @@ class StreamResetHandler { // time and also multiple times. It will enqueue requests that can't be // directly fulfilled, and will asynchronously process them when any ongoing // request has completed. - void ResetStreams(webrtc::ArrayView outgoing_streams); + void ResetStreams(std::span outgoing_streams); // Creates a Reset Streams request that must be sent if returned. Will start // the reconfig timer. Will return std::nullopt if there is no need to @@ -163,6 +163,9 @@ class StreamResetHandler { req_seq_nbr_ = new_req_seq_nbr; } + void set_deferred(bool is_deferred) { is_deferred_ = is_deferred; } + bool is_deferred() const { return is_deferred_; } + private: // If this is set, this request has been sent. If it's not set, the request // has been prepared, but has not yet been sent. This is typically used when @@ -174,6 +177,9 @@ class StreamResetHandler { TSN sender_last_assigned_tsn_; // The streams that are to be reset in this request. const std::vector streams_; + // If the request is deferred (received "In Progress"), the next timeout + // should not be treated as a timeout. + bool is_deferred_ = false; }; // Called to validate an incoming RE-CONFIG chunk. diff --git a/net/dcsctp/socket/stream_reset_handler_test.cc b/net/dcsctp/socket/stream_reset_handler_test.cc index 415643027f2..b609bb2bf42 100644 --- a/net/dcsctp/socket/stream_reset_handler_test.cc +++ b/net/dcsctp/socket/stream_reset_handler_test.cc @@ -16,7 +16,6 @@ #include #include -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "api/units/time_delta.h" #include "net/dcsctp/common/handover_testing.h" @@ -657,11 +656,43 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResetRetransmitOnInProgress) { OutgoingSSNResetRequestParameter req2, reconfig2.parameters().get()); - EXPECT_EQ(req2.request_sequence_number(), - AddTo(req1.request_sequence_number(), 1)); + EXPECT_EQ(req2.request_sequence_number(), req1.request_sequence_number()); EXPECT_THAT(req2.stream_ids(), UnorderedElementsAre(kStreamToReset)); } +TEST_F(StreamResetHandlerTest, + SendOutgoingResetRetransmitOnInProgressDoesNotIncrementErrorCounter) { + static constexpr StreamID kStreamToReset = StreamID(42); + + EXPECT_CALL(producer_, PrepareResetStream(kStreamToReset)); + handler_->ResetStreams(std::vector({kStreamToReset})); + + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({kStreamToReset}))); + + std::optional reconfig1 = handler_->MakeStreamResetRequest(); + ASSERT_TRUE(reconfig1.has_value()); + ASSERT_HAS_VALUE_AND_ASSIGN( + OutgoingSSNResetRequestParameter req1, + reconfig1->parameters().get()); + + // Simulate that the peer responded "In Progress". + Parameters::Builder builder; + builder.Add(ReconfigurationResponseParameter(req1.request_sequence_number(), + ResponseResult::kInProgress)); + ReConfigChunk response_reconfig(builder.Build()); + + // Processing "In Progress" should NOT increment error counter. + EXPECT_CALL(ctx_, IncrementTxErrorCounter).Times(0); + handler_->HandleReConfig(std::move(response_reconfig)); + + // Timer expires. Should re-send, but NOT increment error counter. + EXPECT_CALL(ctx_, IncrementTxErrorCounter).Times(0); + EXPECT_CALL(callbacks_, SendPacketWithStatus).Times(1); + AdvanceTime(kRto); +} + TEST_F(StreamResetHandlerTest, ResetWhileRequestIsSentWillQueue) { EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); diff --git a/net/dcsctp/socket/transmission_control_block_test.cc b/net/dcsctp/socket/transmission_control_block_test.cc index 005df89d09e..070c38ed93a 100644 --- a/net/dcsctp/socket/transmission_control_block_test.cc +++ b/net/dcsctp/socket/transmission_control_block_test.cc @@ -11,8 +11,8 @@ #include #include +#include -#include "api/array_view.h" #include "api/task_queue/task_queue_base.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/public/dcsctp_options.h" @@ -49,8 +49,7 @@ class TransmissionControlBlockTest : public testing::Test { Capabilities capabilities_; StrictMock callbacks_; StrictMock send_queue_; - testing::MockFunction, - SendPacketStatus)> + testing::MockFunction, SendPacketStatus)> on_send_fn_; testing::MockFunction on_connection_established; PacketSender sender_; diff --git a/net/dcsctp/testing/BUILD.gn b/net/dcsctp/testing/BUILD.gn index 069d486557b..bcc997cda67 100644 --- a/net/dcsctp/testing/BUILD.gn +++ b/net/dcsctp/testing/BUILD.gn @@ -16,8 +16,6 @@ rtc_source_set("testing_macros") { rtc_library("data_generator") { testonly = true deps = [ - "../../../api:array_view", - "../../../rtc_base:checks", "../common:internal_types", "../packet:data", "../public:types", diff --git a/net/dcsctp/timer/BUILD.gn b/net/dcsctp/timer/BUILD.gn index c33473df41d..11836ed5a21 100644 --- a/net/dcsctp/timer/BUILD.gn +++ b/net/dcsctp/timer/BUILD.gn @@ -10,13 +10,11 @@ import("../../../webrtc.gni") rtc_library("timer") { deps = [ - "../../../api:array_view", "../../../api/task_queue", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", "../../../rtc_base:strong_alias", - "../../../rtc_base/containers:flat_map", "../../../rtc_base/containers:flat_set", "../public:socket", "../public:types", @@ -32,7 +30,6 @@ rtc_library("timer") { rtc_library("task_queue_timeout") { deps = [ - "../../../api:array_view", "../../../api:scoped_refptr", "../../../api:sequence_checker", "../../../api/task_queue", @@ -59,13 +56,10 @@ if (rtc_include_tests) { deps = [ ":task_queue_timeout", ":timer", - "../../../api:array_view", "../../../api/task_queue", "../../../api/task_queue/test:mock_task_queue_base", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../rtc_base:threading", "../../../test:test_support", "../../../test/time_controller", diff --git a/net/dcsctp/tx/BUILD.gn b/net/dcsctp/tx/BUILD.gn index 147bee69068..41eaca3a5a8 100644 --- a/net/dcsctp/tx/BUILD.gn +++ b/net/dcsctp/tx/BUILD.gn @@ -10,12 +10,9 @@ import("../../../webrtc.gni") rtc_source_set("send_queue") { deps = [ - "../../../api:array_view", "../../../api/units:timestamp", "../common:internal_types", - "../packet:chunk", "../packet:data", - "../public:socket", "../public:types", ] sources = [ "send_queue.h" ] @@ -25,13 +22,11 @@ rtc_library("rr_send_queue") { deps = [ ":send_queue", ":stream_scheduler", - "../../../api:array_view", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", "../../../rtc_base:logging", "../../../rtc_base:stringutils", - "../../../rtc_base/containers:flat_map", "../common:internal_types", "../packet:data", "../public:socket", @@ -48,7 +43,6 @@ rtc_library("rr_send_queue") { rtc_library("stream_scheduler") { deps = [ ":send_queue", - "../../../api:array_view", "../../../api/units:timestamp", "../../../rtc_base:checks", "../../../rtc_base:logging", @@ -58,7 +52,6 @@ rtc_library("stream_scheduler") { "../packet:chunk", "../packet:data", "../packet:sctp_packet", - "../public:socket", "../public:types", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/memory", @@ -72,7 +65,6 @@ rtc_library("stream_scheduler") { rtc_library("retransmission_error_counter") { deps = [ - "../../../rtc_base:checks", "../../../rtc_base:logging", "../public:types", "//third_party/abseil-cpp/absl/strings:string_view", @@ -86,7 +78,6 @@ rtc_library("retransmission_error_counter") { rtc_library("retransmission_timeout") { deps = [ "../../../api/units:time_delta", - "../../../rtc_base:checks", "../public:types", ] sources = [ @@ -97,23 +88,17 @@ rtc_library("retransmission_timeout") { rtc_library("outstanding_data") { deps = [ - ":retransmission_timeout", - ":send_queue", - "../../../api:array_view", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", "../../../rtc_base:logging", - "../../../rtc_base:stringutils", "../../../rtc_base/containers:flat_set", "../common:internal_types", "../common:math", "../common:sequence_numbers", "../packet:chunk", "../packet:data", - "../public:socket", "../public:types", - "../timer", ] sources = [ "outstanding_data.cc", @@ -124,9 +109,7 @@ rtc_library("outstanding_data") { rtc_library("retransmission_queue") { deps = [ ":outstanding_data", - ":retransmission_timeout", ":send_queue", - "../../../api:array_view", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", @@ -154,7 +137,6 @@ if (rtc_include_tests) { testonly = true deps = [ ":send_queue", - "../../../api:array_view", "../../../api/units:timestamp", "../../../test:test_support", "../common:internal_types", @@ -175,12 +157,9 @@ if (rtc_include_tests) { ":rr_send_queue", ":send_queue", ":stream_scheduler", - "../../../api:array_view", "../../../api/task_queue", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../rtc_base:checks", - "../../../rtc_base:gunit_helpers", "../../../test:test_support", "../common:handover_testing", "../common:internal_types", @@ -192,7 +171,6 @@ if (rtc_include_tests) { "../public:socket", "../public:types", "../socket:mock_callbacks", - "../socket:mock_callbacks", "../testing:data_generator", "../testing:testing_macros", "../timer", diff --git a/net/dcsctp/tx/outstanding_data.cc b/net/dcsctp/tx/outstanding_data.cc index 63e750b5fc3..b996dbdbd4d 100644 --- a/net/dcsctp/tx/outstanding_data.cc +++ b/net/dcsctp/tx/outstanding_data.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "net/dcsctp/common/internal_types.h" @@ -141,8 +141,11 @@ void OutstandingData::AckChunk(AckInfo& ack_info, OutstandingData::AckInfo OutstandingData::HandleSack( UnwrappedTSN cumulative_tsn_ack, - webrtc::ArrayView gap_ack_blocks, + std::span gap_ack_blocks, bool is_in_fast_recovery) { + bool cumulative_tsn_ack_advanced = + cumulative_tsn_ack > last_cumulative_tsn_ack_; + OutstandingData::AckInfo ack_info(cumulative_tsn_ack); // Erase all items up to cumulative_tsn_ack. RemoveAcked(cumulative_tsn_ack, ack_info); @@ -152,7 +155,7 @@ OutstandingData::AckInfo OutstandingData::HandleSack( // NACK and possibly mark for retransmit chunks that weren't acked. NackBetweenAckBlocks(cumulative_tsn_ack, gap_ack_blocks, is_in_fast_recovery, - ack_info); + cumulative_tsn_ack_advanced, ack_info); RTC_DCHECK(IsConsistent()); return ack_info; @@ -202,7 +205,7 @@ void OutstandingData::RemoveAcked(UnwrappedTSN cumulative_tsn_ack, void OutstandingData::AckGapBlocks( UnwrappedTSN cumulative_tsn_ack, - webrtc::ArrayView gap_ack_blocks, + std::span gap_ack_blocks, AckInfo& ack_info) { // Mark all non-gaps as ACKED (but they can't be removed) as (from RFC) // "SCTP considers the information carried in the Gap Ack Blocks in the @@ -223,8 +226,9 @@ void OutstandingData::AckGapBlocks( void OutstandingData::NackBetweenAckBlocks( UnwrappedTSN cumulative_tsn_ack, - webrtc::ArrayView gap_ack_blocks, + std::span gap_ack_blocks, bool is_in_fast_recovery, + bool cumulative_tsn_acked_advanced, OutstandingData::AckInfo& ack_info) { // Mark everything between the blocks as NACKED/TO_BE_RETRANSMITTED. // https://tools.ietf.org/html/rfc4960#section-7.2.4 @@ -237,7 +241,7 @@ void OutstandingData::NackBetweenAckBlocks( // in-flight and between gaps should be nacked. This means that SCTP relies on // the T3-RTX-timer to re-send packets otherwise. UnwrappedTSN max_tsn_to_nack = ack_info.highest_tsn_acked; - if (is_in_fast_recovery && cumulative_tsn_ack > last_cumulative_tsn_ack_) { + if (is_in_fast_recovery && cumulative_tsn_acked_advanced) { // https://tools.ietf.org/html/rfc4960#section-7.2.4 // "If an endpoint is in Fast Recovery and a SACK arrives that advances // the Cumulative TSN Ack Point, the miss indications are incremented for @@ -252,7 +256,8 @@ void OutstandingData::NackBetweenAckBlocks( UnwrappedTSN cur_block_first_acked = UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start); for (UnwrappedTSN tsn = prev_block_last_acked.next_value(); - tsn < cur_block_first_acked && tsn <= max_tsn_to_nack; + tsn < cur_block_first_acked && tsn <= max_tsn_to_nack && + tsn < next_tsn(); tsn = tsn.next_value()) { ack_info.has_packet_loss |= NackItem(tsn, /*retransmit_now=*/false, @@ -271,13 +276,17 @@ bool OutstandingData::NackItem(UnwrappedTSN tsn, bool retransmit_now, bool do_fast_retransmit) { Item& item = GetItem(tsn); - if (item.is_outstanding()) { + bool was_outstanding = item.is_outstanding(); + + Item::NackAction action = item.Nack(retransmit_now); + + if (was_outstanding && !item.is_outstanding()) { unacked_payload_bytes_ -= item.data().size(); unacked_packet_bytes_ -= GetSerializedChunkSize(item.data()); --unacked_items_; } - switch (item.Nack(retransmit_now)) { + switch (action) { case Item::NackAction::kNothing: return false; case Item::NackAction::kRetransmit: @@ -335,7 +344,13 @@ void OutstandingData::AbandonAllFor(const Item& item) { to_be_fast_retransmitted_.erase(tsn); to_be_retransmitted_.erase(tsn); } + bool was_outstanding = other.is_outstanding(); other.Abandon(); + if (was_outstanding) { + unacked_payload_bytes_ -= other.data().size(); + unacked_packet_bytes_ -= GetSerializedChunkSize(other.data()); + --unacked_items_; + } } } } @@ -517,10 +532,12 @@ OutstandingData::GetChunkStatesForTesting() const { state = State::kToBeRetransmitted; } else if (item.is_acked()) { state = State::kAcked; + } else if (item.is_nacked()) { + state = State::kNacked; } else if (item.is_outstanding()) { state = State::kInFlight; } else { - state = State::kNacked; + RTC_CHECK_NOTREACHED(); } states.emplace_back(tsn.Wrap(), state); diff --git a/net/dcsctp/tx/outstanding_data.h b/net/dcsctp/tx/outstanding_data.h index 512385e6fef..53037641519 100644 --- a/net/dcsctp/tx/outstanding_data.h +++ b/net/dcsctp/tx/outstanding_data.h @@ -16,10 +16,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "net/dcsctp/common/internal_types.h" @@ -89,10 +89,9 @@ class OutstandingData { last_cumulative_tsn_ack_(last_cumulative_tsn_ack), discard_from_send_queue_(std::move(discard_from_send_queue)) {} - AckInfo HandleSack( - UnwrappedTSN cumulative_tsn_ack, - webrtc::ArrayView gap_ack_blocks, - bool is_in_fast_recovery); + AckInfo HandleSack(UnwrappedTSN cumulative_tsn_ack, + std::span gap_ack_blocks, + bool is_in_fast_recovery); // Returns as many of the chunks that are eligible for fast retransmissions // and that would fit in a single packet of `max_size`. The eligible chunks @@ -227,7 +226,9 @@ class OutstandingData { // Marks this item as abandoned. void Abandon(); - bool is_outstanding() const { return ack_state_ == AckState::kUnacked; } + bool is_outstanding() const { + return ack_state_ != AckState::kAcked && lifecycle_ == Lifecycle::kActive; + } bool is_acked() const { return ack_state_ == AckState::kAcked; } bool is_nacked() const { return ack_state_ == AckState::kNacked; } bool is_abandoned() const { return lifecycle_ == Lifecycle::kAbandoned; } @@ -312,10 +313,9 @@ class OutstandingData { // Will mark the chunks covered by the `gap_ack_blocks` from an incoming SACK // as "acked" and update `ack_info` by adding new TSNs to `added_tsns`. - void AckGapBlocks( - UnwrappedTSN cumulative_tsn_ack, - webrtc::ArrayView gap_ack_blocks, - AckInfo& ack_info); + void AckGapBlocks(UnwrappedTSN cumulative_tsn_ack, + std::span gap_ack_blocks, + AckInfo& ack_info); // Mark chunks reported as "missing", as "nacked" or "to be retransmitted" // depending how many times this has happened. Only packets up until @@ -323,8 +323,9 @@ class OutstandingData { // nacked/retransmitted. The method will set `ack_info.has_packet_loss`. void NackBetweenAckBlocks( UnwrappedTSN cumulative_tsn_ack, - webrtc::ArrayView gap_ack_blocks, + std::span gap_ack_blocks, bool is_in_fast_recovery, + bool cumulative_tsn_acked_advanced, OutstandingData::AckInfo& ack_info); // Process the acknowledgement of the chunk referenced by `iter` and updates diff --git a/net/dcsctp/tx/outstanding_data_test.cc b/net/dcsctp/tx/outstanding_data_test.cc index fac4d7b0e33..7818d9b08de 100644 --- a/net/dcsctp/tx/outstanding_data_test.cc +++ b/net/dcsctp/tx/outstanding_data_test.cc @@ -135,8 +135,9 @@ TEST_F(OutstandingDataTest, AcksAndNacksWithGapAckBlocks) { EXPECT_EQ(ack.highest_tsn_acked.Wrap(), TSN(11)); EXPECT_FALSE(ack.has_packet_loss); - EXPECT_EQ(buf_.unacked_payload_bytes(), 0u); - EXPECT_EQ(buf_.unacked_items(), 0u); + // TSN (10) is still outstanding. + EXPECT_EQ(buf_.unacked_payload_bytes(), 1u); + EXPECT_EQ(buf_.unacked_items(), 1u); EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); EXPECT_EQ(buf_.last_cumulative_tsn_ack().Wrap(), TSN(9)); EXPECT_EQ(buf_.next_tsn().Wrap(), TSN(12)); @@ -675,5 +676,76 @@ TEST_F(OutstandingDataTest, TreatsUnackedPayloadBytesDifferentFromPacketBytes) { EXPECT_EQ(buf_.unacked_items(), 2u); } +TEST_F(OutstandingDataTest, + FastRecoveryIncrementsNackCountWhenCumulativeTsnAdvances) { + // This test verifies that the Fast Recovery retransmission rules are + // correctly applied when the Cumulative TSN Ack point advances. RFC 9260 + // Section 7.2.4: "If an endpoint is in Fast Recovery and a SACK arrives that + // advances the Cumulative TSN Ack Point, the miss indications are incremented + // for all TSNs reported missing in the SACK." + + for (int i = 10; i <= 16; ++i) { + buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow); + } + + // SACK 1: Cumulative Ack = 10. Gap blocks for 12, 14, 16. + // Missing: 11, 13, 15. + // This marks 12, 14, 16 as Acked. + // TSNs 11, 13, 15 get their 1st miss indication each. + std::vector gab1 = { + SackChunk::GapAckBlock(2, 2), // TSN 12 + SackChunk::GapAckBlock(4, 4), // TSN 14 + SackChunk::GapAckBlock(6, 6) // TSN 16 + }; + buf_.HandleSack(unwrapper_.Unwrap(TSN(10)), gab1, + /*is_in_fast_recovery=*/false); + + // SACK 2: Cumulative Ack advances to 11. Same gap blocks (12, 14, 16). + // Endpoint is now in Fast Recovery (is_in_fast_recovery = true). Because the + // Cumulative TSN Ack Point advanced from 10 to 11, 13 and 15 should get their + // 2nd miss indication. + std::vector gab2 = { + SackChunk::GapAckBlock(1, 1), // TSN 12 + SackChunk::GapAckBlock(3, 3), // TSN 14 + SackChunk::GapAckBlock(5, 5) // TSN 16 + }; + buf_.HandleSack(unwrapper_.Unwrap(TSN(11)), gab2, + /*is_in_fast_recovery=*/true); + + // SACK 3: Cumulative Ack advances to 12. + // Note: TSN 12 was already acked via gap block, so this just advances the + // Cumulative Ack. 13 and 15 should get their 3rd miss indication and trigger + // retransmission. + std::vector gab3 = { + SackChunk::GapAckBlock(2, 2), // TSN 14 + SackChunk::GapAckBlock(4, 4) // TSN 16 + }; + buf_.HandleSack(unwrapper_.Unwrap(TSN(12)), gab3, + /*is_in_fast_recovery=*/true); + + // 13 and 15 should now be retransmitted. + EXPECT_THAT(buf_.GetChunkStatesForTesting(), + ElementsAre(Pair(TSN(12), State::kAcked), + Pair(TSN(13), State::kToBeRetransmitted), + Pair(TSN(14), State::kAcked), + Pair(TSN(15), State::kToBeRetransmitted), + Pair(TSN(16), State::kAcked))); +} + +TEST_F(OutstandingDataTest, NackBetweenAckBlocksDoesNotAccessOutOfBounds) { + for (int i = 0; i < 5; ++i) { + buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow); + } + + // Inject a malformed SACK where the GapAckBlock exceeds the number of + // outstanding items, potentially triggering an OOB read/write. + std::vector malformed_blocks = { + SackChunk::GapAckBlock(1, 40000)}; + + // This should not crash or trigger ASAN errors. + buf_.HandleSack(unwrapper_.Unwrap(TSN(10)), malformed_blocks, + /*is_in_fast_recovery=*/false); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_queue.cc b/net/dcsctp/tx/retransmission_queue.cc index 9557a9be226..2cfc6ef087f 100644 --- a/net/dcsctp/tx/retransmission_queue.cc +++ b/net/dcsctp/tx/retransmission_queue.cc @@ -19,7 +19,6 @@ #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "net/dcsctp/common/internal_types.h" @@ -251,6 +250,14 @@ bool RetransmissionQueue::IsSackValid(const SackChunk& sack) const { } else if (cumulative_tsn_ack > outstanding_data_.highest_outstanding_tsn()) { return false; } + + for (const auto& block : sack.gap_ack_blocks()) { + if (UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end) > + outstanding_data_.highest_outstanding_tsn()) { + return false; + } + } + return true; } diff --git a/net/dcsctp/tx/rr_send_queue.cc b/net/dcsctp/tx/rr_send_queue.cc index 537f5f35a76..2b0883ae99b 100644 --- a/net/dcsctp/tx/rr_send_queue.cc +++ b/net/dcsctp/tx/rr_send_queue.cc @@ -9,19 +9,20 @@ */ #include "net/dcsctp/tx/rr_send_queue.h" +#include #include #include #include #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "net/dcsctp/common/internal_types.h" @@ -174,9 +175,10 @@ std::optional RRSendQueue::OutgoingStream::Produce( } // Grab the next `max_size` fragment from this message and calculate flags. - webrtc::ArrayView chunk_payload = - item.message.payload().subview(item.remaining_offset, max_size); - webrtc::ArrayView message_payload = message.payload(); + std::span message_payload = message.payload(); + std::span chunk_payload = message_payload.subspan( + item.remaining_offset, + std::min(message_payload.size() - item.remaining_offset, max_size)); Data::IsBeginning is_beginning(chunk_payload.data() == message_payload.data()); Data::IsEnd is_end((chunk_payload.data() + chunk_payload.size()) == diff --git a/net/dcsctp/tx/rr_send_queue.h b/net/dcsctp/tx/rr_send_queue.h index 07148cccc91..ae24c49ba8f 100644 --- a/net/dcsctp/tx/rr_send_queue.h +++ b/net/dcsctp/tx/rr_send_queue.h @@ -20,7 +20,6 @@ #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/timestamp.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/public/dcsctp_handover_state.h" diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index 57b0fa629c4..344d12756c3 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -47,7 +47,6 @@ rtc_library("async_stun_tcp_socket") { "base/async_stun_tcp_socket.h", ] deps = [ - "../api:array_view", "../api/environment", "../api/transport:stun_types", "../rtc_base:async_packet_socket", @@ -78,7 +77,6 @@ rtc_library("basic_ice_controller") { ":p2p_constants", ":p2p_transport_channel_ice_field_trials", ":transport_description", - "../api:array_view", "../api:candidate", "../api/environment", "../api/transport:enums", @@ -188,7 +186,6 @@ rtc_library("connection") { ":port_interface", ":stun_request", ":transport_description", - "../api:array_view", "../api:candidate", "../api:rtc_error", "../api:sequence_checker", @@ -267,7 +264,6 @@ rtc_library("dtls_transport") { ":dtls_utils", ":ice_transport_internal", ":packet_transport_internal", - "../api:array_view", "../api:dtls_transport_interface", "../api:field_trials_view", "../api:ice_transport_interface", @@ -331,6 +327,7 @@ rtc_library("dtls_transport_internal") { "../api:scoped_refptr", "../rtc_base:buffer", "../rtc_base:callback_list", + "../rtc_base:checks", "../rtc_base:ssl", "../rtc_base:ssl_adapter", "//third_party/abseil-cpp/absl/base:core_headers", @@ -344,7 +341,6 @@ rtc_source_set("ice_agent_interface") { ":connection", ":ice_switch_reason", ":transport_description", - "../api:array_view", "../api/units:timestamp", ] } @@ -359,7 +355,6 @@ rtc_library("ice_controller_interface") { ":ice_switch_reason", ":ice_transport_internal", ":transport_description", - "../api:array_view", "../api/units:time_delta", "../api/units:timestamp", "../rtc_base:checks", @@ -413,7 +408,6 @@ rtc_library("ice_transport_internal") { ":port", ":stun_dictionary", ":transport_description", - "../api:array_view", "../api:candidate", "../api:peer_connection_interface", "../api:rtc_error", @@ -438,6 +432,7 @@ rtc_library("p2p_constants") { "../api/units:data_rate", "../api/units:data_size", "../api/units:time_delta", + "../rtc_base/system:plan_b_only", "../rtc_base/system:rtc_export", ] } @@ -466,7 +461,6 @@ rtc_library("p2p_transport_channel") { ":stun_dictionary", ":transport_description", ":wrapping_active_ice_controller", - "../api:array_view", "../api:async_dns_resolver", "../api:candidate", "../api:field_trials_view", @@ -548,7 +542,6 @@ rtc_library("port") { ":port_interface", ":stun_request", ":transport_description", - "../api:array_view", "../api:candidate", "../api:local_network_access_permission", "../api:packet_socket_factory", @@ -653,7 +646,6 @@ rtc_library("dtls_utils") { "dtls/dtls_utils.h", ] deps = [ - "../api:array_view", "../rtc_base:buffer", "../rtc_base:checks", "../rtc_base:crc32", @@ -669,13 +661,13 @@ rtc_library("dtls_stun_piggyback_controller") { ] deps = [ ":dtls_utils", - "../api:array_view", "../api:sequence_checker", "../api/transport:stun_types", "../rtc_base:checks", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:stringutils", + "../rtc_base/network:received_packet", "../rtc_base/system:no_unique_address", "//third_party/abseil-cpp/absl/container:flat_hash_set", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -730,7 +722,6 @@ rtc_library("stun_request") { "base/stun_request.h", ] deps = [ - "../api:array_view", "../api:sequence_checker", "../api/environment", "../api/task_queue", @@ -837,7 +828,6 @@ rtc_library("turn_port") { ":port_interface", ":relay_port_factory_interface", ":stun_request", - "../api:array_view", "../api:async_dns_resolver", "../api:candidate", "../api:local_network_access_permission", @@ -941,7 +931,6 @@ if (rtc_include_tests) { ":ice_transport_internal", ":port", ":transport_description", - "../api:array_view", "../api:candidate", "../api:field_trials", "../api:ice_transport_interface", @@ -1044,7 +1033,6 @@ if (rtc_include_tests) { ":packet_transport_internal", ":port_interface", ":transport_description", - "../api:array_view", "../api:async_dns_resolver", "../api:candidate", "../api:dtls_transport_interface", @@ -1087,6 +1075,7 @@ if (rtc_include_tests) { "../rtc_base/network:received_packet", "../rtc_base/network:sent_packet", "../rtc_base/synchronization:mutex", + "../test:run_loop", "../test:test_support", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/memory", @@ -1165,7 +1154,6 @@ if (rtc_include_tests) { ":transport_description_factory", ":turn_port", ":wrapping_active_ice_controller", - "../api:array_view", "../api:async_dns_resolver", "../api:candidate", "../api:create_network_emulation_manager", @@ -1185,6 +1173,7 @@ if (rtc_include_tests) { "../api:scoped_refptr", "../api:sequence_checker", "../api:simulated_network_api", + "../api:time_controller", "../api/crypto:options", "../api/environment", "../api/environment:environment_factory", @@ -1207,7 +1196,6 @@ if (rtc_include_tests) { "../rtc_base:copy_on_write_buffer", "../rtc_base:crypto_random", "../rtc_base:dscp", - "../rtc_base:gunit_helpers", "../rtc_base:ip_address", "../rtc_base:logging", "../rtc_base:mdns_responder_interface", @@ -1232,9 +1220,11 @@ if (rtc_include_tests) { "../rtc_base:timeutils", "../rtc_base/network:received_packet", "../rtc_base/network:sent_packet", + "../rtc_base/system:unused", "../system_wrappers:metrics", "../test:create_test_environment", "../test:create_test_field_trials", + "../test:run_loop", "../test:test_support", "../test:wait_until", "../test/time_controller", @@ -1248,7 +1238,7 @@ if (rtc_include_tests) { "//third_party/abseil-cpp/absl/strings:string_view", ] } - rtc_executable("dtls_ice_integration_bench") { + rtc_test("dtls_ice_integration_bench") { testonly = true sources = [ @@ -1296,7 +1286,7 @@ if (rtc_include_tests) { "../rtc_base:ssl_adapter", "../rtc_base:threading", "../test:create_test_field_trials", - "../test:test_main", + "../test:run_loop", "../test:test_support", "../test:wait_until", "//third_party/abseil-cpp/absl/algorithm:container", @@ -1322,7 +1312,6 @@ rtc_library("test_port") { ":p2p_constants", ":port", ":port_interface", - "../api:array_view", "../api:candidate", "../api/transport:stun_types", "../rtc_base:async_packet_socket", @@ -1348,8 +1337,8 @@ rtc_library("p2p_server_utils") { ] deps = [ ":async_stun_tcp_socket", + ":port", ":port_interface", - "../api:array_view", "../api:packet_socket_factory", "../api:sequence_checker", "../api/environment", diff --git a/p2p/DIR_METADATA b/p2p/DIR_METADATA new file mode 100644 index 00000000000..783c9b89583 --- /dev/null +++ b/p2p/DIR_METADATA @@ -0,0 +1,3 @@ +buganizer_public: { + component_id: 1565889 # Network +} \ No newline at end of file diff --git a/p2p/OWNERS b/p2p/OWNERS index 5f4ceb60154..eb01ed8625a 100644 --- a/p2p/OWNERS +++ b/p2p/OWNERS @@ -1,6 +1,4 @@ hta@webrtc.org -mflodman@webrtc.org perkj@webrtc.org -sergeyu@chromium.org tommi@webrtc.org jonaso@webrtc.org diff --git a/p2p/base/async_stun_tcp_socket.cc b/p2p/base/async_stun_tcp_socket.cc index ff8d5105705..9501b66a128 100644 --- a/p2p/base/async_stun_tcp_socket.cc +++ b/p2p/base/async_stun_tcp_socket.cc @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/base/nullability.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/transport/stun.h" #include "rtc_base/async_packet_socket.h" @@ -89,7 +89,7 @@ int AsyncStunTCPSocket::Send(const void* pv, return static_cast(cb); } -size_t AsyncStunTCPSocket::ProcessInput(ArrayView data) { +size_t AsyncStunTCPSocket::ProcessInput(std::span data) { SocketAddress remote_addr(GetRemoteAddress()); // STUN packet - First 4 bytes. Total header size is 20 bytes. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -118,7 +118,7 @@ size_t AsyncStunTCPSocket::ProcessInput(ArrayView data) { } ReceivedIpPacket received_packet( - data.subview(processed_bytes, expected_pkt_len), remote_addr, + data.subspan(processed_bytes, expected_pkt_len), remote_addr, env_.clock().CurrentTime()); NotifyPacketReceived(received_packet); processed_bytes += actual_length; @@ -126,13 +126,13 @@ size_t AsyncStunTCPSocket::ProcessInput(ArrayView data) { } size_t AsyncStunTCPSocket::GetExpectedLength(const void* data, - size_t /* len */, + size_t len, int* pad_bytes) { *pad_bytes = 0; - PacketLength pkt_len = - GetBE16(static_cast(data) + kPacketLenOffset); + std::span view(static_cast(data), len); + PacketLength pkt_len = GetBE16(view.subspan(kPacketLenOffset, 2)); size_t expected_pkt_len; - uint16_t msg_type = GetBE16(data); + uint16_t msg_type = GetBE16(view); if (IsStunMessage(msg_type)) { // STUN message. expected_pkt_len = kStunHeaderSize + pkt_len; diff --git a/p2p/base/async_stun_tcp_socket.h b/p2p/base/async_stun_tcp_socket.h index e3bc0b9fd9f..c1e1924adba 100644 --- a/p2p/base/async_stun_tcp_socket.h +++ b/p2p/base/async_stun_tcp_socket.h @@ -14,9 +14,9 @@ #include #include #include +#include #include "absl/base/nullability.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_tcp_socket.h" @@ -35,7 +35,7 @@ class AsyncStunTCPSocket : public AsyncTCPSocketBase { int Send(const void* pv, size_t cb, const AsyncSocketPacketOptions& options) override; - size_t ProcessInput(ArrayView data) override; + size_t ProcessInput(std::span data) override; private: // This method returns the message hdr + length written in the header. diff --git a/p2p/base/async_stun_tcp_socket_unittest.cc b/p2p/base/async_stun_tcp_socket_unittest.cc index 25677c35946..7857cc37f97 100644 --- a/p2p/base/async_stun_tcp_socket_unittest.cc +++ b/p2p/base/async_stun_tcp_socket_unittest.cc @@ -18,7 +18,6 @@ #include #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_tcp_socket.h" @@ -27,11 +26,11 @@ #include "rtc_base/network/sent_packet.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" -#include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { @@ -152,7 +151,7 @@ class AsyncStunTCPSocketTest : public ::testing::Test { } std::unique_ptr vss_; - AutoSocketServerThread thread_; + test::RunLoop thread_; std::unique_ptr send_socket_; std::unique_ptr listen_socket_; std::unique_ptr recv_socket_; diff --git a/p2p/base/basic_ice_controller.h b/p2p/base/basic_ice_controller.h index 35083c45ef4..d0120b4a487 100644 --- a/p2p/base/basic_ice_controller.h +++ b/p2p/base/basic_ice_controller.h @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -45,14 +45,9 @@ class BasicIceController : public IceControllerInterface { void SetSelectedConnection(const Connection* selected_connection) override; void AddConnection(const Connection* connection) override; void OnConnectionDestroyed(const Connection* connection) override; - ArrayView GetConnections() const override { + std::span GetConnections() const override { return connections_; } - ArrayView connections() const override { - return ArrayView( - const_cast(connections_.data()), - connections_.size()); - } bool HasPingableConnection() const override; diff --git a/p2p/base/basic_packet_socket_factory.cc b/p2p/base/basic_packet_socket_factory.cc index c410099bbce..1093bd4bfab 100644 --- a/p2p/base/basic_packet_socket_factory.cc +++ b/p2p/base/basic_packet_socket_factory.cc @@ -12,7 +12,6 @@ #include #include -#include #include #include "absl/memory/memory.h" @@ -58,6 +57,57 @@ std::unique_ptr BasicPacketSocketFactory::CreateUdpSocket( return std::make_unique(env, std::move(socket)); } +std::unique_ptr +BasicPacketSocketFactory::CreateClientUdpSocket( + const Environment& env, + const SocketAddress& local_address, + const SocketAddress& remote_address, + uint16_t min_port, + uint16_t max_port, + const PacketSocketTcpOptions& udp_options) { + std::unique_ptr socket = + socket_factory_->Create(local_address.family(), SOCK_DGRAM); + if (!socket) { + return nullptr; + } + if (BindSocket(*socket, local_address, min_port, max_port) < 0) { + RTC_LOG(LS_ERROR) << "UDP bind failed with error " << socket->GetError(); + return nullptr; + } + // Assert that at most one DTLS option is used. + int udp_opts = udp_options.opts & (PacketSocketFactory::OPT_DTLS | + PacketSocketFactory::OPT_DTLS_INSECURE); + + RTC_DCHECK(!((udp_opts & PacketSocketFactory::OPT_DTLS) && + (udp_opts & PacketSocketFactory::OPT_DTLS_INSECURE))); + if ((udp_opts & PacketSocketFactory::OPT_DTLS) || + (udp_opts & PacketSocketFactory::OPT_DTLS_INSECURE)) { + if (socket->Connect(remote_address) < 0) { + RTC_LOG(LS_ERROR) << "UDP connect failed with error " + << socket->GetError(); + return nullptr; + } + // Using TLS, wrap the socket in an SSL adapter. + SSLAdapter* ssl_adapter = SSLAdapter::Create(socket.release(), true); + if (!ssl_adapter) { + return nullptr; + } + + if (udp_opts & PacketSocketFactory::OPT_DTLS_INSECURE) { + ssl_adapter->SetIgnoreBadCert(true); + } + + ssl_adapter->SetAlpnProtocols(udp_options.tls_alpn_protocols); + ssl_adapter->SetEllipticCurves(udp_options.tls_elliptic_curves); + ssl_adapter->SetCertVerifier(udp_options.tls_cert_verifier); + socket = absl::WrapUnique(ssl_adapter); + if (ssl_adapter->StartSSL(remote_address.hostname()) != 0) { + return nullptr; + } + } + return std::make_unique(env, std::move(socket)); +} + std::unique_ptr BasicPacketSocketFactory::CreateServerTcpSocket( const Environment& env, @@ -149,7 +199,7 @@ BasicPacketSocketFactory::CreateClientTcpSocket( ssl_adapter->SetEllipticCurves(tcp_options.tls_elliptic_curves); ssl_adapter->SetCertVerifier(tcp_options.tls_cert_verifier); - if (ssl_adapter->StartSSL(remote_address.hostname().c_str()) != 0) { + if (ssl_adapter->StartSSL(remote_address.hostname()) != 0) { return nullptr; } socket = std::move(ssl_adapter); diff --git a/p2p/base/basic_packet_socket_factory.h b/p2p/base/basic_packet_socket_factory.h index b5c382ff29c..aaa3b854b86 100644 --- a/p2p/base/basic_packet_socket_factory.h +++ b/p2p/base/basic_packet_socket_factory.h @@ -49,6 +49,14 @@ class RTC_EXPORT BasicPacketSocketFactory : public PacketSocketFactory { std::unique_ptr CreateAsyncDnsResolver() override; + std::unique_ptr CreateClientUdpSocket( + const Environment& env, + const SocketAddress& local_address, + const SocketAddress& remote_address, + uint16_t min_port, + uint16_t max_port, + const PacketSocketTcpOptions& options) override; + private: int BindSocket(Socket& socket, const SocketAddress& local_address, diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc index 6603e3f67f4..6bb2e578b3c 100644 --- a/p2p/base/connection.cc +++ b/p2p/base/connection.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include "absl/algorithm/container.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/environment/environment.h" #include "api/rtc_error.h" @@ -653,7 +653,7 @@ void Connection::MaybeHandleDtlsPiggybackingAttributes( msg->GetByteString(STUN_ATTR_META_DTLS_IN_STUN); const StunByteStringAttribute* dtls_piggyback_ack = msg->GetByteString(STUN_ATTR_META_DTLS_IN_STUN_ACK); - std::optional> piggyback_data; + std::optional> piggyback_data; if (dtls_piggyback_attr != nullptr) { piggyback_data = dtls_piggyback_attr->array_view(); } @@ -1187,8 +1187,15 @@ std::unique_ptr Connection::BuildPingRequest( if (delta) { RTC_DCHECK(delta->type() == STUN_ATTR_GOOG_DELTA); - RTC_LOG(LS_INFO) << "Sending GOOG_DELTA: len: " << delta->length(); - message->AddAttribute(std::move(delta)); + size_t msg_length = message->length(); + if (msg_length + kStunAttributeHeaderSize + delta->length() < + kMaxStunBindingLength) { + RTC_LOG(LS_INFO) << "Sending GOOG_DELTA: len: " << delta->length(); + message->AddAttribute(std::move(delta)); + } else { + RTC_LOG(LS_WARNING) << "Not sending GOOG_DELTA, request full: len: " + << delta->length() << " msg_length: " << msg_length; + } } MaybeAddDtlsPiggybackingAttributes(message.get()); diff --git a/p2p/base/connection_unittest.cc b/p2p/base/connection_unittest.cc index d7db86fc3d2..02ce1f736a5 100644 --- a/p2p/base/connection_unittest.cc +++ b/p2p/base/connection_unittest.cc @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include "absl/memory/memory.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" #include "api/rtc_error.h" @@ -300,7 +300,6 @@ TEST_F(ConnectionTest, SendReceiveGoogDelta) { ASSERT_GT(rport_->last_stun_buf().size(), 0u); lconn->OnReadPacket( ReceivedIpPacket(rport_->last_stun_buf(), SocketAddress(), std::nullopt)); - EXPECT_TRUE(received_goog_delta_ack); } @@ -346,5 +345,65 @@ TEST_F(ConnectionTest, SendGoogDeltaNoReply) { EXPECT_TRUE(received_goog_delta_ack_error); } +// Test that if StunBinding is sufficiently full, aka DELTA is too big, it is +// not sent. +TEST_F(ConnectionTest, TooBigDeltaIsNotSent) { + constexpr int64_t ms = 10; + Connection* lconn = CreateConnection(ICEROLE_CONTROLLING); + Connection* rconn = CreateConnection(ICEROLE_CONTROLLED); + + std::string a_long_string(1200, 'a'); + std::unique_ptr delta = + absl::WrapUnique(new StunByteStringAttribute(STUN_ATTR_GOOG_DELTA)); + delta->CopyBytes(a_long_string); + + std::unique_ptr delta_ack = + absl::WrapUnique(new StunUInt64Attribute(STUN_ATTR_GOOG_DELTA_ACK, 133)); + + bool received_goog_delta = false; + bool received_goog_delta_ack = false; + lconn->SetStunDictConsumer( + // DeltaReceived + [](const StunByteStringAttribute* delta) + -> std::unique_ptr { return nullptr; }, + // DeltaAckReceived + [&](RTCErrorOr error_or_ack) { + received_goog_delta_ack = true; + EXPECT_TRUE(error_or_ack.ok()); + EXPECT_EQ(error_or_ack.value()->value(), 133ull); + }); + + rconn->SetStunDictConsumer( + // DeltaReceived + [&](const StunByteStringAttribute* delta) + -> std::unique_ptr { + received_goog_delta = true; + EXPECT_EQ(delta->string_view(), "DELTA"); + return std::move(delta_ack); + }, + // DeltaAckReceived + [](RTCErrorOr error_or__ack) {}); + + lconn->Ping(env().clock().CurrentTime(), std::move(delta)); + ASSERT_THAT(WaitUntil([&] { return lport_->last_stun_msg(); }, IsTrue(), + {.timeout = TimeDelta::Millis(kDefaultTimeout), + .clock = &time_controller_}), + IsRtcOk()); + ASSERT_GT(lport_->last_stun_buf().size(), 0u); + rconn->OnReadPacket( + ReceivedIpPacket(lport_->last_stun_buf(), SocketAddress(), std::nullopt)); + EXPECT_FALSE(received_goog_delta); + + time_controller_.SkipForwardBy(TimeDelta::Millis(ms)); + ASSERT_TRUE(WaitUntil([&] { return rport_->last_stun_msg(); }, + {.timeout = TimeDelta::Millis(kDefaultTimeout), + .clock = &time_controller_})); + ASSERT_GT(rport_->last_stun_buf().size(), 0u); + lconn->OnReadPacket( + ReceivedIpPacket(rport_->last_stun_buf(), SocketAddress(), std::nullopt)); + + EXPECT_FALSE(received_goog_delta_ack); +} + } // namespace } // namespace webrtc diff --git a/p2p/base/default_ice_transport_factory.cc b/p2p/base/default_ice_transport_factory.cc index 6c314fd8270..5c2fb883395 100644 --- a/p2p/base/default_ice_transport_factory.cc +++ b/p2p/base/default_ice_transport_factory.cc @@ -23,20 +23,20 @@ #include "p2p/base/ice_controller_interface.h" #include "p2p/base/p2p_transport_channel.h" +namespace webrtc { + namespace { -class BasicIceControllerFactory : public webrtc::IceControllerFactoryInterface { +class BasicIceControllerFactory : public IceControllerFactoryInterface { public: - std::unique_ptr Create( - const webrtc::IceControllerFactoryArgs& args) override { - return std::make_unique(args); + std::unique_ptr Create( + const IceControllerFactoryArgs& args) override { + return std::make_unique(args); } }; } // namespace -namespace webrtc { - DefaultIceTransport::DefaultIceTransport( std::unique_ptr internal) : internal_(std::move(internal)) {} diff --git a/p2p/base/ice_agent_interface.h b/p2p/base/ice_agent_interface.h index e2c3f966fd8..2047be08dd5 100644 --- a/p2p/base/ice_agent_interface.h +++ b/p2p/base/ice_agent_interface.h @@ -11,7 +11,8 @@ #ifndef P2P_BASE_ICE_AGENT_INTERFACE_H_ #define P2P_BASE_ICE_AGENT_INTERFACE_H_ -#include "api/array_view.h" +#include + #include "api/units/timestamp.h" #include "p2p/base/connection.h" #include "p2p/base/ice_switch_reason.h" @@ -62,7 +63,7 @@ class IceAgentInterface { // // SignalStateChange will not be triggered. virtual void ForgetLearnedStateForConnections( - ArrayView connections) = 0; + std::span connections) = 0; // Send a STUN ping request for the given connection. virtual void SendPingRequest(const Connection* connection) = 0; @@ -74,7 +75,7 @@ class IceAgentInterface { // Prune away the given connections. Returns true if pruning is permitted and // successfully performed. virtual bool PruneConnections( - ArrayView connections) = 0; + std::span connections) = 0; }; } // namespace webrtc diff --git a/p2p/base/ice_controller_interface.h b/p2p/base/ice_controller_interface.h index 53bf509b048..6aa5b6024d1 100644 --- a/p2p/base/ice_controller_interface.h +++ b/p2p/base/ice_controller_interface.h @@ -12,17 +12,16 @@ #define P2P_BASE_ICE_CONTROLLER_INTERFACE_H_ #include +#include #include #include -#include "api/array_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "p2p/base/connection.h" #include "p2p/base/ice_switch_reason.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/transport_description.h" -#include "rtc_base/checks.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -112,17 +111,7 @@ class IceControllerInterface { virtual void OnConnectionDestroyed(const Connection* connection) = 0; // These are all connections that has been added and not destroyed. - virtual ArrayView GetConnections() const { - // Stub implementation to simplify downstream roll. - RTC_CHECK_NOTREACHED(); - return {}; - } - // TODO(bugs.webrtc.org/15702): Remove this after downstream is cleaned up. - virtual ArrayView connections() const { - // Stub implementation to simplify downstream removal. - RTC_CHECK_NOTREACHED(); - return {}; - } + virtual std::span GetConnections() const = 0; // Is there a pingable connection ? // This function is used to boot-strap pinging, after this returns true diff --git a/p2p/base/ice_transport_internal.h b/p2p/base/ice_transport_internal.h index 1e3321e1784..a30db71a622 100644 --- a/p2p/base/ice_transport_internal.h +++ b/p2p/base/ice_transport_internal.h @@ -14,12 +14,12 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" @@ -410,7 +410,7 @@ class RTC_EXPORT IceTransportInternal : public PacketTransportInternal { CallbackList> + std::span> dictionary_view_updated_callback_list_; CallbackList dictionary_writer_synced_callback_list_; diff --git a/p2p/base/local_network_access_port_unittest.cc b/p2p/base/local_network_access_port_unittest.cc index 01292a01c35..9f27cb77662 100644 --- a/p2p/base/local_network_access_port_unittest.cc +++ b/p2p/base/local_network_access_port_unittest.cc @@ -23,6 +23,7 @@ #include "api/test/mock_async_dns_resolver.h" #include "api/test/mock_local_network_access_permission.h" #include "api/test/rtc_error_matchers.h" +#include "api/units/timestamp.h" #include "p2p/base/port.h" #include "p2p/base/port_allocator.h" #include "p2p/base/stun_port.h" @@ -32,17 +33,16 @@ #include "p2p/test/test_stun_server.h" #include "p2p/test/test_turn_server.h" #include "p2p/test/turn_server.h" -#include "rtc_base/fake_clock.h" #include "rtc_base/net_helper.h" #include "rtc_base/net_helpers.h" #include "rtc_base/network.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" -#include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" namespace webrtc { @@ -78,8 +78,9 @@ class LocalNetworkAccessPortTest switch (server_type()) { case kStun: - stun_server_ = TestStunServer::Create(env_, {server_address(), 5000}, - ss_, thread_); + stun_server_ = + TestStunServer::Create(env_, {server_address(), 5000}, ss_, + *time_controller_.GetMainThread()); break; case kTurn: turn_server_.AddInternalSocket({server_address(), 5000}, PROTO_UDP); @@ -118,7 +119,7 @@ class LocalNetworkAccessPortTest ProtocolAddress proto_server_address({server_address, 5000}, PROTO_UDP); CreateRelayPortArgs args = { .env = env_, - .network_thread = &thread_, + .network_thread = time_controller_.GetMainThread(), .socket_factory = &socket_factory_, .network = &network_, .server_address = &proto_server_address, @@ -145,7 +146,7 @@ class LocalNetworkAccessPortTest LocalNetworkAccessPermissionFactoryInterface& lna_permission_factory) { Port::PortParametersRef params = { .env = env_, - .network_thread = &thread_, + .network_thread = time_controller_.GetMainThread(), .socket_factory = &socket_factory_, .network = &network_, .ice_username_fragment = kIceUfrag, @@ -187,10 +188,10 @@ class LocalNetworkAccessPortTest bool port_ready_ = false; bool port_error_ = false; - ScopedFakeClock fake_clock_; - const Environment env_ = CreateTestEnvironment(); VirtualSocketServer ss_; - AutoSocketServerThread thread_{&ss_}; + GlobalSimulatedTimeController time_controller_{Timestamp::Micros(1234567), + &ss_}; + const Environment env_ = CreateTestEnvironment({.time = &time_controller_}); MockDnsResolvingPacketSocketFactory socket_factory_{&ss_}; const bool is_using_ipv6_{SocketAddress(server_address(), 5000).family() == @@ -199,8 +200,8 @@ class LocalNetworkAccessPortTest : kLocalAddr}; Network network_{"unittest", "unittest", local_address_.ipaddr(), 32}; - TestTurnServer turn_server_{env_, &thread_, &ss_, kTurnUdpIntAddr, - kTurnUdpExtAddr}; + TestTurnServer turn_server_{env_, time_controller_.GetMainThread(), &ss_, + kTurnUdpIntAddr, kTurnUdpExtAddr}; TestStunServer::StunServerPtr stun_server_; }; @@ -265,13 +266,13 @@ TEST_P(LocalNetworkAccessPortTest, ResolvedAddress) { if (lna_fake_result() == LnaFakeResult::kPermissionNotNeeded || lna_fake_result() == LnaFakeResult::kPermissionGranted) { EXPECT_THAT(WaitUntil([&] { return port_ready_; }, IsTrue(), - {.clock = &fake_clock_}), + {.clock = &time_controller_}), IsRtcOk()); EXPECT_EQ(1u, port->Candidates().size()); EXPECT_NE(SOCKET_ERROR, port->GetError()); } else { EXPECT_THAT(WaitUntil([&] { return port_error_; }, IsTrue(), - {.clock = &fake_clock_}), + {.clock = &time_controller_}), IsRtcOk()); EXPECT_EQ(0u, port->Candidates().size()); EXPECT_NE(SOCKET_ERROR, port->GetError()); @@ -288,13 +289,13 @@ TEST_P(LocalNetworkAccessPortTest, UnresolvedAddress) { if (lna_fake_result() == LnaFakeResult::kPermissionNotNeeded || lna_fake_result() == LnaFakeResult::kPermissionGranted) { EXPECT_THAT(WaitUntil([&] { return port_ready_; }, IsTrue(), - {.clock = &fake_clock_}), + {.clock = &time_controller_}), IsRtcOk()); EXPECT_EQ(1u, port->Candidates().size()); EXPECT_NE(SOCKET_ERROR, port->GetError()); } else { EXPECT_THAT(WaitUntil([&] { return port_error_; }, IsTrue(), - {.clock = &fake_clock_}), + {.clock = &time_controller_}), IsRtcOk()); EXPECT_EQ(0u, port->Candidates().size()); EXPECT_NE(SOCKET_ERROR, port->GetError()); diff --git a/p2p/base/p2p_constants.h b/p2p/base/p2p_constants.h index fa702cef95e..c015fb7bd3f 100644 --- a/p2p/base/p2p_constants.h +++ b/p2p/base/p2p_constants.h @@ -15,6 +15,7 @@ #include #include "api/units/time_delta.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -25,14 +26,10 @@ namespace webrtc { // Jingle call, the content name can be anything, so don't rely on // these values being the same as the ones received. // Note: these were used in the deprecated "plan-b". -[[deprecated("plan-b")]] -extern const char CN_AUDIO[]; -[[deprecated("plan-b")]] -extern const char CN_VIDEO[]; -[[deprecated("plan-b")]] -extern const char CN_DATA[]; -[[deprecated("plan-b")]] -extern const char CN_OTHER[]; +PLAN_B_ONLY extern const char CN_AUDIO[]; +PLAN_B_ONLY extern const char CN_VIDEO[]; +PLAN_B_ONLY extern const char CN_DATA[]; +PLAN_B_ONLY extern const char CN_OTHER[]; extern const char GROUP_TYPE_BUNDLE[]; @@ -128,7 +125,8 @@ inline constexpr TimeDelta kMinConnectionLifetime = TimeDelta::Seconds(10); enum IcePriorityValue : uint8_t { ICE_TYPE_PREFERENCE_RELAY_TLS = 0, ICE_TYPE_PREFERENCE_RELAY_TCP = 1, - ICE_TYPE_PREFERENCE_RELAY_UDP = 2, + ICE_TYPE_PREFERENCE_RELAY_DTLS = 2, + ICE_TYPE_PREFERENCE_RELAY_UDP = 3, ICE_TYPE_PREFERENCE_PRFLX_TCP = 80, ICE_TYPE_PREFERENCE_HOST_TCP = 90, ICE_TYPE_PREFERENCE_SRFLX = 100, diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc index 4e659e055ef..76b5006e556 100644 --- a/p2p/base/p2p_transport_channel.cc +++ b/p2p/base/p2p_transport_channel.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/async_dns_resolver.h" #include "api/candidate.h" #include "api/environment/environment.h" @@ -96,8 +96,7 @@ RouteEndpoint CreateRouteEndpointFromCandidate(bool local, bool uses_turn) { auto adapter_type = candidate.network_type(); if (!local && adapter_type == ADAPTER_TYPE_UNKNOWN) { - bool vpn; - std::tie(adapter_type, vpn) = + std::tie(adapter_type, std::ignore, std::ignore) = Network::GuessAdapterFromNetworkCost(candidate.network_cost()); } @@ -334,7 +333,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) { } void P2PTransportChannel::ForgetLearnedStateForConnections( - ArrayView connections) { + std::span connections) { for (const Connection* con : connections) { FromIceController(con)->ForgetLearnedState(); } @@ -1706,9 +1705,9 @@ DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const { return static_cast(it->second); } -ArrayView P2PTransportChannel::connections() const { +std::span P2PTransportChannel::connections() const { RTC_DCHECK_RUN_ON(&network_thread_); - return ArrayView(connections_.data(), connections_.size()); + return std::span(connections_.data(), connections_.size()); } void P2PTransportChannel::RemoveConnectionForTest(Connection* connection) { @@ -1807,7 +1806,7 @@ bool P2PTransportChannel::AllowedToPruneConnections() const { } bool P2PTransportChannel::PruneConnections( - ArrayView connections) { + std::span connections) { RTC_DCHECK_RUN_ON(&network_thread_); if (!AllowedToPruneConnections()) { RTC_LOG(LS_WARNING) << "Not allowed to prune connections"; diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h index e44b6eece40..92f8dfe2bd2 100644 --- a/p2p/base/p2p_transport_channel.h +++ b/p2p/base/p2p_transport_channel.h @@ -26,11 +26,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/async_dns_resolver.h" #include "api/candidate.h" #include "api/environment/environment.h" @@ -164,16 +164,9 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, void SwitchSelectedConnection(const Connection* connection, IceSwitchReason reason) override; void ForgetLearnedStateForConnections( - ArrayView connections) override; + std::span connections) override; bool PruneConnections( - ArrayView connections) override; - - // TODO(honghaiz): Remove this method once the reference of it in - // Chromoting is removed. - const Connection* best_connection() const { - RTC_DCHECK_RUN_ON(&network_thread_); - return selected_connection_; - } + std::span connections) override; void set_incoming_only(bool value) { RTC_DCHECK_RUN_ON(&network_thread_); @@ -210,7 +203,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, void MarkConnectionPinged(Connection* conn); // Public for unit tests. - ArrayView connections() const; + std::span connections() const; void RemoveConnectionForTest(Connection* connection); // Public for unit tests. diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc index f6eac835aac..8b596e0b43c 100644 --- a/p2p/base/p2p_transport_channel_unittest.cc +++ b/p2p/base/p2p_transport_channel_unittest.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,11 +26,9 @@ #include "absl/algorithm/container.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/async_dns_resolver.h" #include "api/candidate.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/ice_transport_interface.h" #include "api/packet_socket_factory.h" #include "api/scoped_refptr.h" @@ -72,11 +71,9 @@ #include "rtc_base/byte_buffer.h" #include "rtc_base/checks.h" #include "rtc_base/dscp.h" -#include "rtc_base/fake_clock.h" #include "rtc_base/fake_mdns_responder.h" #include "rtc_base/fake_network.h" #include "rtc_base/firewall_socket_server.h" -#include "rtc_base/gunit.h" #include "rtc_base/internal/default_socket_server.h" #include "rtc_base/ip_address.h" #include "rtc_base/logging.h" @@ -95,9 +92,12 @@ #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" +#include "test/create_test_environment.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" namespace webrtc { @@ -202,7 +202,7 @@ Candidate CreateUdpCandidate(IceCandidateType type, } std::unique_ptr CreateBasicPortAllocator( - const Environment& env, + const Environment& env_, NetworkManager* network_manager, PacketSocketFactory* socket_factory, const ServerAddresses& stun_servers, @@ -218,7 +218,7 @@ std::unique_ptr CreateBasicPortAllocator( } std::vector turn_servers(1, turn_server); - auto allocator = std::make_unique(env, network_manager, + auto allocator = std::make_unique(env_, network_manager, socket_factory); allocator->Initialize(); allocator->SetConfiguration(stun_servers, turn_servers, 0, NO_PRUNE); @@ -306,26 +306,30 @@ class P2PTransportChannelTestBase : public ::testing::Test { : vss_(new VirtualSocketServer()), nss_(new NATSocketServer(vss_.get())), ss_(new FirewallSocketServer(nss_.get())), + time_controller_(Timestamp::Millis(1000), ss_.get()), + env_(CreateTestEnvironment({.time = &time_controller_})), socket_factory_(new BasicPacketSocketFactory(ss_.get())), - main_(ss_.get()), + main_(time_controller_.GetMainThread()), + ep1_(main_), + ep2_(main_), force_relay_(false) { - ep1_.role_ = ICEROLE_CONTROLLING; - ep2_.role_ = ICEROLE_CONTROLLED; + ep1_.SetIceRole(ICEROLE_CONTROLLING); + ep2_.SetIceRole(ICEROLE_CONTROLLED); metrics::Reset(); } - void CreatePortAllocators(const Environment& env) { - stun_server_ = TestStunServer::Create(env, kStunAddr, *ss_, main_); - turn_server_.emplace(env, &main_, ss_.get(), kTurnUdpIntAddr, + void CreatePortAllocators() { + stun_server_ = TestStunServer::Create(env_, kStunAddr, *ss_, *main_); + turn_server_.emplace(env_, main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr); ServerAddresses stun_servers = {kStunAddr}; - ep1_.allocator_ = CreateBasicPortAllocator( - env, &ep1_.network_manager_, socket_factory_.get(), stun_servers, - kTurnUdpIntAddr, SocketAddress()); - ep2_.allocator_ = CreateBasicPortAllocator( - env, &ep2_.network_manager_, socket_factory_.get(), stun_servers, - kTurnUdpIntAddr, SocketAddress()); + ep1_.set_allocator(CreateBasicPortAllocator( + env_, &ep1_.network_manager(), socket_factory_.get(), stun_servers, + kTurnUdpIntAddr, SocketAddress())); + ep2_.set_allocator(CreateBasicPortAllocator( + env_, &ep2_.network_manager(), socket_factory_.get(), stun_servers, + kTurnUdpIntAddr, SocketAddress())); } protected: @@ -365,7 +369,8 @@ class P2PTransportChannelTestBase : public ::testing::Test { TimeDelta connect_wait; }; - struct ChannelData { + class ChannelData { + public: bool CheckData(const char* data, int len) { bool ret = false; if (!ch_packets_.empty()) { @@ -376,6 +381,14 @@ class P2PTransportChannelTestBase : public ::testing::Test { return ret; } + void set_ch(std::unique_ptr ch) { + ch_ = std::move(ch); + } + void reset_ch() { ch_.reset(); } + P2PTransportChannel* ch() const { return ch_.get(); } + std::list& ch_packets() { return ch_packets_; } + + private: std::string name_; // TODO(?) - Currently not used. std::list ch_packets_; std::unique_ptr ch_; @@ -386,27 +399,29 @@ class P2PTransportChannelTestBase : public ::testing::Test { Candidate candidate; }; - struct Endpoint { - Endpoint() - : role_(ICEROLE_UNKNOWN), + class Endpoint { + public: + explicit Endpoint(Thread* thread) + : network_manager_(thread), + role_(ICEROLE_UNKNOWN), role_conflict_(false), save_candidates_(false) {} - bool HasTransport(const PacketTransportInternal* transport) { - return (transport == cd1_.ch_.get() || transport == cd2_.ch_.get()); + bool HasTransport(const PacketTransportInternal* transport) const { + return (transport == cd1_.ch() || transport == cd2_.ch()); } ChannelData* GetChannelData(PacketTransportInternal* transport) { if (!HasTransport(transport)) return nullptr; - if (cd1_.ch_.get() == transport) + if (cd1_.ch() == transport) return &cd1_; else return &cd2_; } void SetIceRole(IceRole role) { role_ = role; } - IceRole ice_role() { return role_; } + IceRole ice_role() const { return role_; } void OnRoleConflict(bool role_conflict) { role_conflict_ = role_conflict; } - bool role_conflict() { return role_conflict_; } + bool role_conflict() const { return role_conflict_; } void SetAllocationStepDelay(uint32_t delay) { allocator_->set_step_delay(delay); } @@ -418,11 +433,33 @@ class P2PTransportChannelTestBase : public ::testing::Test { ++ice_regathering_counter_[reason]; } - int GetIceRegatheringCountForReason(IceRegatheringReason reason) { - return ice_regathering_counter_[reason]; + int GetIceRegatheringCountForReason(IceRegatheringReason reason) const { + auto it = ice_regathering_counter_.find(reason); + return it == ice_regathering_counter_.end() ? 0 : it->second; } - FakeNetworkManager network_manager_{Thread::Current()}; + FakeNetworkManager& network_manager() { return network_manager_; } + void set_allocator(std::unique_ptr allocator) { + allocator_ = std::move(allocator); + } + BasicPortAllocator* allocator() const { return allocator_.get(); } + void set_async_dns_resolver_factory( + AsyncDnsResolverFactoryInterface* async_dns_resolver_factory) { + async_dns_resolver_factory_ = async_dns_resolver_factory; + } + AsyncDnsResolverFactoryInterface* async_dns_resolver_factory() const { + return async_dns_resolver_factory_; + } + ChannelData& cd1() { return cd1_; } + ChannelData& cd2() { return cd2_; } + void set_save_candidates(bool save) { save_candidates_ = save; } + bool save_candidates() const { return save_candidates_; } + std::vector& saved_candidates() { return saved_candidates_; } + void set_ready_to_send(bool ready) { ready_to_send_ = ready; } + bool ready_to_send() const { return ready_to_send_; } + + private: + FakeNetworkManager network_manager_; std::unique_ptr allocator_; AsyncDnsResolverFactoryInterface* async_dns_resolver_factory_ = nullptr; ChannelData cd1_; @@ -449,49 +486,60 @@ class P2PTransportChannelTestBase : public ::testing::Test { return new_ice; } - void CreateChannels(const Environment& env, - const IceConfig& ep1_config, + Waiter DefaultWait() { + return Waiter({.timeout = kDefaultTimeout, .clock = &time_controller_}); + } + Waiter MediumWait() { + return Waiter({.timeout = kMediumTimeout, .clock = &time_controller_}); + } + Waiter ShortWait() { + return Waiter({.timeout = kShortTimeout, .clock = &time_controller_}); + } + Waiter Wait(TimeDelta timeout) { + return Waiter({.timeout = timeout, .clock = &time_controller_}); + } + + void CreateChannels(const IceConfig& ep1_config, const IceConfig& ep2_config, bool renomination = false) { IceParameters ice_ep1_cd1_ch = IceParamsWithRenomination(kIceParams[0], renomination); IceParameters ice_ep2_cd1_ch = IceParamsWithRenomination(kIceParams[1], renomination); - ep1_.cd1_.ch_ = CreateChannel(env, 0, ICE_CANDIDATE_COMPONENT_DEFAULT, - ice_ep1_cd1_ch, ice_ep2_cd1_ch); - ep2_.cd1_.ch_ = CreateChannel(env, 1, ICE_CANDIDATE_COMPONENT_DEFAULT, - ice_ep2_cd1_ch, ice_ep1_cd1_ch); - ep1_.cd1_.ch_->SetIceConfig(ep1_config); - ep2_.cd1_.ch_->SetIceConfig(ep2_config); - ep1_.cd1_.ch_->MaybeStartGathering(); - ep2_.cd1_.ch_->MaybeStartGathering(); - ep1_.cd1_.ch_->allocator_session()->SubscribeIceRegathering( + ep1_.cd1().set_ch(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, + ice_ep1_cd1_ch, ice_ep2_cd1_ch)); + ep2_.cd1().set_ch(CreateChannel(1, ICE_CANDIDATE_COMPONENT_DEFAULT, + ice_ep2_cd1_ch, ice_ep1_cd1_ch)); + ep1_.cd1().ch()->SetIceConfig(ep1_config); + ep2_.cd1().ch()->SetIceConfig(ep2_config); + ep1_.cd1().ch()->MaybeStartGathering(); + ep2_.cd1().ch()->MaybeStartGathering(); + ep1_.cd1().ch()->allocator_session()->SubscribeIceRegathering( this, [this](PortAllocatorSession* allocator_session, IceRegatheringReason reason) { ep1_.OnIceRegathering(allocator_session, reason); }); - ep2_.cd1_.ch_->allocator_session()->SubscribeIceRegathering( + ep2_.cd1().ch()->allocator_session()->SubscribeIceRegathering( this, [this](PortAllocatorSession* allocator_session, IceRegatheringReason reason) { ep2_.OnIceRegathering(allocator_session, reason); }); } - void CreateChannels(const Environment& env) { + void CreateChannels() { IceConfig default_config; - CreateChannels(env, default_config, default_config, false); + CreateChannels(default_config, default_config, false); } std::unique_ptr CreateChannel( - const Environment& env, int endpoint, int component, const IceParameters& local_ice, const IceParameters& remote_ice) { - IceTransportInit init(env); + IceTransportInit init(env_); init.set_port_allocator(GetAllocator(endpoint)); init.set_async_dns_resolver_factory( - GetEndpoint(endpoint)->async_dns_resolver_factory_); + GetEndpoint(endpoint)->async_dns_resolver_factory()); auto channel = P2PTransportChannel::Create("test content name", component, std::move(init)); channel->SubscribeReadyToSend(this, @@ -534,18 +582,18 @@ class P2PTransportChannelTestBase : public ::testing::Test { void DestroyChannels() { safety_->SetNotAlive(); - ep1_.cd1_.ch_.reset(); - ep2_.cd1_.ch_.reset(); - ep1_.cd2_.ch_.reset(); - ep2_.cd2_.ch_.reset(); + ep1_.cd1().reset_ch(); + ep2_.cd1().reset_ch(); + ep1_.cd2().reset_ch(); + ep2_.cd2().reset_ch(); // Process pending tasks that need to run for cleanup purposes such as // pending deletion of Connection objects (see Connection::Destroy). Thread::Current()->ProcessMessages(0); } - P2PTransportChannel* ep1_ch1() { return ep1_.cd1_.ch_.get(); } - P2PTransportChannel* ep1_ch2() { return ep1_.cd2_.ch_.get(); } - P2PTransportChannel* ep2_ch1() { return ep2_.cd1_.ch_.get(); } - P2PTransportChannel* ep2_ch2() { return ep2_.cd2_.ch_.get(); } + P2PTransportChannel* ep1_ch1() { return ep1_.cd1().ch(); } + P2PTransportChannel* ep1_ch2() { return ep1_.cd2().ch(); } + P2PTransportChannel* ep2_ch1() { return ep2_.cd1().ch(); } + P2PTransportChannel* ep2_ch2() { return ep2_.cd2().ch(); } TestTurnServer* test_turn_server() { EXPECT_TRUE(turn_server_.has_value()); @@ -582,10 +630,10 @@ class P2PTransportChannelTestBase : public ::testing::Test { } } BasicPortAllocator* GetAllocator(int endpoint) { - return GetEndpoint(endpoint)->allocator_.get(); + return GetEndpoint(endpoint)->allocator(); } void AddAddress(int endpoint, const SocketAddress& addr) { - GetEndpoint(endpoint)->network_manager_.AddInterface(addr); + GetEndpoint(endpoint)->network_manager().AddInterface(addr); } void AddAddress( int endpoint, @@ -593,11 +641,11 @@ class P2PTransportChannelTestBase : public ::testing::Test { absl::string_view ifname, AdapterType adapter_type, std::optional underlying_vpn_adapter_type = std::nullopt) { - GetEndpoint(endpoint)->network_manager_.AddInterface( + GetEndpoint(endpoint)->network_manager().AddInterface( addr, ifname, adapter_type, underlying_vpn_adapter_type); } void RemoveAddress(int endpoint, const SocketAddress& addr) { - GetEndpoint(endpoint)->network_manager_.RemoveInterface(addr); + GetEndpoint(endpoint)->network_manager().RemoveInterface(addr); fw()->AddRule(false, FP_ANY, FD_ANY, addr); } void SetAllocatorFlags(int endpoint, int flags) { @@ -713,17 +761,15 @@ class P2PTransportChannelTestBase : public ::testing::Test { return CheckConnected(ch1, ch2) && CheckCandidatePair(ch1, ch2, from, to); } - void Test(const Environment& env, const Result& expected) { - ScopedFakeClock clock; - int64_t connect_start = env.clock().TimeInMilliseconds(); + void Test(const Result& expected) { + webrtc::Timestamp connect_start = env_.clock().CurrentTime(); int64_t connect_time; // Create the channels and wait for them to connect. - CreateChannels(env); - EXPECT_TRUE(WaitUntil( - [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = expected.connect_wait + kShortTimeout, .clock = &clock})); - connect_time = env.clock().TimeInMilliseconds() - connect_start; + CreateChannels(); + EXPECT_TRUE(MediumWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + connect_time = (env_.clock().CurrentTime() - connect_start).ms(); if (connect_time < expected.connect_wait.ms()) { RTC_LOG(LS_INFO) << "Connect time: " << connect_time << " ms"; } else { @@ -734,22 +780,20 @@ class P2PTransportChannelTestBase : public ::testing::Test { // Allow a few turns of the crank for the selected connections to emerge. // This may take up to 2 seconds. if (ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection()) { - int64_t converge_start = env.clock().TimeInMilliseconds(); + webrtc::Timestamp converge_start = env_.clock().CurrentTime(); int64_t converge_time; // Verifying local and remote channel selected connection information. // This is done only for the RFC 5245 as controlled agent will use // USE-CANDIDATE from controlling (ep1) agent. We can easily predict from // EP1 result matrix. - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidate1(expected) && CheckCandidate2(expected); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(ShortWait().Until([&] { + return CheckCandidate1(expected) && CheckCandidate2(expected); + })); // Also do EXPECT_EQ on each part so that failures are more verbose. ExpectCandidate1(expected); ExpectCandidate2(expected); - converge_time = env.clock().TimeInMilliseconds() - converge_start; + converge_time = (env_.clock().CurrentTime() - converge_start).ms(); int64_t converge_wait = 2000; if (converge_time < converge_wait) { RTC_LOG(LS_INFO) << "Converge time: " << converge_time << " ms"; @@ -759,31 +803,27 @@ class P2PTransportChannelTestBase : public ::testing::Test { } } // Try sending some data to other end. - TestSendRecv(&clock); + TestSendRecv(); // Destroy the channels, and wait for them to be fully cleaned up. DestroyChannels(); } - void TestSendRecv(ThreadProcessingFakeClock* clock) { + void TestSendRecv() { for (int i = 0; i < 10; ++i) { const char* data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; int len = static_cast(strlen(data)); // local_channel1 <==> remote_channel1 - EXPECT_THAT( - WaitUntil([&] { return SendData(ep1_ch1(), data, len); }, Eq(len), - {.timeout = kMediumTimeout, .clock = clock}), - IsRtcOk()); - EXPECT_TRUE( - WaitUntil([&] { return CheckDataOnChannel(ep2_ch1(), data, len); }, - {.timeout = kMediumTimeout, .clock = clock})); - EXPECT_THAT( - WaitUntil([&] { return SendData(ep2_ch1(), data, len); }, Eq(len), - {.timeout = kMediumTimeout, .clock = clock}), - IsRtcOk()); - EXPECT_TRUE( - WaitUntil([&] { return CheckDataOnChannel(ep1_ch1(), data, len); }, - {.timeout = kMediumTimeout, .clock = clock})); + EXPECT_THAT(MediumWait().Until( + [&] { return SendData(ep1_ch1(), data, len); }, Eq(len)), + IsRtcOk()); + EXPECT_TRUE(MediumWait().Until( + [&] { return CheckDataOnChannel(ep2_ch1(), data, len); })); + EXPECT_THAT(MediumWait().Until( + [&] { return SendData(ep2_ch1(), data, len); }, Eq(len)), + IsRtcOk()); + EXPECT_TRUE(MediumWait().Until( + [&] { return CheckDataOnChannel(ep1_ch1(), data, len); })); } } @@ -793,11 +833,10 @@ class P2PTransportChannelTestBase : public ::testing::Test { // new connection using the newly generated ice candidates. // Before calling this function the end points must be configured. void TestHandleIceUfragPasswordChanged() { - ScopedFakeClock clock; ep1_ch1()->SetRemoteIceParameters(kIceParams[1]); ep2_ch1()->SetRemoteIceParameters(kIceParams[0]); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); const Candidate* old_local_candidate1 = LocalCandidate(ep1_ch1()); const Candidate* old_local_candidate2 = LocalCandidate(ep2_ch1()); @@ -812,26 +851,22 @@ class P2PTransportChannelTestBase : public ::testing::Test { ep2_ch1()->SetRemoteIceParameters(kIceParams[2]); ep2_ch1()->MaybeStartGathering(); - EXPECT_THAT( - WaitUntil([&] { return LocalCandidate(ep1_ch1())->generation(); }, - Ne(old_local_candidate1->generation()), - {.timeout = kMediumTimeout, .clock = &clock}), - IsRtcOk()); - EXPECT_THAT( - WaitUntil([&] { return LocalCandidate(ep2_ch1())->generation(); }, - Ne(old_local_candidate2->generation()), - {.timeout = kMediumTimeout, .clock = &clock}), - IsRtcOk()); - EXPECT_THAT( - WaitUntil([&] { return RemoteCandidate(ep1_ch1())->generation(); }, - Ne(old_remote_candidate1->generation()), - {.timeout = kMediumTimeout, .clock = &clock}), - IsRtcOk()); - EXPECT_THAT( - WaitUntil([&] { return RemoteCandidate(ep2_ch1())->generation(); }, - Ne(old_remote_candidate2->generation()), - {.timeout = kMediumTimeout, .clock = &clock}), - IsRtcOk()); + EXPECT_THAT(MediumWait().Until( + [&] { return LocalCandidate(ep1_ch1())->generation(); }, + Ne(old_local_candidate1->generation())), + IsRtcOk()); + EXPECT_THAT(MediumWait().Until( + [&] { return LocalCandidate(ep2_ch1())->generation(); }, + Ne(old_local_candidate2->generation())), + IsRtcOk()); + EXPECT_THAT(MediumWait().Until( + [&] { return RemoteCandidate(ep1_ch1())->generation(); }, + Ne(old_remote_candidate1->generation())), + IsRtcOk()); + EXPECT_THAT(MediumWait().Until( + [&] { return RemoteCandidate(ep2_ch1())->generation(); }, + Ne(old_remote_candidate2->generation())), + IsRtcOk()); EXPECT_EQ(1u, RemoteCandidate(ep2_ch1())->generation()); EXPECT_EQ(1u, RemoteCandidate(ep1_ch1())->generation()); } @@ -843,7 +878,7 @@ class P2PTransportChannelTestBase : public ::testing::Test { } void OnReadyToSend(PacketTransportInternal* transport) { - GetEndpoint(transport)->ready_to_send_ = true; + GetEndpoint(transport)->set_ready_to_send(true); } // We pass the candidates directly to the other side. @@ -851,11 +886,11 @@ class P2PTransportChannelTestBase : public ::testing::Test { if (force_relay_ && !c.is_relay()) return; - if (GetEndpoint(ch)->save_candidates_) { - GetEndpoint(ch)->saved_candidates_.push_back( + if (GetEndpoint(ch)->save_candidates()) { + GetEndpoint(ch)->saved_candidates().push_back( {.channel = ch, .candidate = c}); } else { - main_.PostTask(SafeTask( + main_->PostTask(SafeTask( safety_, [this, ch, c = c]() mutable { AddCandidate(ch, c); })); } } @@ -876,13 +911,13 @@ class P2PTransportChannelTestBase : public ::testing::Test { } void PauseCandidates(int endpoint) { - GetEndpoint(endpoint)->save_candidates_ = true; + GetEndpoint(endpoint)->set_save_candidates(true); } void OnCandidatesRemoved(IceTransportInternal* ch, const std::vector& candidates) { // Candidate removals are not paused. - main_.PostTask(SafeTask(safety_, [this, ch, candidates]() mutable { + main_->PostTask(SafeTask(safety_, [this, ch, candidates]() mutable { P2PTransportChannel* rch = GetRemoteChannel(ch); if (rch == nullptr) { return; @@ -896,7 +931,7 @@ class P2PTransportChannelTestBase : public ::testing::Test { // Tcp candidate verification has to be done when they are generated. void VerifySavedTcpCandidates(int endpoint, absl::string_view tcptype) { - for (auto& data : GetEndpoint(endpoint)->saved_candidates_) { + for (auto& data : GetEndpoint(endpoint)->saved_candidates()) { EXPECT_EQ(data.candidate.protocol(), TCP_PROTOCOL_NAME); EXPECT_EQ(data.candidate.tcptype(), tcptype); if (data.candidate.tcptype() == TCPTYPE_ACTIVE_STR) { @@ -911,17 +946,17 @@ class P2PTransportChannelTestBase : public ::testing::Test { void ResumeCandidates(int endpoint) { Endpoint* ed = GetEndpoint(endpoint); - std::vector candidates = std::move(ed->saved_candidates_); + std::vector candidates = std::move(ed->saved_candidates()); if (!candidates.empty()) { - main_.PostTask(SafeTask( + main_->PostTask(SafeTask( safety_, [this, candidates = std::move(candidates)]() mutable { for (CandidateData& data : candidates) { AddCandidate(data.channel, data.candidate); } })); } - ed->saved_candidates_.clear(); - ed->save_candidates_ = false; + ed->saved_candidates().clear(); + ed->set_save_candidates(false); } void AddCandidate(IceTransportInternal* channel, Candidate& candidate) { @@ -1000,7 +1035,7 @@ class P2PTransportChannelTestBase : public ::testing::Test { return nullptr; } std::list& GetPacketList(PacketTransportInternal* transport) { - return GetChannelData(transport)->ch_packets_; + return GetChannelData(transport)->ch_packets(); } enum RemoteIceParameterSource { FROM_CANDIDATE, FROM_SETICEPARAMETERS }; @@ -1022,13 +1057,15 @@ class P2PTransportChannelTestBase : public ::testing::Test { void OnNominated(Connection* conn) { nominated_ = true; } bool nominated() { return nominated_; } - private: + protected: std::unique_ptr vss_; std::unique_ptr nss_; std::unique_ptr ss_; + GlobalSimulatedTimeController time_controller_; + Environment env_; std::unique_ptr socket_factory_; - AutoSocketServerThread main_; + Thread* main_; scoped_refptr safety_ = PendingTaskSafetyFlag::Create(); TestStunServer::StunServerPtr stun_server_; @@ -1132,22 +1169,21 @@ const P2PTransportChannelTestBase::Result // Just test every combination of the configs in the Config enum. class P2PTransportChannelTest : public P2PTransportChannelTestBase { protected: - void ConfigureEndpoints(const Environment& env, - Config config1, + void ConfigureEndpoints(Config config1, Config config2, int allocator_flags1, int allocator_flags2) { - CreatePortAllocators(env); - ConfigureEndpoint(env, 0, config1); + CreatePortAllocators(); + ConfigureEndpoint(0, config1); SetAllocatorFlags(0, allocator_flags1); SetAllocationStepDelay(0, kMinimumStepDelay); - ConfigureEndpoint(env, 1, config2); + ConfigureEndpoint(1, config2); SetAllocatorFlags(1, allocator_flags2); SetAllocationStepDelay(1, kMinimumStepDelay); set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); } - void ConfigureEndpoint(const Environment& env, int endpoint, Config config) { + void ConfigureEndpoint(int endpoint, Config config) { switch (config) { case OPEN: AddAddress(endpoint, kPublicAddrs[endpoint]); @@ -1159,7 +1195,7 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { AddAddress(endpoint, kPrivateAddrs[endpoint]); // Add a single NAT of the desired type nat() - ->AddTranslator(env, kPublicAddrs[endpoint], kNatAddrs[endpoint], + ->AddTranslator(env_, kPublicAddrs[endpoint], kNatAddrs[endpoint], static_cast(config - NAT_FULL_CONE)) ->AddClient(kPrivateAddrs[endpoint]); break; @@ -1168,11 +1204,11 @@ class P2PTransportChannelTest : public P2PTransportChannelTestBase { AddAddress(endpoint, kCascadedPrivateAddrs[endpoint]); // Add a two cascaded NATs of the desired types nat() - ->AddTranslator(env, kPublicAddrs[endpoint], kNatAddrs[endpoint], + ->AddTranslator(env_, kPublicAddrs[endpoint], kNatAddrs[endpoint], (config == Config::NAT_DOUBLE_CONE) ? NATType::NAT_OPEN_CONE : NATType::NAT_SYMMETRIC) - ->AddTranslator(env, kPrivateAddrs[endpoint], + ->AddTranslator(env_, kPrivateAddrs[endpoint], kCascadedNatAddrs[endpoint], NAT_OPEN_CONE) ->AddClient(kCascadedPrivateAddrs[endpoint]); break; @@ -1259,16 +1295,14 @@ const P2PTransportChannelMatrixTest::Result* // The actual tests that exercise all the various configurations. // Test names are of the form P2PTransportChannelTest_TestOPENToNAT_FULL_CONE -#define P2P_TEST_DECLARATION(x, y, z) \ - TEST_P(P2PTransportChannelMatrixTest, z##Test##x##To##y) { \ - const Environment env = \ - CreateEnvironment(CreateTestFieldTrialsPtr(GetParam())); \ - ConfigureEndpoints(env, x, y, PORTALLOCATOR_ENABLE_SHARED_SOCKET, \ - PORTALLOCATOR_ENABLE_SHARED_SOCKET); \ - if (kMatrix[x][y] != NULL) \ - Test(env, *kMatrix[x][y]); \ - else \ - RTC_LOG(LS_WARNING) << "Not yet implemented"; \ +#define P2P_TEST_DECLARATION(x, y, z) \ + TEST_P(P2PTransportChannelMatrixTest, z##Test##x##To##y) { \ + ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_SOCKET, \ + PORTALLOCATOR_ENABLE_SHARED_SOCKET); \ + if (kMatrix[x][y] != NULL) \ + Test(*kMatrix[x][y]); \ + else \ + RTC_LOG(LS_WARNING) << "Not yet implemented"; \ } #define P2P_TEST(x, y) P2P_TEST_DECLARATION(x, y, /* empty argument */) @@ -1305,10 +1339,9 @@ INSTANTIATE_TEST_SUITE_P( // Test that we restart candidate allocation when local ufrag&pwd changed. // Standard Ice protocol is used. TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); - CreateChannels(env); + CreateChannels(); TestHandleIceUfragPasswordChanged(); DestroyChannels(); } @@ -1316,29 +1349,24 @@ TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) { // Same as above test, but with a symmetric NAT. // We should end up with relay<->prflx candidate pairs, with generation "1". TEST_F(P2PTransportChannelTest, HandleUfragPwdChangeSymmetricNat) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, NAT_SYMMETRIC, NAT_SYMMETRIC, - kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); - CreateChannels(env); + ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags, + kDefaultPortAllocatorFlags); + CreateChannels(); TestHandleIceUfragPasswordChanged(); DestroyChannels(); } // Test the operation of GetStats. TEST_F(P2PTransportChannelTest, GetStats) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); - CreateChannels(env); - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->receiving() && ep1_ch1()->writable() && - ep2_ch1()->receiving() && ep2_ch1()->writable(); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + CreateChannels(); + EXPECT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->receiving() && ep1_ch1()->writable() && + ep2_ch1()->receiving() && ep2_ch1()->writable(); + })); // Sends and receives 10 packets. - TestSendRecv(&clock); + TestSendRecv(); // Try sending a packet which is discarded due to the socket being blocked. virtual_socket_server()->SetSendingBlocked(true); @@ -1378,25 +1406,21 @@ TEST_F(P2PTransportChannelTest, GetStats) { } TEST_F(P2PTransportChannelTest, GetStatsSwitchConnection) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); IceConfig continual_gathering_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); AddAddress(0, kAlternateAddrs[1], "rmnet0", ADAPTER_TYPE_CELLULAR); - CreateChannels(env, continual_gathering_config, continual_gathering_config); - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->receiving() && ep1_ch1()->writable() && - ep2_ch1()->receiving() && ep2_ch1()->writable(); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + CreateChannels(continual_gathering_config, continual_gathering_config); + EXPECT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->receiving() && ep1_ch1()->writable() && + ep2_ch1()->receiving() && ep2_ch1()->writable(); + })); // Sends and receives 10 packets. - TestSendRecv(&clock); + TestSendRecv(); IceTransportStats ice_transport_stats; ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -1426,12 +1450,12 @@ TEST_F(P2PTransportChannelTest, GetStatsSwitchConnection) { const_cast(old_selected_connection)); EXPECT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kMediumTimeout, .clock = &clock}), + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); // Sends and receives 10 packets. - TestSendRecv(&clock); + TestSendRecv(); IceTransportStats ice_transport_stats2; ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats2)); @@ -1456,36 +1480,34 @@ TEST_F(P2PTransportChannelTest, GetStatsSwitchConnection) { // change if and only if continual gathering is enabled. TEST_F(P2PTransportChannelTest, TestIceRegatheringReasonContinualGatheringByNetworkChange) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); // ep1 gathers continually but ep2 does not. IceConfig continual_gathering_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); IceConfig default_config; - CreateChannels(env, continual_gathering_config, default_config); + CreateChannels(continual_gathering_config, default_config); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE( + MediumWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); // Adding address in ep1 will trigger continual gathering. AddAddress(0, kAlternateAddrs[0]); - EXPECT_THAT( - WaitUntil( - [&] { - return GetEndpoint(0)->GetIceRegatheringCountForReason( - IceRegatheringReason::NETWORK_CHANGE); - }, - Eq(1), {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); + EXPECT_THAT(Wait(TimeDelta::Millis(1900)) + .Until( + [&] { + return GetEndpoint(0)->GetIceRegatheringCountForReason( + IceRegatheringReason::NETWORK_CHANGE); + }, + Eq(1)), + IsRtcOk()); ep2_ch1()->SetIceParameters(kIceParams[3]); ep2_ch1()->SetRemoteIceParameters(kIceParams[2]); ep2_ch1()->MaybeStartGathering(); AddAddress(1, kAlternateAddrs[1]); - SIMULATED_WAIT(false, kDefaultTimeout.ms(), clock); + time_controller_.AdvanceTime(kDefaultTimeout); // ep2 has not enabled continual gathering. EXPECT_EQ(0, GetEndpoint(1)->GetIceRegatheringCountForReason( IceRegatheringReason::NETWORK_CHANGE)); @@ -1497,9 +1519,7 @@ TEST_F(P2PTransportChannelTest, // failure if and only if continual gathering is enabled. TEST_F(P2PTransportChannelTest, TestIceRegatheringReasonContinualGatheringByNetworkFailure) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); // ep1 gathers continually but ep2 does not. IceConfig config1 = @@ -1507,15 +1527,15 @@ TEST_F(P2PTransportChannelTest, config1.regather_on_failed_networks_interval = TimeDelta::Seconds(2); IceConfig config2; config2.regather_on_failed_networks_interval = TimeDelta::Seconds(2); - CreateChannels(env, config1, config2); + CreateChannels(config1, config2); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); fw()->AddRule(false, FP_ANY, FD_ANY, kPublicAddrs[0]); // Timeout value such that all connections are deleted. const int kNetworkFailureTimeout = 35000; - SIMULATED_WAIT(false, kNetworkFailureTimeout, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(kNetworkFailureTimeout)); EXPECT_LE(1, GetEndpoint(0)->GetIceRegatheringCountForReason( IceRegatheringReason::NETWORK_FAILURE)); EXPECT_EQ(0, GetEndpoint(1)->GetIceRegatheringCountForReason( @@ -1527,12 +1547,11 @@ TEST_F(P2PTransportChannelTest, // Test that we properly create a connection on a STUN ping from unknown address // when the signaling is slow. TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // Emulate no remote parameters coming in. set_remote_ice_parameter_source(FROM_CANDIDATE); - CreateChannels(env); + CreateChannels(); // Only have remote parameters come in for ep2, not ep1. ep2_ch1()->SetRemoteIceParameters(kIceParams[0]); @@ -1542,9 +1561,10 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { // Wait until the callee becomes writable to make sure that a ping request is // received by the caller before their remote ICE credentials are set. - ASSERT_THAT(WaitUntil([&] { return ep2_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + Wait(TimeDelta::Seconds(11)) + .Until([&] { return ep2_ch1()->selected_connection(); }, Ne(nullptr)), + IsRtcOk()); // Add two sets of remote ICE credentials, so that the ones used by the // candidate will be generation 1 instead of 0. ep1_ch1()->SetRemoteIceParameters(kIceParams[3]); @@ -1552,12 +1572,12 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { // The caller should have the selected connection connected to the peer // reflexive candidate. const Connection* selected_connection = nullptr; - ASSERT_THAT(WaitUntil( + ASSERT_THAT(MediumWait().Until( [&] { return selected_connection = ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), + Ne(nullptr)), IsRtcOk()); EXPECT_TRUE(selected_connection->remote_candidate().is_prflx()); EXPECT_EQ(kIceUfrag[1], selected_connection->remote_candidate().username()); @@ -1566,11 +1586,9 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { ResumeCandidates(1); // Verify ep1's selected connection is updated to use the 'local' candidate. - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->remote_candidate().is_local(); - }, - {.timeout = kMediumTimeout})); + EXPECT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->selected_connection()->remote_candidate().is_local(); + })); EXPECT_EQ(selected_connection, ep1_ch1()->selected_connection()); DestroyChannels(); } @@ -1580,11 +1598,10 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) { // 2. the candidate pair stats // until we learn the same address from signaling. TEST_F(P2PTransportChannelTest, PeerReflexiveRemoteCandidateIsSanitized) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); // Emulate no remote parameters coming in. set_remote_ice_parameter_source(FROM_CANDIDATE); - CreateChannels(env); + CreateChannels(); // Only have remote parameters come in for ep2, not ep1. ep2_ch1()->SetRemoteIceParameters(kIceParams[0]); @@ -1592,13 +1609,15 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveRemoteCandidateIsSanitized) { // candidate. PauseCandidates(1); - ASSERT_THAT(WaitUntil([&] { return ep2_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + MediumWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); ep1_ch1()->SetRemoteIceParameters(kIceParams[1]); - ASSERT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); // Check the selected candidate pair. auto pair_ep1 = ep1_ch1()->GetSelectedCandidatePair(); @@ -1619,12 +1638,10 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveRemoteCandidateIsSanitized) { // Let ep1 receive the remote candidate to update its type from prflx to host. ResumeCandidates(1); - ASSERT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection() != nullptr && - ep1_ch1()->selected_connection()->remote_candidate().is_local(); - }, - {.timeout = kMediumTimeout})); + ASSERT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->selected_connection() != nullptr && + ep1_ch1()->selected_connection()->remote_candidate().is_local(); + })); // We should be able to reveal the address after it is learnt via // AddIceCandidate. @@ -1650,12 +1667,11 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveRemoteCandidateIsSanitized) { // Test that we properly create a connection on a STUN ping from unknown address // when the signaling is slow and the end points are behind NAT. TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, NAT_SYMMETRIC, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, NAT_SYMMETRIC, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // Emulate no remote parameters coming in. set_remote_ice_parameter_source(FROM_CANDIDATE); - CreateChannels(env); + CreateChannels(); // Only have remote parameters come in for ep2, not ep1. ep2_ch1()->SetRemoteIceParameters(kIceParams[0]); // Pause sending ep2's candidates to ep1 until ep1 receives the peer reflexive @@ -1664,9 +1680,10 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) { // Wait until the callee becomes writable to make sure that a ping request is // received by the caller before their remote ICE credentials are set. - ASSERT_THAT(WaitUntil([&] { return ep2_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + MediumWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); // Add two sets of remote ICE credentials, so that the ones used by the // candidate will be generation 1 instead of 0. ep1_ch1()->SetRemoteIceParameters(kIceParams[3]); @@ -1675,12 +1692,12 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) { // The caller's selected connection should be connected to the peer reflexive // candidate. const Connection* selected_connection = nullptr; - ASSERT_THAT(WaitUntil( + ASSERT_THAT(MediumWait().Until( [&] { return selected_connection = ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), + Ne(nullptr)), IsRtcOk()); EXPECT_TRUE(selected_connection->remote_candidate().is_prflx()); EXPECT_EQ(kIceUfrag[1], selected_connection->remote_candidate().username()); @@ -1689,11 +1706,9 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) { ResumeCandidates(1); - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->remote_candidate().is_prflx(); - }, - {.timeout = kMediumTimeout})); + EXPECT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->selected_connection()->remote_candidate().is_prflx(); + })); EXPECT_EQ(selected_connection, ep1_ch1()->selected_connection()); DestroyChannels(); } @@ -1709,21 +1724,20 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) { // prioritized above new-generation candidate pairs. TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithIceRestart) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // Only gather relay candidates, so that when the prflx candidate arrives // it's prioritized above the current candidate pair. - GetEndpoint(0)->allocator_->SetCandidateFilter(CF_RELAY); - GetEndpoint(1)->allocator_->SetCandidateFilter(CF_RELAY); + GetEndpoint(0)->allocator()->SetCandidateFilter(CF_RELAY); + GetEndpoint(1)->allocator()->SetCandidateFilter(CF_RELAY); // Setting this allows us to control when SetRemoteIceParameters is called. set_remote_ice_parameter_source(FROM_CANDIDATE); - CreateChannels(env); + CreateChannels(); // Wait for the initial connection to be made. ep1_ch1()->SetRemoteIceParameters(kIceParams[1]); ep2_ch1()->SetRemoteIceParameters(kIceParams[0]); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE( + MediumWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); // Simulate an ICE restart on ep2, but don't signal the candidate or new // ICE parameters until after a prflx connection has been made. @@ -1735,11 +1749,9 @@ TEST_F(P2PTransportChannelTest, // The caller should have the selected connection connected to the peer // reflexive candidate. - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->remote_candidate().is_prflx(); - }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE(Wait(TimeDelta::Seconds(8)).Until([&] { + return ep1_ch1()->selected_connection()->remote_candidate().is_prflx(); + })); const Connection* prflx_selected_connection = ep1_ch1()->selected_connection(); @@ -1753,35 +1765,31 @@ TEST_F(P2PTransportChannelTest, // their information to update the peer reflexive candidate. ResumeCandidates(1); - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->remote_candidate().is_relay(); - }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection()->remote_candidate().is_relay(); + })); EXPECT_EQ(prflx_selected_connection, ep1_ch1()->selected_connection()); DestroyChannels(); } // Test that if remote candidates don't have ufrag and pwd, we still work. TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); - CreateChannels(env); + CreateChannels(); const Connection* selected_connection = nullptr; // Wait until the callee's connections are created. - EXPECT_THAT(WaitUntil( + EXPECT_THAT(DefaultWait().Until( [&] { return selected_connection = ep2_ch1()->selected_connection(); }, - NotNull(), {.timeout = kMediumTimeout, .clock = &clock}), + NotNull()), IsRtcOk()); // Wait to make sure the selected connection is not changed. - SIMULATED_WAIT(ep2_ch1()->selected_connection() != selected_connection, - kShortTimeout.ms(), clock); + (void)MediumWait().Until( + [&] { return ep2_ch1()->selected_connection() != selected_connection; }); EXPECT_TRUE(ep2_ch1()->selected_connection() == selected_connection); DestroyChannels(); } @@ -1789,17 +1797,15 @@ TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) { // Test that a host behind NAT cannot be reached when incoming_only // is set to true. TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, NAT_FULL_CONE, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(NAT_FULL_CONE, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); SetAllocatorFlags(0, kOnlyLocalPorts); - CreateChannels(env); + CreateChannels(); ep1_ch1()->set_incoming_only(true); // Pump for 1 second and verify that the channels are not connected. - SIMULATED_WAIT(false, kShortTimeout.ms(), clock); + time_controller_.AdvanceTime(kShortTimeout); EXPECT_FALSE(ep1_ch1()->receiving()); EXPECT_FALSE(ep1_ch1()->writable()); @@ -1812,17 +1818,15 @@ TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) { // Test that a peer behind NAT can connect to a peer that has // incoming_only flag set. TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, NAT_FULL_CONE, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, NAT_FULL_CONE, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); SetAllocatorFlags(0, kOnlyLocalPorts); - CreateChannels(env); + CreateChannels(); ep1_ch1()->set_incoming_only(true); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE( + ShortWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); DestroyChannels(); } @@ -1831,7 +1835,6 @@ TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) { // connections. This has been observed in some scenarios involving // VPNs/firewalls. TEST_F(P2PTransportChannelTest, CanOnlyMakeOutgoingTcpConnections) { - const Environment env = CreateEnvironment(); // The PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS flag is required if the // application needs this use case to work, since the application must accept // the tradeoff that more candidates need to be allocated. @@ -1839,7 +1842,7 @@ TEST_F(P2PTransportChannelTest, CanOnlyMakeOutgoingTcpConnections) { // TODO(deadbeef): Later, make this flag the default, and do more elegant // things to ensure extra candidates don't waste resources? ConfigureEndpoints( - env, OPEN, OPEN, + OPEN, OPEN, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS, kDefaultPortAllocatorFlags); // In order to simulate nothing working but outgoing TCP connections, prevent @@ -1847,17 +1850,15 @@ TEST_F(P2PTransportChannelTest, CanOnlyMakeOutgoingTcpConnections) { // "any" addresses. It can then only make a connection by using "Connect()". fw()->SetUnbindableIps( {GetAnyIP(AF_INET), GetAnyIP(AF_INET6), kPublicAddrs[0].ipaddr()}); - CreateChannels(env); + CreateChannels(); // Expect a IceCandidateType::kPrflx candidate on the side that can only make // outgoing connections, endpoint 0. - Test(env, kPrflxTcpToLocalTcp); + Test(kPrflxTcpToLocalTcp); DestroyChannels(); } TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kPublicAddrs[1]); @@ -1882,7 +1883,7 @@ TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) { // Pause candidate so we could verify the candidate properties. PauseCandidates(0); PauseCandidates(1); - CreateChannels(env); + CreateChannels(); // Verify tcp candidates. VerifySavedTcpCandidates(0, TCPTYPE_PASSIVE_STR); @@ -1892,32 +1893,28 @@ TEST_F(P2PTransportChannelTest, TestTcpConnectionsFromActiveToPassive) { ResumeCandidates(0); ResumeCandidates(1); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - kPublicAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kPublicAddrs[1]); + })); - TestSendRecv(&clock); + TestSendRecv(); DestroyChannels(); } // Test that tcptype is set on all candidates for a connection running over TCP. TEST_F(P2PTransportChannelTest, TestTcpConnectionTcptypeSet) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, BLOCK_UDP_AND_INCOMING_TCP, OPEN, + ConfigureEndpoints(BLOCK_UDP_AND_INCOMING_TCP, OPEN, PORTALLOCATOR_ENABLE_SHARED_SOCKET, PORTALLOCATOR_ENABLE_SHARED_SOCKET); SetAllowTcpListen(0, false); // active. SetAllowTcpListen(1, true); // actpass. - CreateChannels(env); + CreateChannels(); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); - SIMULATED_WAIT(false, kDefaultTimeout.ms(), clock); + EXPECT_TRUE( + ShortWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + time_controller_.AdvanceTime(kDefaultTimeout); EXPECT_EQ(RemoteCandidate(ep1_ch1())->tcptype(), "passive"); EXPECT_EQ(LocalCandidate(ep1_ch1())->tcptype(), "active"); @@ -1928,9 +1925,7 @@ TEST_F(P2PTransportChannelTest, TestTcpConnectionTcptypeSet) { } TEST_F(P2PTransportChannelTest, TestIceRoleConflict) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kPublicAddrs[1]); @@ -1938,52 +1933,47 @@ TEST_F(P2PTransportChannelTest, TestIceRoleConflict) { SetIceRole(0, ICEROLE_CONTROLLING); SetIceRole(1, ICEROLE_CONTROLLING); - CreateChannels(env); + CreateChannels(); bool first_endpoint_has_lower_tiebreaker = - GetEndpoint(0)->allocator_->ice_tiebreaker() < - GetEndpoint(1)->allocator_->ice_tiebreaker(); + GetEndpoint(0)->allocator()->ice_tiebreaker() < + GetEndpoint(1)->allocator()->ice_tiebreaker(); // Since both the channels initiated with controlling state, the channel with // the lower tiebreaker should receive SignalRoleConflict. - EXPECT_TRUE(WaitUntil( - [&] { - return GetRoleConflict(first_endpoint_has_lower_tiebreaker ? 0 : 1); - }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return GetRoleConflict(first_endpoint_has_lower_tiebreaker ? 0 : 1); + })); EXPECT_FALSE(GetRoleConflict(first_endpoint_has_lower_tiebreaker ? 1 : 0)); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE( + ShortWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); EXPECT_TRUE(ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection()); - TestSendRecv(&clock); + TestSendRecv(); DestroyChannels(); } // Tests that the ice configs (protocol and role) can be passed down to ports. TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kPublicAddrs[1]); SetIceRole(0, ICEROLE_CONTROLLING); SetIceRole(1, ICEROLE_CONTROLLING); - CreateChannels(env); + CreateChannels(); // Pick channel with the higher tiebreaker so its role won't change unless we // tell it to. P2PTransportChannel* channel = - GetEndpoint(0)->allocator_->ice_tiebreaker() > - GetEndpoint(1)->allocator_->ice_tiebreaker() + GetEndpoint(0)->allocator()->ice_tiebreaker() > + GetEndpoint(1)->allocator()->ice_tiebreaker() ? ep1_ch1() : ep2_ch1(); - EXPECT_THAT(WaitUntil([&] { return channel->ports(); }, SizeIs(2), - {.timeout = kShortTimeout, .clock = &clock}), + EXPECT_THAT(ShortWait().Until([&] { return channel->ports(); }, SizeIs(2)), IsRtcOk()); EXPECT_THAT(channel->ports(), Each(Property(&PortInterface::GetIceRole, @@ -1994,42 +1984,39 @@ TEST_F(P2PTransportChannelTest, TestIceConfigWillPassDownToPort) { EXPECT_THAT(channel->ports(), Each(Property(&PortInterface::GetIceRole, Eq(ICEROLE_CONTROLLED)))); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE( + ShortWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); EXPECT_TRUE(ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection()); - TestSendRecv(&clock); + TestSendRecv(); DestroyChannels(); } // Verify that we can set DSCP value and retrieve properly from P2PTC. TEST_F(P2PTransportChannelTest, TestDefaultDscpValue) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kPublicAddrs[1]); - CreateChannels(env); - EXPECT_EQ(DSCP_NO_CHANGE, GetEndpoint(0)->cd1_.ch_->DefaultDscpValue()); - EXPECT_EQ(DSCP_NO_CHANGE, GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); - GetEndpoint(0)->cd1_.ch_->SetOption(Socket::OPT_DSCP, DSCP_CS6); - GetEndpoint(1)->cd1_.ch_->SetOption(Socket::OPT_DSCP, DSCP_CS6); - EXPECT_EQ(DSCP_CS6, GetEndpoint(0)->cd1_.ch_->DefaultDscpValue()); - EXPECT_EQ(DSCP_CS6, GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); - GetEndpoint(0)->cd1_.ch_->SetOption(Socket::OPT_DSCP, DSCP_AF41); - GetEndpoint(1)->cd1_.ch_->SetOption(Socket::OPT_DSCP, DSCP_AF41); - EXPECT_EQ(DSCP_AF41, GetEndpoint(0)->cd1_.ch_->DefaultDscpValue()); - EXPECT_EQ(DSCP_AF41, GetEndpoint(1)->cd1_.ch_->DefaultDscpValue()); + CreateChannels(); + EXPECT_EQ(DSCP_NO_CHANGE, GetEndpoint(0)->cd1().ch()->DefaultDscpValue()); + EXPECT_EQ(DSCP_NO_CHANGE, GetEndpoint(1)->cd1().ch()->DefaultDscpValue()); + GetEndpoint(0)->cd1().ch()->SetOption(Socket::OPT_DSCP, DSCP_CS6); + GetEndpoint(1)->cd1().ch()->SetOption(Socket::OPT_DSCP, DSCP_CS6); + EXPECT_EQ(DSCP_CS6, GetEndpoint(0)->cd1().ch()->DefaultDscpValue()); + EXPECT_EQ(DSCP_CS6, GetEndpoint(1)->cd1().ch()->DefaultDscpValue()); + GetEndpoint(0)->cd1().ch()->SetOption(Socket::OPT_DSCP, DSCP_AF41); + GetEndpoint(1)->cd1().ch()->SetOption(Socket::OPT_DSCP, DSCP_AF41); + EXPECT_EQ(DSCP_AF41, GetEndpoint(0)->cd1().ch()->DefaultDscpValue()); + EXPECT_EQ(DSCP_AF41, GetEndpoint(1)->cd1().ch()->DefaultDscpValue()); DestroyChannels(); } // Verify IPv6 connection is preferred over IPv4. TEST_F(P2PTransportChannelTest, TestIPv6Connections) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kIPv6PublicAddrs[0]); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kIPv6PublicAddrs[1]); @@ -2044,25 +2031,21 @@ TEST_F(P2PTransportChannelTest, TestIPv6Connections) { SetAllocatorFlags( 1, PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_ENABLE_IPV6_ON_WIFI); - CreateChannels(env); + CreateChannels(); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected( - ep1_ch1(), ep2_ch1(), kIPv6PublicAddrs[0], kIPv6PublicAddrs[1]); - }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE(ShortWait().Until([&] { + return CheckCandidatePairAndConnected( + ep1_ch1(), ep2_ch1(), kIPv6PublicAddrs[0], kIPv6PublicAddrs[1]); + })); - TestSendRecv(&clock); + TestSendRecv(); DestroyChannels(); } // Testing forceful TURN connections. TEST_F(P2PTransportChannelTest, TestForceTurn) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); ConfigureEndpoints( - env, NAT_PORT_RESTRICTED, NAT_SYMMETRIC, + NAT_PORT_RESTRICTED, NAT_SYMMETRIC, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET); set_force_relay(true); @@ -2070,10 +2053,10 @@ TEST_F(P2PTransportChannelTest, TestForceTurn) { SetAllocationStepDelay(0, kMinimumStepDelay); SetAllocationStepDelay(1, kMinimumStepDelay); - CreateChannels(env); + CreateChannels(); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE( + ShortWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); EXPECT_TRUE(ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection()); @@ -2083,16 +2066,14 @@ TEST_F(P2PTransportChannelTest, TestForceTurn) { EXPECT_TRUE(RemoteCandidate(ep2_ch1())->is_relay()); EXPECT_TRUE(LocalCandidate(ep2_ch1())->is_relay()); - TestSendRecv(&clock); + TestSendRecv(); DestroyChannels(); } // Test that if continual gathering is set to true, ICE gathering state will // not change to "Complete", and vice versa. TEST_F(P2PTransportChannelTest, TestContinualGathering) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); SetAllocationStepDelay(0, kDefaultStepDelay); SetAllocationStepDelay(1, kDefaultStepDelay); @@ -2100,13 +2081,14 @@ TEST_F(P2PTransportChannelTest, TestContinualGathering) { CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); // By default, ep2 does not gather continually. IceConfig default_config; - CreateChannels(env, continual_gathering_config, default_config); + CreateChannels(continual_gathering_config, default_config); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); - SIMULATED_WAIT( - IceGatheringState::kIceGatheringComplete == ep1_ch1()->gathering_state(), - kShortTimeout.ms(), clock); + EXPECT_TRUE( + MediumWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + (void)MediumWait().Until([&] { + return IceGatheringState::kIceGatheringComplete == + ep1_ch1()->gathering_state(); + }); EXPECT_EQ(IceGatheringState::kIceGatheringGathering, ep1_ch1()->gathering_state()); // By now, ep2 should have completed gathering. @@ -2119,13 +2101,11 @@ TEST_F(P2PTransportChannelTest, TestContinualGathering) { // Test that a connection succeeds when the P2PTransportChannel uses a pooled // PortAllocatorSession that has not yet finished gathering candidates. TEST_F(P2PTransportChannelTest, TestUsingPooledSessionBeforeDoneGathering) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // First create a pooled session for each endpoint. - auto& allocator_1 = GetEndpoint(0)->allocator_; - auto& allocator_2 = GetEndpoint(1)->allocator_; + auto* allocator_1 = GetEndpoint(0)->allocator(); + auto* allocator_2 = GetEndpoint(1)->allocator(); int pool_size = 1; allocator_1->SetConfiguration(allocator_1->stun_servers(), allocator_1->turn_servers(), pool_size, @@ -2145,10 +2125,10 @@ TEST_F(P2PTransportChannelTest, TestUsingPooledSessionBeforeDoneGathering) { EXPECT_TRUE(pooled_session_2->ReadyPorts().empty()); EXPECT_TRUE(pooled_session_2->ReadyCandidates().empty()); // Now let the endpoints connect and try exchanging some data. - CreateChannels(env); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); - TestSendRecv(&clock); + CreateChannels(); + EXPECT_TRUE( + ShortWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + TestSendRecv(); // Make sure the P2PTransportChannels are actually using ports from the // pooled sessions. auto pooled_ports_1 = pooled_session_1->ReadyPorts(); @@ -2163,13 +2143,11 @@ TEST_F(P2PTransportChannelTest, TestUsingPooledSessionBeforeDoneGathering) { // Test that a connection succeeds when the P2PTransportChannel uses a pooled // PortAllocatorSession that already finished gathering candidates. TEST_F(P2PTransportChannelTest, TestUsingPooledSessionAfterDoneGathering) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // First create a pooled session for each endpoint. - auto& allocator_1 = GetEndpoint(0)->allocator_; - auto& allocator_2 = GetEndpoint(1)->allocator_; + auto* allocator_1 = GetEndpoint(0)->allocator(); + auto* allocator_2 = GetEndpoint(1)->allocator(); int pool_size = 1; allocator_1->SetConfiguration(allocator_1->stun_servers(), allocator_1->turn_servers(), pool_size, @@ -2185,17 +2163,15 @@ TEST_F(P2PTransportChannelTest, TestUsingPooledSessionAfterDoneGathering) { ASSERT_NE(nullptr, pooled_session_2); // Wait for the pooled sessions to finish gathering before the // P2PTransportChannels try to use them. - EXPECT_TRUE(WaitUntil( - [&] { - return pooled_session_1->CandidatesAllocationDone() && - pooled_session_2->CandidatesAllocationDone(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return pooled_session_1->CandidatesAllocationDone() && + pooled_session_2->CandidatesAllocationDone(); + })); // Now let the endpoints connect and try exchanging some data. - CreateChannels(env); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); - TestSendRecv(&clock); + CreateChannels(); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + TestSendRecv(); // Make sure the P2PTransportChannels are actually using ports from the // pooled sessions. auto pooled_ports_1 = pooled_session_1->ReadyPorts(); @@ -2214,20 +2190,18 @@ TEST_F(P2PTransportChannelTest, TestUsingPooledSessionAfterDoneGathering) { // class that operates on a single P2PTransportChannel, once an appropriate one // (which supports TURN servers and TURN candidate gathering) is available. TEST_F(P2PTransportChannelTest, TurnToTurnPresumedWritable) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // Only configure one channel so we can control when the remote candidate // is added. - GetEndpoint(0)->cd1_.ch_ = CreateChannel( - env, 0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]); + GetEndpoint(0)->cd1().set_ch(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[0], kIceParams[1])); IceConfig config; config.presume_writable_when_fully_relayed = true; ep1_ch1()->SetIceConfig(config); ep1_ch1()->MaybeStartGathering(); - EXPECT_THAT(WaitUntil([&] { return ep1_ch1()->gathering_state(); }, - Eq(IceGatheringState::kIceGatheringComplete), - {.timeout = kDefaultTimeout}), + EXPECT_THAT(MediumWait().Until([&] { return ep1_ch1()->gathering_state(); }, + Eq(IceGatheringState::kIceGatheringComplete)), IsRtcOk()); // Add two remote candidates; a host candidate (with higher priority) // and TURN candidate. @@ -2237,15 +2211,16 @@ TEST_F(P2PTransportChannelTest, TurnToTurnPresumedWritable) { CreateUdpCandidate(IceCandidateType::kRelay, "2.2.2.2", 2, 0)); // Expect that the TURN-TURN candidate pair will be prioritized since it's // "probably writable". - EXPECT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kShortTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); EXPECT_TRUE(LocalCandidate(ep1_ch1())->is_relay()); EXPECT_TRUE(RemoteCandidate(ep1_ch1())->is_relay()); // Also expect that the channel instantly indicates that it's writable since // it has a TURN-TURN pair. EXPECT_TRUE(ep1_ch1()->writable()); - EXPECT_TRUE(GetEndpoint(0)->ready_to_send_); + EXPECT_TRUE(GetEndpoint(0)->ready_to_send()); // Also make sure we can immediately send packets. const char* data = "test"; int len = static_cast(strlen(data)); @@ -2256,27 +2231,24 @@ TEST_F(P2PTransportChannelTest, TurnToTurnPresumedWritable) { // Test that a TURN/peer reflexive candidate pair is also presumed writable. TEST_F(P2PTransportChannelTest, TurnToPrflxPresumedWritable) { - ScopedFakeClock fake_clock; - const Environment env = CreateEnvironment(); - // We need to add artificial network delay to verify that the connection // is presumed writable before it's actually writable. Without this delay // it would become writable instantly. virtual_socket_server()->set_delay_mean(50); virtual_socket_server()->UpdateDelayDistribution(); - ConfigureEndpoints(env, NAT_SYMMETRIC, NAT_SYMMETRIC, - kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); + ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags, + kDefaultPortAllocatorFlags); // We want the remote TURN candidate to show up as prflx. To do this we need // to configure the server to accept packets from an address we haven't // explicitly installed permission for. test_turn_server()->set_enable_permission_checks(false); IceConfig config; config.presume_writable_when_fully_relayed = true; - GetEndpoint(0)->cd1_.ch_ = CreateChannel( - env, 0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]); - GetEndpoint(1)->cd1_.ch_ = CreateChannel( - env, 1, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[1], kIceParams[0]); + GetEndpoint(0)->cd1().set_ch(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[0], kIceParams[1])); + GetEndpoint(1)->cd1().set_ch(CreateChannel(1, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[1], kIceParams[0])); ep1_ch1()->SetIceConfig(config); ep2_ch1()->SetIceConfig(config); // Don't signal candidates from channel 2, so that channel 1 sees the TURN @@ -2286,9 +2258,8 @@ TEST_F(P2PTransportChannelTest, TurnToPrflxPresumedWritable) { ep2_ch1()->MaybeStartGathering(); // Wait for the TURN<->prflx connection. - EXPECT_TRUE( - WaitUntil([&] { return ep1_ch1()->receiving() && ep1_ch1()->writable(); }, - {.timeout = kShortTimeout, .clock = &fake_clock})); + EXPECT_TRUE(ShortWait().Until( + [&] { return ep1_ch1()->receiving() && ep1_ch1()->writable(); })); ASSERT_NE(nullptr, ep1_ch1()->selected_connection()); EXPECT_TRUE(LocalCandidate(ep1_ch1())->is_relay()); EXPECT_TRUE(RemoteCandidate(ep1_ch1())->is_prflx()); @@ -2297,9 +2268,8 @@ TEST_F(P2PTransportChannelTest, TurnToPrflxPresumedWritable) { EXPECT_FALSE(ep1_ch1()->selected_connection()->writable()); // Now wait for it to actually become writable. - EXPECT_TRUE( - WaitUntil([&] { return ep1_ch1()->selected_connection()->writable(); }, - {.timeout = kShortTimeout, .clock = &fake_clock})); + EXPECT_TRUE(ShortWait().Until( + [&] { return ep1_ch1()->selected_connection()->writable(); })); // Explitly destroy channels, before fake clock is destroyed. DestroyChannels(); @@ -2308,30 +2278,26 @@ TEST_F(P2PTransportChannelTest, TurnToPrflxPresumedWritable) { // Test that a presumed-writable TURN<->TURN connection is preferred above an // unreliable connection (one that has failed to be pinged for some time). TEST_F(P2PTransportChannelTest, PresumedWritablePreferredOverUnreliable) { - ScopedFakeClock fake_clock; - const Environment env = CreateEnvironment(); - - ConfigureEndpoints(env, NAT_SYMMETRIC, NAT_SYMMETRIC, - kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); + ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags, + kDefaultPortAllocatorFlags); IceConfig config; config.presume_writable_when_fully_relayed = true; - GetEndpoint(0)->cd1_.ch_ = CreateChannel( - env, 0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]); - GetEndpoint(1)->cd1_.ch_ = CreateChannel( - env, 1, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[1], kIceParams[0]); + GetEndpoint(0)->cd1().set_ch(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[0], kIceParams[1])); + GetEndpoint(1)->cd1().set_ch(CreateChannel(1, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[1], kIceParams[0])); ep1_ch1()->SetIceConfig(config); ep2_ch1()->SetIceConfig(config); ep1_ch1()->MaybeStartGathering(); ep2_ch1()->MaybeStartGathering(); // Wait for initial connection as usual. - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kShortTimeout, .clock = &fake_clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); const Connection* old_selected_connection = ep1_ch1()->selected_connection(); // Destroy the second channel and wait for the current connection on the // first channel to become "unreliable", making it no longer writable. - GetEndpoint(1)->cd1_.ch_.reset(); - EXPECT_TRUE(WaitUntil([&] { return !ep1_ch1()->writable(); }, - {.timeout = kDefaultTimeout, .clock = &fake_clock})); + GetEndpoint(1)->cd1().reset_ch(); + EXPECT_TRUE(DefaultWait().Until([&] { return !ep1_ch1()->writable(); })); EXPECT_NE(nullptr, ep1_ch1()->selected_connection()); // Add a remote TURN candidate. The first channel should still have a TURN // port available to make a TURN<->TURN pair that's presumed writable. @@ -2340,7 +2306,7 @@ TEST_F(P2PTransportChannelTest, PresumedWritablePreferredOverUnreliable) { EXPECT_TRUE(LocalCandidate(ep1_ch1())->is_relay()); EXPECT_TRUE(RemoteCandidate(ep1_ch1())->is_relay()); EXPECT_TRUE(ep1_ch1()->writable()); - EXPECT_TRUE(GetEndpoint(0)->ready_to_send_); + EXPECT_TRUE(GetEndpoint(0)->ready_to_send()); EXPECT_NE(old_selected_connection, ep1_ch1()->selected_connection()); // Explitly destroy channels, before fake clock is destroyed. DestroyChannels(); @@ -2349,27 +2315,26 @@ TEST_F(P2PTransportChannelTest, PresumedWritablePreferredOverUnreliable) { // Ensure that "SignalReadyToSend" is fired as expected with a "presumed // writable" connection. Previously this did not work. TEST_F(P2PTransportChannelTest, SignalReadyToSendWithPresumedWritable) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // Only test one endpoint, so we can ensure the connection doesn't receive a // binding response and advance beyond being "presumed" writable. - GetEndpoint(0)->cd1_.ch_ = CreateChannel( - env, 0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]); + GetEndpoint(0)->cd1().set_ch(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[0], kIceParams[1])); IceConfig config; config.presume_writable_when_fully_relayed = true; ep1_ch1()->SetIceConfig(config); ep1_ch1()->MaybeStartGathering(); - EXPECT_THAT(WaitUntil([&] { return ep1_ch1()->gathering_state(); }, - Eq(IceGatheringState::kIceGatheringComplete), - {.timeout = kDefaultTimeout}), + EXPECT_THAT(DefaultWait().Until([&] { return ep1_ch1()->gathering_state(); }, + Eq(IceGatheringState::kIceGatheringComplete)), IsRtcOk()); ep1_ch1()->AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kRelay, "1.1.1.1", 1, 0)); // Sanity checking the type of the connection. - EXPECT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kShortTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); EXPECT_TRUE(LocalCandidate(ep1_ch1())->is_relay()); EXPECT_TRUE(RemoteCandidate(ep1_ch1())->is_relay()); @@ -2381,9 +2346,9 @@ TEST_F(P2PTransportChannelTest, SignalReadyToSendWithPresumedWritable) { // Reset `ready_to_send_` flag, which is set to true if the event fires as it // should. - GetEndpoint(0)->ready_to_send_ = false; + GetEndpoint(0)->set_ready_to_send(false); virtual_socket_server()->SetSendingBlocked(false); - EXPECT_TRUE(GetEndpoint(0)->ready_to_send_); + EXPECT_TRUE(GetEndpoint(0)->ready_to_send()); EXPECT_EQ(len, SendData(ep1_ch1(), data, len)); DestroyChannels(); } @@ -2393,10 +2358,8 @@ TEST_F(P2PTransportChannelTest, SignalReadyToSendWithPresumedWritable) { // crbug.com/webrtc/9034. TEST_F(P2PTransportChannelTest, TurnToPrflxSelectedAfterResolvingIceControllingRoleConflict) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); // Gather only relay candidates. - ConfigureEndpoints(env, NAT_SYMMETRIC, NAT_SYMMETRIC, + ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags | PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_TCP, kDefaultPortAllocatorFlags | PORTALLOCATOR_DISABLE_UDP | @@ -2409,19 +2372,18 @@ TEST_F(P2PTransportChannelTest, // to configure the server to accept packets from an address we haven't // explicitly installed permission for. test_turn_server()->set_enable_permission_checks(false); - GetEndpoint(0)->cd1_.ch_ = CreateChannel( - env, 0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]); - GetEndpoint(1)->cd1_.ch_ = CreateChannel( - env, 1, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[1], kIceParams[0]); + GetEndpoint(0)->cd1().set_ch(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[0], kIceParams[1])); + GetEndpoint(1)->cd1().set_ch(CreateChannel(1, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[1], kIceParams[0])); // Don't signal candidates from channel 2, so that channel 1 sees the TURN // candidate as peer reflexive. PauseCandidates(1); ep1_ch1()->MaybeStartGathering(); ep2_ch1()->MaybeStartGathering(); - EXPECT_TRUE( - WaitUntil([&] { return ep1_ch1()->receiving() && ep1_ch1()->writable(); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(ShortWait().Until( + [&] { return ep1_ch1()->receiving() && ep1_ch1()->writable(); })); ASSERT_NE(nullptr, ep1_ch1()->selected_connection()); @@ -2435,10 +2397,11 @@ TEST_F(P2PTransportChannelTest, // acknowledgement in the connectivity check from the remote peer. TEST_F(P2PTransportChannelTest, CanConnectWithPiggybackCheckAcknowledgementWhenCheckResponseBlocked) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-PiggybackIceCheckAcknowledgement/Enabled/")); - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-PiggybackIceCheckAcknowledgement/Enabled/"), + .time = &time_controller_}); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); IceConfig ep1_config; IceConfig ep2_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); @@ -2450,20 +2413,19 @@ TEST_F(P2PTransportChannelTest, ep2_config.ice_unwritable_min_checks = 30; ep2_config.ice_inactive_timeout = TimeDelta::Seconds(60); - CreateChannels(env, ep1_config, ep2_config); + CreateChannels(ep1_config, ep2_config); // Wait until both sides become writable for the first time. - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE( + MediumWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); // Block the ingress traffic to ep1 so that there is no check response from // ep2. ASSERT_NE(nullptr, LocalCandidate(ep1_ch1())); fw()->AddRule(false, FP_ANY, FD_IN, LocalCandidate(ep1_ch1())->address()); // Wait until ep1 becomes unwritable. At the same time ep2 should be still // fine so that it will keep sending pings. - EXPECT_TRUE( - WaitUntil([&] { return ep1_ch1() != nullptr && !ep1_ch1()->writable(); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return ep1_ch1() != nullptr && !ep1_ch1()->writable(); })); EXPECT_TRUE(ep2_ch1() != nullptr && ep2_ch1()->writable()); // Now let the pings from ep2 to flow but block any pings from ep1, so that // ep1 can only become writable again after receiving an incoming ping from @@ -2472,9 +2434,8 @@ TEST_F(P2PTransportChannelTest, // in the current design. fw()->ClearRules(); fw()->AddRule(false, FP_ANY, FD_OUT, LocalCandidate(ep1_ch1())->address()); - EXPECT_TRUE( - WaitUntil([&] { return ep1_ch1() != nullptr && ep1_ch1()->writable(); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return ep1_ch1() != nullptr && ep1_ch1()->writable(); })); DestroyChannels(); } @@ -2483,22 +2444,18 @@ TEST_F(P2PTransportChannelTest, // address of the outermost NAT. class P2PTransportChannelSameNatTest : public P2PTransportChannelTestBase { protected: - void ConfigureEndpoints(const Environment& env, - Config nat_type, - Config config1, - Config config2) { + void ConfigureEndpoints(Config nat_type, Config config1, Config config2) { RTC_CHECK_GE(nat_type, NAT_FULL_CONE); RTC_CHECK_LE(nat_type, NAT_SYMMETRIC); - CreatePortAllocators(env); + CreatePortAllocators(); NATSocketServer::Translator* outer_nat = - nat()->AddTranslator(env, kPublicAddrs[0], kNatAddrs[0], + nat()->AddTranslator(env_, kPublicAddrs[0], kNatAddrs[0], static_cast(nat_type - NAT_FULL_CONE)); - ConfigureEndpoint(env, outer_nat, 0, config1); - ConfigureEndpoint(env, outer_nat, 1, config2); + ConfigureEndpoint(outer_nat, 0, config1); + ConfigureEndpoint(outer_nat, 1, config2); set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); } - void ConfigureEndpoint(const Environment& env, - NATSocketServer::Translator* nat, + void ConfigureEndpoint(NATSocketServer::Translator* nat, int endpoint, Config config) { RTC_CHECK(config <= NAT_SYMMETRIC); @@ -2507,7 +2464,7 @@ class P2PTransportChannelSameNatTest : public P2PTransportChannelTestBase { nat->AddClient(kPrivateAddrs[endpoint]); } else { AddAddress(endpoint, kCascadedPrivateAddrs[endpoint]); - nat->AddTranslator(env, kPrivateAddrs[endpoint], + nat->AddTranslator(env_, kPrivateAddrs[endpoint], kCascadedNatAddrs[endpoint], static_cast(config - NAT_FULL_CONE)) ->AddClient(kCascadedPrivateAddrs[endpoint]); @@ -2516,11 +2473,10 @@ class P2PTransportChannelSameNatTest : public P2PTransportChannelTestBase { }; TEST_F(P2PTransportChannelSameNatTest, TestConesBehindSameCone) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, NAT_FULL_CONE, NAT_FULL_CONE, NAT_FULL_CONE); - Test(env, P2PTransportChannelTestBase::Result(IceCandidateType::kPrflx, "udp", - IceCandidateType::kSrflx, "udp", - TimeDelta::Seconds(1))); + ConfigureEndpoints(NAT_FULL_CONE, NAT_FULL_CONE, NAT_FULL_CONE); + Test(P2PTransportChannelTestBase::Result(IceCandidateType::kPrflx, "udp", + IceCandidateType::kSrflx, "udp", + TimeDelta::Seconds(1))); } // Test what happens when we have multiple available pathways. @@ -2561,7 +2517,7 @@ class P2PTransportChannelMultihomedTest : public P2PTransportChannelTest { } Connection* GetBestConnection(P2PTransportChannel* channel) { - ArrayView connections = channel->connections(); + std::span connections = channel->connections(); auto it = absl::c_find(connections, channel->selected_connection()); if (it == connections.end()) { return nullptr; @@ -2570,7 +2526,7 @@ class P2PTransportChannelMultihomedTest : public P2PTransportChannelTest { } Connection* GetBackupConnection(P2PTransportChannel* channel) { - ArrayView connections = channel->connections(); + std::span connections = channel->connections(); auto it = absl::c_find_if_not(connections, [channel](Connection* conn) { return conn == channel->selected_connection(); }); @@ -2583,7 +2539,7 @@ class P2PTransportChannelMultihomedTest : public P2PTransportChannelTest { void DestroyAllButBestConnection(P2PTransportChannel* channel) { const Connection* selected_connection = channel->selected_connection(); // Copy the list of connections since the original will be modified. - ArrayView view = channel->connections(); + std::span view = channel->connections(); std::vector connections(view.begin(), view.end()); for (Connection* conn : connections) { if (conn != selected_connection) @@ -2594,21 +2550,18 @@ class P2PTransportChannelMultihomedTest : public P2PTransportChannelTest { // Test that we can establish connectivity when both peers are multihomed. TEST_F(P2PTransportChannelMultihomedTest, TestBasic) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); AddAddress(0, kAlternateAddrs[0]); AddAddress(1, kPublicAddrs[1]); AddAddress(1, kAlternateAddrs[1]); - Test(env, kLocalUdpToLocalUdp); + Test(kLocalUdpToLocalUdp); } // Test that we can quickly switch links if an interface goes down. // The controlled side has two interfaces and one will die. TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); // Simulate failing over from Wi-Fi to cell interface. AddAddress(1, kPublicAddrs[1], "eth0", ADAPTER_TYPE_WIFI); @@ -2621,14 +2574,12 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { // Make the receiving timeout shorter for testing. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE); // Create channels and let them go writable, as usual. - CreateChannels(env, config, config); + CreateChannels(config, config); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - kPublicAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kPublicAddrs[1]); + })); // Blackhole any traffic to or from the public addrs. RTC_LOG(LS_INFO) << "Failing over..."; @@ -2636,17 +2587,15 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { // The selected connections may switch, so keep references to them. const Connection* selected_connection1 = ep1_ch1()->selected_connection(); // We should detect loss of receiving within 1 second or so. - EXPECT_TRUE(WaitUntil([&] { return !selected_connection1->receiving(); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE( + MediumWait().Until([&] { return !selected_connection1->receiving(); })); // We should switch over to use the alternate addr on both sides // when we are not receiving. - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->receiving() && - ep2_ch1()->selected_connection()->receiving(); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->selected_connection()->receiving() && + ep2_ch1()->selected_connection()->receiving(); + })); EXPECT_TRUE(LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0])); EXPECT_TRUE( RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[1])); @@ -2659,9 +2608,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControlledSide) { // Test that we can quickly switch links if an interface goes down. // The controlling side has two interfaces and one will die. TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); // Simulate failing over from Wi-Fi to cell interface. AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_WIFI); AddAddress(0, kAlternateAddrs[0], "wlan0", ADAPTER_TYPE_CELLULAR); @@ -2674,13 +2621,11 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { // Make the receiving timeout shorter for testing. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE); // Create channels and let them go writable, as usual. - CreateChannels(env, config, config); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - kPublicAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + CreateChannels(config, config); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kPublicAddrs[1]); + })); // Blackhole any traffic to or from the public addrs. RTC_LOG(LS_INFO) << "Failing over..."; @@ -2689,12 +2634,10 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { // We should detect loss of receiving within 1 second or so. // We should switch over to use the alternate addr on both sides // when we are not receiving. - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected( - ep1_ch1(), ep2_ch1(), kAlternateAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), + kAlternateAddrs[0], kPublicAddrs[1]); + })); DestroyChannels(); } @@ -2702,9 +2645,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) { // Tests that we can quickly switch links if an interface goes down when // there are many connections. TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); test_turn_server()->AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); RelayServerConfig turn_server; turn_server.credentials = kRelayCredentials; @@ -2742,13 +2683,11 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) { // Make the receiving timeout shorter for testing. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); // Create channels and let them go writable, as usual. - CreateChannels(env, config, config, true /* ice_renomination */); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), wifiIpv6[0], - wifiIpv6[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + CreateChannels(config, config, true /* ice_renomination */); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), wifiIpv6[0], + wifiIpv6[1]); + })); // Blackhole any traffic to or from the wifi on endpoint 1. RTC_LOG(LS_INFO) << "Failing over..."; @@ -2758,12 +2697,10 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) { // The selected connections may switch, so keep references to them. const Connection* selected_connection1 = ep1_ch1()->selected_connection(); const Connection* selected_connection2 = ep2_ch1()->selected_connection(); - EXPECT_TRUE(WaitUntil( - [&] { - return !selected_connection1->receiving() && - !selected_connection2->receiving(); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return !selected_connection1->receiving() && + !selected_connection2->receiving(); + })); // Per-network best connections will be pinged at relatively higher rate when // the selected connection becomes not receiving. @@ -2773,9 +2710,9 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) { Timestamp last_ping_sent1 = per_network_best_connection1->LastPingSent(); int num_pings_sent1 = per_network_best_connection1->num_pings_sent(); EXPECT_THAT( - WaitUntil([&] { return per_network_best_connection1->num_pings_sent(); }, - Gt(num_pings_sent1), - {.timeout = kMediumTimeout, .clock = &clock}), + MediumWait().Until( + [&] { return per_network_best_connection1->num_pings_sent(); }, + Gt(num_pings_sent1)), IsRtcOk()); ASSERT_GT(per_network_best_connection1->num_pings_sent() - num_pings_sent1, 0); @@ -2788,12 +2725,10 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) { // It should switch over to use the cellular IPv6 addr on endpoint 1 before // it timed out on writing. - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - cellularIpv6[0], wifiIpv6[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), cellularIpv6[0], + wifiIpv6[1]); + })); DestroyChannels(); } @@ -2802,9 +2737,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverWithManyConnections) { // the nomination of the selected connection on the controlled side will // increase. TEST_F(P2PTransportChannelMultihomedTest, TestIceRenomination) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); // Simulate failing over from Wi-Fi to cell interface. AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_WIFI); AddAddress(0, kAlternateAddrs[0], "wlan0", ADAPTER_TYPE_CELLULAR); @@ -2819,15 +2752,13 @@ TEST_F(P2PTransportChannelMultihomedTest, TestIceRenomination) { // Make the receiving timeout shorter for testing. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE); // Create channels with ICE renomination and let them go writable as usual. - CreateChannels(env, config, config, true); - ASSERT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); - EXPECT_TRUE(WaitUntil( - [&] { - return ep2_ch1()->selected_connection()->remote_nomination() > 0 && - ep1_ch1()->selected_connection()->acked_nomination() > 0; - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + CreateChannels(config, config, true); + ASSERT_TRUE( + MediumWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + EXPECT_TRUE(MediumWait().Until([&] { + return ep2_ch1()->selected_connection()->remote_nomination() > 0 && + ep1_ch1()->selected_connection()->acked_nomination() > 0; + })); const Connection* selected_connection1 = ep1_ch1()->selected_connection(); Connection* selected_connection2 = const_cast(ep2_ch1()->selected_connection()); @@ -2835,7 +2766,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestIceRenomination) { // `selected_connection2` should not be nominated any more since the previous // nomination has been acknowledged. ConnectSignalNominated(selected_connection2); - SIMULATED_WAIT(nominated(), kMediumTimeout.ms(), clock); + EXPECT_FALSE(DefaultWait().Until([&] { return nominated(); })); EXPECT_FALSE(nominated()); // Blackhole any traffic to or from the public addrs. @@ -2843,17 +2774,16 @@ TEST_F(P2PTransportChannelMultihomedTest, TestIceRenomination) { fw()->AddRule(false, FP_ANY, FD_ANY, kPublicAddrs[0]); // The selected connection on the controlling side should switch. - EXPECT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(selected_connection1), - {.timeout = kMediumTimeout, .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(selected_connection1)), + IsRtcOk()); // The connection on the controlled side should be nominated again // and have an increased nomination. EXPECT_THAT( - WaitUntil( + MediumWait().Until( [&] { return ep2_ch1()->selected_connection()->remote_nomination(); }, - Gt(remote_nomination2), - {.timeout = kDefaultTimeout, .clock = &clock}), + Gt(remote_nomination2)), IsRtcOk()); DestroyChannels(); @@ -2866,9 +2796,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestIceRenomination) { // TestFailoverControlledSide and TestFailoverControllingSide. TEST_F(P2PTransportChannelMultihomedTest, TestConnectionSwitchDampeningControlledSide) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); // Simulate failing over from Wi-Fi to cell interface. AddAddress(1, kPublicAddrs[1], "eth0", ADAPTER_TYPE_WIFI); @@ -2879,14 +2807,12 @@ TEST_F(P2PTransportChannelMultihomedTest, SetAllocatorFlags(1, kOnlyLocalPorts); // Create channels and let them go writable, as usual. - CreateChannels(env); + CreateChannels(); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - kPublicAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kPublicAddrs[1]); + })); // Make the receiving timeout shorter for testing. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE); @@ -2901,20 +2827,18 @@ TEST_F(P2PTransportChannelMultihomedTest, // The selected connections may switch, so keep references to them. const Connection* selected_connection1 = ep1_ch1()->selected_connection(); // We should detect loss of receiving within 1 second or so. - EXPECT_TRUE(WaitUntil([&] { return !selected_connection1->receiving(); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE( + MediumWait().Until([&] { return !selected_connection1->receiving(); })); // After a short while, the link recovers itself. - SIMULATED_WAIT(false, 10, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(10)); fw()->ClearRules(); // We should remain on the public address on both sides and no connection // switches should have happened. - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->receiving() && - ep2_ch1()->selected_connection()->receiving(); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->selected_connection()->receiving() && + ep2_ch1()->selected_connection()->receiving(); + })); EXPECT_TRUE(RemoteCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[1])); EXPECT_TRUE(LocalCandidate(ep2_ch1())->address().EqualIPs(kPublicAddrs[1])); EXPECT_EQ(0, reset_selected_candidate_pair_switches()); @@ -2926,9 +2850,7 @@ TEST_F(P2PTransportChannelMultihomedTest, // the selected connection will not switch. TEST_F(P2PTransportChannelMultihomedTest, TestConnectionSwitchDampeningControllingSide) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); // Simulate failing over from Wi-Fi to cell interface. AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_WIFI); AddAddress(0, kAlternateAddrs[0], "wlan0", ADAPTER_TYPE_CELLULAR); @@ -2939,13 +2861,11 @@ TEST_F(P2PTransportChannelMultihomedTest, SetAllocatorFlags(1, kOnlyLocalPorts); // Create channels and let them go writable, as usual. - CreateChannels(env); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - kPublicAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + CreateChannels(); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kPublicAddrs[1]); + })); // Make the receiving timeout shorter for testing. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE); @@ -2959,20 +2879,18 @@ TEST_F(P2PTransportChannelMultihomedTest, // The selected connections may switch, so keep references to them. const Connection* selected_connection1 = ep1_ch1()->selected_connection(); // We should detect loss of receiving within 1 second or so. - EXPECT_TRUE(WaitUntil([&] { return !selected_connection1->receiving(); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE( + MediumWait().Until([&] { return !selected_connection1->receiving(); })); // The link recovers after a short while. - SIMULATED_WAIT(false, 10, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(10)); fw()->ClearRules(); // We should not switch to the alternate addr on both sides because of the // dampening. - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - kPublicAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kPublicAddrs[1]); + })); EXPECT_EQ(0, reset_selected_candidate_pair_switches()); DestroyChannels(); } @@ -2980,9 +2898,7 @@ TEST_F(P2PTransportChannelMultihomedTest, // Tests that if the remote side's network failed, it won't cause the local // side to switch connections and networks. TEST_F(P2PTransportChannelMultihomedTest, TestRemoteFailover) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); // The interface names are chosen so that `cellular` would have higher // candidate priority and higher cost. auto& wifi = kPublicAddrs; @@ -2995,7 +2911,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestRemoteFailover) { SetAllocatorFlags(0, kOnlyLocalPorts); SetAllocatorFlags(1, kOnlyLocalPorts); // Create channels and let them go writable, as usual. - CreateChannels(env); + CreateChannels(); // Make the receiving timeout shorter for testing. // Set the backup connection ping interval to 25s. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE, @@ -3005,36 +2921,33 @@ TEST_F(P2PTransportChannelMultihomedTest, TestRemoteFailover) { ep1_ch1()->SetIceConfig(config); ep2_ch1()->SetIceConfig(config); // Need to wait to make sure the connections on both networks are writable. - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), wifi[0], - wifi[1]); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), wifi[0], + wifi[1]); + })); Connection* backup_conn = GetConnectionWithLocalAddress(ep1_ch1(), cellular[0]); ASSERT_NE(nullptr, backup_conn); // After a short while, the backup connection will be writable but not // receiving because backup connection is pinged at a slower rate. - EXPECT_TRUE(WaitUntil( - [&] { return backup_conn->writable() && !backup_conn->receiving(); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return backup_conn->writable() && !backup_conn->receiving(); })); reset_selected_candidate_pair_switches(); // Blackhole any traffic to or from the remote WiFi networks. RTC_LOG(LS_INFO) << "Failing over..."; fw()->AddRule(false, FP_ANY, FD_ANY, wifi[1]); int num_switches = 0; - SIMULATED_WAIT((num_switches = reset_selected_candidate_pair_switches()) > 0, - 20000, clock); + (void)DefaultWait().Until([&] { + return (num_switches = reset_selected_candidate_pair_switches()) > 0; + }); EXPECT_EQ(0, num_switches); DestroyChannels(); } // Tests that a Wifi-Wifi connection has the highest precedence. TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiToWifiConnection) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); // The interface names are chosen so that `cellular` would have higher // candidate priority if it is not for the network type. auto& wifi = kAlternateAddrs; @@ -3049,12 +2962,12 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiToWifiConnection) { SetAllocatorFlags(1, kOnlyLocalPorts); // Create channels and let them go writable, as usual. - CreateChannels(env); + CreateChannels(); - EXPECT_TRUE( - WaitUntil([&]() { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + EXPECT_TRUE(DefaultWait().Until( + [&]() { return CheckConnected(ep1_ch1(), ep2_ch1()); })); // Need to wait to make sure the connections on both networks are writable. - EXPECT_TRUE(WaitUntil([&] { + EXPECT_TRUE(DefaultWait().Until([&] { return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), wifi[0], wifi[1]); })); @@ -3064,8 +2977,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiToWifiConnection) { // Tests that a Wifi-Cellular connection has higher precedence than // a Cellular-Cellular connection. TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiOverCellularNetwork) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); // The interface names are chosen so that `cellular` would have higher // candidate priority if it is not for the network type. auto& wifi = kAlternateAddrs; @@ -3079,9 +2991,9 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiOverCellularNetwork) { SetAllocatorFlags(1, kOnlyLocalPorts); // Create channels and let them go writable, as usual. - CreateChannels(env); + CreateChannels(); - EXPECT_TRUE(WaitUntil([&]() { + EXPECT_TRUE(DefaultWait().Until([&]() { return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), cellular[0], wifi[1]); })); @@ -3091,8 +3003,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPreferWifiOverCellularNetwork) { // Test that the backup connection is pinged at a rate no faster than // what was configured. TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); // Adding alternate address will make sure `kPublicAddrs` has the higher // priority than others. This is due to FakeNetwork::AddInterface method. @@ -3104,25 +3015,25 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) { SetAllocatorFlags(1, kOnlyLocalPorts); // Create channels and let them go writable, as usual. - CreateChannels(env); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + CreateChannels(); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); TimeDelta backup_ping_interval = TimeDelta::Seconds(2); ep2_ch1()->SetIceConfig(CreateIceConfig(TimeDelta::Seconds(2), GATHER_ONCE, backup_ping_interval)); // After the state becomes COMPLETED, the backup connection will be pinged // once every `backup_ping_interval` milliseconds. - ASSERT_THAT(WaitUntil([&] { return ep2_ch1()->GetState(); }, - Eq(IceTransportStateInternal::STATE_COMPLETED)), - IsRtcOk()); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep2_ch1()->GetState() == IceTransportStateInternal::STATE_COMPLETED; + })); auto connections = ep2_ch1()->connections(); ASSERT_EQ(2U, connections.size()); Connection* backup_conn = GetBackupConnection(ep2_ch1()); - EXPECT_TRUE(WaitUntil([&] { return backup_conn->writable(); }, - {.timeout = kMediumTimeout})); + EXPECT_TRUE(DefaultWait().Until([&] { return backup_conn->writable(); })); Timestamp last_ping_response = backup_conn->LastPingResponseReceived(); - EXPECT_THAT(WaitUntil([&] { return backup_conn->LastPingResponseReceived(); }, - Gt(last_ping_response), {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_TRUE(MediumWait().Until([&] { + return backup_conn->LastPingResponseReceived() > last_ping_response; + })); TimeDelta time_elapsed = backup_conn->LastPingResponseReceived() - last_ping_response; RTC_LOG(LS_INFO) << "Time elapsed: " << time_elapsed; @@ -3134,8 +3045,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestPingBackupConnectionRate) { // Test that the connection is pinged at a rate no faster than // what was configured when stable and writable. TEST_F(P2PTransportChannelMultihomedTest, TestStableWritableRate) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); // Adding alternate address will make sure `kPublicAddrs` has the higher // priority than others. This is due to FakeNetwork::AddInterface method. @@ -3147,8 +3057,9 @@ TEST_F(P2PTransportChannelMultihomedTest, TestStableWritableRate) { SetAllocatorFlags(1, kOnlyLocalPorts); // Create channels and let them go writable, as usual. - CreateChannels(env); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); + CreateChannels(); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); // Set a value larger than the default value of 2500 ms TimeDelta ping_interval = TimeDelta::Millis(3'456); IceConfig config = CreateIceConfig(2 * ping_interval, GATHER_ONCE); @@ -3156,22 +3067,20 @@ TEST_F(P2PTransportChannelMultihomedTest, TestStableWritableRate) { ep2_ch1()->SetIceConfig(config); // After the state becomes COMPLETED and is stable and writable, the // connection will be pinged once every `ping_interval`. - ASSERT_THAT(WaitUntil([&] { return ep2_ch1()->GetState(); }, - Eq(IceTransportStateInternal::STATE_COMPLETED)), - IsRtcOk()); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep2_ch1()->GetState() == IceTransportStateInternal::STATE_COMPLETED; + })); auto connections = ep2_ch1()->connections(); ASSERT_EQ(2U, connections.size()); Connection* conn = GetBestConnection(ep2_ch1()); - EXPECT_TRUE( - WaitUntil([&] { return conn->writable(); }, {.timeout = kMediumTimeout})); + EXPECT_TRUE(DefaultWait().Until([&] { return conn->writable(); })); Timestamp last_ping_response = Timestamp::Zero(); // Burn through some pings so the connection is stable. for (int i = 0; i < 5; i++) { last_ping_response = conn->LastPingResponseReceived(); - EXPECT_THAT(WaitUntil([&] { return conn->LastPingResponseReceived(); }, - Gt(last_ping_response), {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_TRUE(DefaultWait().Until( + [&] { return conn->LastPingResponseReceived() > last_ping_response; })); } EXPECT_TRUE(conn->stable(last_ping_response)) << "Connection not stable"; TimeDelta time_elapsed = @@ -3183,23 +3092,20 @@ TEST_F(P2PTransportChannelMultihomedTest, TestStableWritableRate) { } TEST_F(P2PTransportChannelMultihomedTest, TestGetState) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kAlternateAddrs[0]); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kPublicAddrs[1]); // Create channels and let them go writable, as usual. - CreateChannels(env); + CreateChannels(); // Both transport channels will reach STATE_COMPLETED quickly. - EXPECT_THAT(WaitUntil([&] { return ep1_ch1()->GetState(); }, - Eq(IceTransportStateInternal::STATE_COMPLETED), - {.timeout = kShortTimeout, .clock = &clock}), - IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return ep2_ch1()->GetState(); }, - Eq(IceTransportStateInternal::STATE_COMPLETED), - {.timeout = kShortTimeout, .clock = &clock}), + EXPECT_THAT( + DefaultWait().Until([&] { return ep1_ch1()->GetState(); }, + Eq(IceTransportStateInternal::STATE_COMPLETED)), + IsRtcOk()); + EXPECT_THAT(ShortWait().Until([&] { return ep2_ch1()->GetState(); }, + Eq(IceTransportStateInternal::STATE_COMPLETED)), IsRtcOk()); DestroyChannels(); } @@ -3209,21 +3115,19 @@ TEST_F(P2PTransportChannelMultihomedTest, TestGetState) { // will be removed from the port list of the channel, and the respective // remote candidates on the other participant will be removed eventually. TEST_F(P2PTransportChannelMultihomedTest, TestNetworkBecomesInactive) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kPublicAddrs[1]); // Create channels and let them go writable, as usual. IceConfig ep1_config = CreateIceConfig(TimeDelta::Seconds(2), GATHER_CONTINUALLY); IceConfig ep2_config = CreateIceConfig(TimeDelta::Seconds(2), GATHER_ONCE); - CreateChannels(env, ep1_config, ep2_config); + CreateChannels(ep1_config, ep2_config); SetAllocatorFlags(0, kOnlyLocalPorts); SetAllocatorFlags(1, kOnlyLocalPorts); - ASSERT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE( + ShortWait().Until([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); // More than one port has been created. EXPECT_LE(1U, ep1_ch1()->ports().size()); // Endpoint 1 enabled continual gathering; the port will be removed @@ -3231,8 +3135,8 @@ TEST_F(P2PTransportChannelMultihomedTest, TestNetworkBecomesInactive) { RemoveAddress(0, kPublicAddrs[0]); EXPECT_TRUE(ep1_ch1()->ports().empty()); // The remote candidates will be removed eventually. - EXPECT_TRUE(WaitUntil([&] { return ep2_ch1()->remote_candidates().empty(); }, - {.clock = &clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return ep2_ch1()->remote_candidates().empty(); })); size_t num_ports = ep2_ch1()->ports().size(); EXPECT_LE(1U, num_ports); @@ -3242,10 +3146,11 @@ TEST_F(P2PTransportChannelMultihomedTest, TestNetworkBecomesInactive) { // other participant will not be removed. RemoveAddress(1, kPublicAddrs[1]); - EXPECT_THAT(WaitUntil([&] { return ep2_ch1()->ports().size(); }, Eq(0U), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); - SIMULATED_WAIT(ep1_ch1()->remote_candidates().empty(), 500, clock); + EXPECT_THAT( + DefaultWait().Until([&] { return ep2_ch1()->ports().size(); }, Eq(0U)), + IsRtcOk()); + (void)DefaultWait().Until( + [&] { return ep1_ch1()->remote_candidates().empty(); }); EXPECT_EQ(num_remote_candidates, ep1_ch1()->remote_candidates().size()); DestroyChannels(); @@ -3255,8 +3160,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestNetworkBecomesInactive) { // interface is added. TEST_F(P2PTransportChannelMultihomedTest, TestContinualGatheringOnNewInterface) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); auto& wifi = kAlternateAddrs; auto& cellular = kPublicAddrs; AddAddress(0, wifi[0], "test_wifi0", ADAPTER_TYPE_WIFI); @@ -3264,58 +3168,50 @@ TEST_F(P2PTransportChannelMultihomedTest, // Set continual gathering policy. IceConfig continual_gathering_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); - CreateChannels(env, continual_gathering_config, continual_gathering_config); + CreateChannels(continual_gathering_config, continual_gathering_config); SetAllocatorFlags(0, kOnlyLocalPorts); SetAllocatorFlags(1, kOnlyLocalPorts); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); // Add a new wifi interface on end point 2. We should expect a new connection // to be created and the new one will be the best connection. AddAddress(1, wifi[1], "test_wifi1", ADAPTER_TYPE_WIFI); const Connection* conn; - EXPECT_TRUE(WaitUntil( - [&] { - return (conn = ep1_ch1()->selected_connection()) != nullptr && - HasRemoteAddress(conn, wifi[1]); - }, - {.timeout = kDefaultTimeout})); - EXPECT_TRUE(WaitUntil( - [&] { - return (conn = ep2_ch1()->selected_connection()) != nullptr && - HasLocalAddress(conn, wifi[1]); - }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE(DefaultWait().Until([&] { + return (conn = ep1_ch1()->selected_connection()) != nullptr && + HasRemoteAddress(conn, wifi[1]); + })); + EXPECT_TRUE(DefaultWait().Until([&] { + return (conn = ep2_ch1()->selected_connection()) != nullptr && + HasLocalAddress(conn, wifi[1]); + })); // Add a new cellular interface on end point 1, we should expect a new // backup connection created using this new interface. AddAddress(0, cellular[0], "test_cellular0", ADAPTER_TYPE_CELLULAR); - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->GetState() == - IceTransportStateInternal::STATE_COMPLETED && - absl::c_any_of(ep1_ch1()->connections(), - [channel = ep1_ch1(), - address = cellular[0]](const Connection* conn) { - return HasLocalAddress(conn, address) && - conn != channel->selected_connection() && - conn->writable(); - }); - }, - {.timeout = kDefaultTimeout})); - EXPECT_TRUE(WaitUntil( - [&] { - return ep2_ch1()->GetState() == - IceTransportStateInternal::STATE_COMPLETED && - absl::c_any_of(ep2_ch1()->connections(), - [channel = ep2_ch1(), - address = cellular[0]](const Connection* conn) { - return HasRemoteAddress(conn, address) && - conn != channel->selected_connection() && - conn->receiving(); - }); - }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->GetState() == + IceTransportStateInternal::STATE_COMPLETED && + absl::c_any_of(ep1_ch1()->connections(), + [channel = ep1_ch1(), + address = cellular[0]](const Connection* conn) { + return HasLocalAddress(conn, address) && + conn != channel->selected_connection() && + conn->writable(); + }); + })); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep2_ch1()->GetState() == + IceTransportStateInternal::STATE_COMPLETED && + absl::c_any_of(ep2_ch1()->connections(), + [channel = ep2_ch1(), + address = cellular[0]](const Connection* conn) { + return HasRemoteAddress(conn, address) && + conn != channel->selected_connection() && + conn->receiving(); + }); + })); DestroyChannels(); } @@ -3323,9 +3219,7 @@ TEST_F(P2PTransportChannelMultihomedTest, // Tests that we can switch links via continual gathering. TEST_F(P2PTransportChannelMultihomedTest, TestSwitchLinksViaContinualGathering) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0]); AddAddress(1, kPublicAddrs[1]); // Use only local ports for simplicity. @@ -3336,45 +3230,37 @@ TEST_F(P2PTransportChannelMultihomedTest, IceConfig continual_gathering_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); // Create channels and let them go writable, as usual. - CreateChannels(env, continual_gathering_config, continual_gathering_config); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), - kPublicAddrs[0], kPublicAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + CreateChannels(continual_gathering_config, continual_gathering_config); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kPublicAddrs[1]); + })); // Add the new address first and then remove the other one. RTC_LOG(LS_INFO) << "Draining..."; AddAddress(1, kAlternateAddrs[1]); RemoveAddress(1, kPublicAddrs[1]); // We should switch to use the alternate address after an exchange of pings. - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected( - ep1_ch1(), ep2_ch1(), kPublicAddrs[0], kAlternateAddrs[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kAlternateAddrs[1]); + })); // Remove one address first and then add another address. RTC_LOG(LS_INFO) << "Draining again..."; RemoveAddress(1, kAlternateAddrs[1]); AddAddress(1, kAlternateAddrs[0]); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected( - ep1_ch1(), ep2_ch1(), kPublicAddrs[0], kAlternateAddrs[0]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), kPublicAddrs[0], + kAlternateAddrs[0]); + })); DestroyChannels(); } // Tests that the backup connection will be restored after it is destroyed. TEST_F(P2PTransportChannelMultihomedTest, TestRestoreBackupConnection) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); auto& wifi = kAlternateAddrs; auto& cellular = kPublicAddrs; AddAddress(0, wifi[0], "test_wifi0", ADAPTER_TYPE_WIFI); @@ -3388,57 +3274,46 @@ TEST_F(P2PTransportChannelMultihomedTest, TestRestoreBackupConnection) { // Create channels and let them go writable, as usual. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); config.regather_on_failed_networks_interval = TimeDelta::Seconds(2); - CreateChannels(env, config, config); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), wifi[0], - wifi[1]); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + CreateChannels(config, config); + EXPECT_TRUE(MediumWait().Until([&] { + return CheckCandidatePairAndConnected(ep1_ch1(), ep2_ch1(), wifi[0], + wifi[1]); + })); // Destroy all backup connections. DestroyAllButBestConnection(ep1_ch1()); // Ensure the backup connection is removed first. EXPECT_THAT( - WaitUntil( + MediumWait().Until( [&] { return GetConnectionWithLocalAddress(ep1_ch1(), cellular[0]); }, - Eq(nullptr), {.timeout = kDefaultTimeout, .clock = &clock}), + Eq(nullptr)), IsRtcOk()); const Connection* conn; - EXPECT_TRUE( - WaitUntil( - [&] { - return (conn = GetConnectionWithLocalAddress( - ep1_ch1(), cellular[0])) != nullptr && - conn != ep1_ch1()->selected_connection() && conn->writable(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return (conn = GetConnectionWithLocalAddress(ep1_ch1(), cellular[0])) != + nullptr && + conn != ep1_ch1()->selected_connection() && conn->writable(); + })); DestroyChannels(); } TEST_F(P2PTransportChannelMultihomedTest, TestVpnDefault) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_ETHERNET); AddAddress(0, kAlternateAddrs[0], "vpn0", ADAPTER_TYPE_VPN); AddAddress(1, kPublicAddrs[1]); IceConfig config; - CreateChannels(env, config, config, false); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckConnected(ep1_ch1(), ep2_ch1()) && - !ep1_ch1()->selected_connection()->network()->IsVpn(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + CreateChannels(config, config, false); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckConnected(ep1_ch1(), ep2_ch1()) && + !ep1_ch1()->selected_connection()->network()->IsVpn(); + })); } TEST_F(P2PTransportChannelMultihomedTest, TestVpnPreferVpn) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_ETHERNET); AddAddress(0, kAlternateAddrs[0], "vpn0", ADAPTER_TYPE_VPN, ADAPTER_TYPE_CELLULAR); @@ -3447,30 +3322,24 @@ TEST_F(P2PTransportChannelMultihomedTest, TestVpnPreferVpn) { IceConfig config; config.vpn_preference = VpnPreference::kPreferVpn; RTC_LOG(LS_INFO) << "KESO: config.vpn_preference: " << config.vpn_preference; - CreateChannels(env, config, config, false); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckConnected(ep1_ch1(), ep2_ch1()) && - ep1_ch1()->selected_connection()->network()->IsVpn(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + CreateChannels(config, config, false); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckConnected(ep1_ch1(), ep2_ch1()) && + ep1_ch1()->selected_connection()->network()->IsVpn(); + })); // Block VPN. fw()->AddRule(false, FP_ANY, FD_ANY, kAlternateAddrs[0]); // Check that it switches to non-VPN - EXPECT_TRUE(WaitUntil( - [&] { - return CheckConnected(ep1_ch1(), ep2_ch1()) && - !ep1_ch1()->selected_connection()->network()->IsVpn(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckConnected(ep1_ch1(), ep2_ch1()) && + !ep1_ch1()->selected_connection()->network()->IsVpn(); + })); } TEST_F(P2PTransportChannelMultihomedTest, TestVpnAvoidVpn) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_CELLULAR); AddAddress(0, kAlternateAddrs[0], "vpn0", ADAPTER_TYPE_VPN, ADAPTER_TYPE_ETHERNET); @@ -3478,30 +3347,24 @@ TEST_F(P2PTransportChannelMultihomedTest, TestVpnAvoidVpn) { IceConfig config; config.vpn_preference = VpnPreference::kAvoidVpn; - CreateChannels(env, config, config, false); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckConnected(ep1_ch1(), ep2_ch1()) && - !ep1_ch1()->selected_connection()->network()->IsVpn(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + CreateChannels(config, config, false); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckConnected(ep1_ch1(), ep2_ch1()) && + !ep1_ch1()->selected_connection()->network()->IsVpn(); + })); // Block non-VPN. fw()->AddRule(false, FP_ANY, FD_ANY, kPublicAddrs[0]); // Check that it switches to VPN - EXPECT_TRUE(WaitUntil( - [&] { - return CheckConnected(ep1_ch1(), ep2_ch1()) && - ep1_ch1()->selected_connection()->network()->IsVpn(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckConnected(ep1_ch1(), ep2_ch1()) && + ep1_ch1()->selected_connection()->network()->IsVpn(); + })); } TEST_F(P2PTransportChannelMultihomedTest, TestVpnNeverVpn) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_CELLULAR); AddAddress(0, kAlternateAddrs[0], "vpn0", ADAPTER_TYPE_VPN, ADAPTER_TYPE_ETHERNET); @@ -3509,27 +3372,23 @@ TEST_F(P2PTransportChannelMultihomedTest, TestVpnNeverVpn) { IceConfig config; config.vpn_preference = VpnPreference::kNeverUseVpn; - CreateChannels(env, config, config, false); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckConnected(ep1_ch1(), ep2_ch1()) && - !ep1_ch1()->selected_connection()->network()->IsVpn(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + CreateChannels(config, config, false); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckConnected(ep1_ch1(), ep2_ch1()) && + !ep1_ch1()->selected_connection()->network()->IsVpn(); + })); // Block non-VPN. fw()->AddRule(false, FP_ANY, FD_ANY, kPublicAddrs[0]); // Check that it does not switches to VPN - clock.AdvanceTime(kDefaultTimeout); - EXPECT_TRUE(WaitUntil([&] { return !CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + time_controller_.AdvanceTime(kDefaultTimeout); + EXPECT_TRUE(DefaultWait().Until( + [&] { return !CheckConnected(ep1_ch1(), ep2_ch1()); })); } TEST_F(P2PTransportChannelMultihomedTest, TestVpnOnlyVpn) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_CELLULAR); AddAddress(0, kAlternateAddrs[0], "vpn0", ADAPTER_TYPE_VPN, ADAPTER_TYPE_ETHERNET); @@ -3537,37 +3396,33 @@ TEST_F(P2PTransportChannelMultihomedTest, TestVpnOnlyVpn) { IceConfig config; config.vpn_preference = VpnPreference::kOnlyUseVpn; - CreateChannels(env, config, config, false); - EXPECT_TRUE(WaitUntil( - [&] { - return CheckConnected(ep1_ch1(), ep2_ch1()) && - ep1_ch1()->selected_connection()->network()->IsVpn(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + CreateChannels(config, config, false); + EXPECT_TRUE(DefaultWait().Until([&] { + return CheckConnected(ep1_ch1(), ep2_ch1()) && + ep1_ch1()->selected_connection()->network()->IsVpn(); + })); // Block VPN. fw()->AddRule(false, FP_ANY, FD_ANY, kAlternateAddrs[0]); // Check that it does not switch to non-VPN - clock.AdvanceTime(kDefaultTimeout); - EXPECT_TRUE(WaitUntil([&] { return !CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + time_controller_.AdvanceTime(kDefaultTimeout); + EXPECT_TRUE(DefaultWait().Until( + [&] { return !CheckConnected(ep1_ch1(), ep2_ch1()); })); } TEST_F(P2PTransportChannelMultihomedTest, StunDictionaryPerformsSync) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); AddAddress(0, kPublicAddrs[0], "eth0", ADAPTER_TYPE_CELLULAR); AddAddress(0, kAlternateAddrs[0], "vpn0", ADAPTER_TYPE_VPN, ADAPTER_TYPE_ETHERNET); AddAddress(1, kPublicAddrs[1]); // Create channels and let them go writable, as usual. - CreateChannels(env); + CreateChannels(); MockFunction)> + std::span)> view_updated_func; ep2_ch1()->AddDictionaryViewUpdatedCallback( "tag", view_updated_func.AsStdFunction()); @@ -3584,8 +3439,8 @@ TEST_F(P2PTransportChannelMultihomedTest, StunDictionaryPerformsSync) { EXPECT_EQ(view.GetByteString(12)->string_view(), "keso"); }); EXPECT_CALL(writer_synced_func, Call).Times(1); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); } // A collection of tests which tests a single P2PTransportChannel by sending @@ -3594,11 +3449,26 @@ class P2PTransportChannelPingTest : public ::testing::Test { public: P2PTransportChannelPingTest() : vss_(std::make_unique()), + time_controller_(Timestamp::Millis(1000), vss_.get()), + env_(CreateTestEnvironment({.time = &time_controller_})), packet_socket_factory_( std::make_unique(vss_.get())), - thread_(vss_.get()) {} + thread_(time_controller_.GetMainThread()) {} protected: + Waiter DefaultWait() { + return Waiter({.timeout = kDefaultTimeout, .clock = &time_controller_}); + } + Waiter MediumWait() { + return Waiter({.timeout = kMediumTimeout, .clock = &time_controller_}); + } + Waiter ShortWait() { + return Waiter({.timeout = kShortTimeout, .clock = &time_controller_}); + } + Waiter Wait(TimeDelta timeout) { + return Waiter({.timeout = timeout, .clock = &time_controller_}); + } + void PrepareChannel(P2PTransportChannel* ch) { ch->SetIceRole(ICEROLE_CONTROLLING); ch->SetIceParameters(kIceParams[0]); @@ -3622,18 +3492,11 @@ class P2PTransportChannelPingTest : public ::testing::Test { Connection* WaitForConnectionTo(P2PTransportChannel* ch, absl::string_view ip, - int port_num, - ThreadProcessingFakeClock* clock = nullptr) { - if (clock == nullptr) { - EXPECT_THAT(WaitUntil([&] { return GetConnectionTo(ch, ip, port_num); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); - } else { - EXPECT_THAT( - WaitUntil([&] { return GetConnectionTo(ch, ip, port_num); }, - Ne(nullptr), {.timeout = kMediumTimeout, .clock = clock}), - IsRtcOk()); - } + int port_num) { + EXPECT_THAT( + MediumWait().Until([&] { return GetConnectionTo(ch, ip, port_num); }, + Ne(nullptr)), + IsRtcOk()); return GetConnectionTo(ch, ip, port_num); } @@ -3679,17 +3542,16 @@ class P2PTransportChannelPingTest : public ::testing::Test { } Connection* CreateConnectionWithCandidate(P2PTransportChannel* channel, - ScopedFakeClock* clock, absl::string_view ip_addr, int port, int priority, bool writable) { channel->AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, ip_addr, port, priority)); - EXPECT_THAT( - WaitUntil([&] { return GetConnectionTo(channel, ip_addr, port); }, - Ne(nullptr), {.timeout = kMediumTimeout, .clock = clock}), - IsRtcOk()); + EXPECT_THAT(MediumWait().Until( + [&] { return GetConnectionTo(channel, ip_addr, port); }, + Ne(nullptr)), + IsRtcOk()); Connection* conn = GetConnectionTo(channel, ip_addr, port); if (conn && writable) { @@ -3811,10 +3673,12 @@ class P2PTransportChannelPingTest : public ::testing::Test { return packet_socket_factory_.get(); } - private: + protected: std::unique_ptr vss_; + GlobalSimulatedTimeController time_controller_; + Environment env_; std::unique_ptr packet_socket_factory_; - AutoSocketServerThread thread_; + Thread* thread_; int selected_candidate_pair_switches_ = 0; int last_sent_packet_id_ = -1; bool channel_ready_to_send_ = false; @@ -3825,9 +3689,8 @@ class P2PTransportChannelPingTest : public ::testing::Test { }; TEST_F(P2PTransportChannelPingTest, TestTriggeredChecks) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "trigger checks", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "trigger checks", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); ch.AddRemoteCandidate( @@ -3852,9 +3715,8 @@ TEST_F(P2PTransportChannelPingTest, TestTriggeredChecks) { } TEST_F(P2PTransportChannelPingTest, TestAllConnectionsPingedSufficiently) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "ping sufficiently", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "ping sufficiently", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); ch.AddRemoteCandidate( @@ -3870,24 +3732,20 @@ TEST_F(P2PTransportChannelPingTest, TestAllConnectionsPingedSufficiently) { // Low-priority connection becomes writable so that the other connection // is not pruned. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_TRUE(WaitUntil( - [&] { - return conn1->num_pings_sent() >= kMinPingsAtWeakPingInterval && - conn2->num_pings_sent() >= kMinPingsAtWeakPingInterval; - }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE(MediumWait().Until([&] { + return conn1->num_pings_sent() >= kMinPingsAtWeakPingInterval && + conn2->num_pings_sent() >= kMinPingsAtWeakPingInterval; + })); } // Verify that the connections are pinged at the right time. TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); int RTT_RATIO = 4; constexpr TimeDelta kSchedulingRange = TimeDelta::Millis(200); constexpr TimeDelta kRttRange = TimeDelta::Millis(10); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "TestChannel", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "TestChannel", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); ch.AddRemoteCandidate( @@ -3895,27 +3753,27 @@ TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) { Connection* conn = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn != nullptr); - SIMULATED_WAIT(conn->num_pings_sent() == 1, kDefaultTimeout.ms(), clock); + EXPECT_TRUE(DefaultWait().Until([&] { return conn->num_pings_sent() == 1; })); // Initializing. - Timestamp start = env.clock().CurrentTime(); - SIMULATED_WAIT(conn->num_pings_sent() >= kMinPingsAtWeakPingInterval, - kDefaultTimeout.ms(), clock); + Timestamp start = env_.clock().CurrentTime(); + EXPECT_TRUE(DefaultWait().Until( + [&] { return conn->num_pings_sent() >= kMinPingsAtWeakPingInterval; })); TimeDelta ping_interval = - (env.clock().CurrentTime() - start) / (kMinPingsAtWeakPingInterval - 1); + (env_.clock().CurrentTime() - start) / (kMinPingsAtWeakPingInterval - 1); EXPECT_EQ(ping_interval, kWeakPingInterval); // Stabilizing. conn->ReceivedPingResponse(kLowRtt, "id"); int ping_sent_before = conn->num_pings_sent(); - start = env.clock().CurrentTime(); + start = env_.clock().CurrentTime(); // The connection becomes strong but not stable because we haven't been able // to converge the RTT. - SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, - kMediumTimeout.ms(), clock); - ping_interval = env.clock().CurrentTime() - start; + EXPECT_TRUE(DefaultWait().Until( + [&] { return conn->num_pings_sent() == ping_sent_before + 1; })); + ping_interval = env_.clock().CurrentTime() - start; EXPECT_GE(ping_interval, kWeakOrStabilizingWritableConnectionPingInterval); EXPECT_LE(ping_interval, kWeakOrStabilizingWritableConnectionPingInterval + kSchedulingRange); @@ -3928,10 +3786,10 @@ TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) { conn->ReceivedPingResponse(kLowRtt, "id"); } ping_sent_before = conn->num_pings_sent(); - start = env.clock().CurrentTime(); - SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, - kMediumTimeout.ms(), clock); - ping_interval = env.clock().CurrentTime() - start; + start = env_.clock().CurrentTime(); + EXPECT_TRUE(MediumWait().Until( + [&] { return conn->num_pings_sent() == ping_sent_before + 1; })); + ping_interval = env_.clock().CurrentTime() - start; EXPECT_GE(ping_interval, kStrongAndStableWritableConnectionPingInterval); EXPECT_LE(ping_interval, kStrongAndStableWritableConnectionPingInterval + kSchedulingRange); @@ -3941,24 +3799,24 @@ TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) { conn->ReceivedPingResponse(kLowRtt, "id"); // Create a in-flight ping. conn->Ping(); - start = env.clock().CurrentTime(); + start = env_.clock().CurrentTime(); // In-flight ping timeout and the connection will be unstable. - SIMULATED_WAIT(!conn->stable(env.clock().CurrentTime()), kMediumTimeout.ms(), - clock); - TimeDelta duration = env.clock().CurrentTime() - start; + EXPECT_TRUE(MediumWait().Until( + [&] { return !conn->stable(env_.clock().CurrentTime()); })); + TimeDelta duration = env_.clock().CurrentTime() - start; EXPECT_GE(duration, 2 * conn->Rtt() - kRttRange); EXPECT_LE(duration, 2 * conn->Rtt() + kRttRange); // The connection become unstable due to not receiving ping responses. ping_sent_before = conn->num_pings_sent(); - SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, - kMediumTimeout.ms(), clock); + EXPECT_TRUE(MediumWait().Until( + [&] { return conn->num_pings_sent() == ping_sent_before + 1; })); // The interval is expected to be // kWeakOrStabilizingWritableConnectionPingInterval. - start = env.clock().CurrentTime(); + start = env_.clock().CurrentTime(); ping_sent_before = conn->num_pings_sent(); - SIMULATED_WAIT(conn->num_pings_sent() == ping_sent_before + 1, - kMediumTimeout.ms(), clock); - ping_interval = env.clock().CurrentTime() - start; + EXPECT_TRUE(MediumWait().Until( + [&] { return conn->num_pings_sent() == ping_sent_before + 1; })); + ping_interval = env_.clock().CurrentTime() - start; EXPECT_GE(ping_interval, kWeakOrStabilizingWritableConnectionPingInterval); EXPECT_LE(ping_interval, kWeakOrStabilizingWritableConnectionPingInterval + kSchedulingRange); @@ -3967,18 +3825,14 @@ TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) { // Test that we start pinging as soon as we have a connection and remote ICE // parameters. TEST_F(P2PTransportChannelPingTest, PingingStartedAsSoonAsPossible) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "TestChannel", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "TestChannel", 1, &pa); ch.SetIceRole(ICEROLE_CONTROLLING); ch.SetIceParameters(kIceParams[0]); ch.MaybeStartGathering(); - EXPECT_THAT(WaitUntil([&] { return ch.gathering_state(); }, - Eq(IceGatheringState::kIceGatheringComplete), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_TRUE(MediumWait().Until([&] { + return ch.gathering_state() == IceGatheringState::kIceGatheringComplete; + })); // Simulate a binding request being received, creating a peer reflexive // candidate pair while we still don't have remote ICE parameters. @@ -3997,22 +3851,19 @@ TEST_F(P2PTransportChannelPingTest, PingingStartedAsSoonAsPossible) { // Simulate waiting for a second (and change) and verify that no pings were // sent, since we don't yet have remote ICE parameters. - SIMULATED_WAIT(conn->num_pings_sent() > 0, 1025, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1025)); EXPECT_EQ(0, conn->num_pings_sent()); // Set remote ICE parameters. Now we should be able to ping. Ensure that // the first ping is sent as soon as possible, within one simulated clock // tick. ch.SetRemoteIceParameters(kIceParams[1]); - EXPECT_THAT(WaitUntil([&] { return conn->num_pings_sent(); }, Gt(0), - {.clock = &clock}), - IsRtcOk()); + EXPECT_TRUE(DefaultWait().Until([&] { return conn->num_pings_sent() > 0; })); } TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "trigger checks", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "trigger checks", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); ch.AddRemoteCandidate( @@ -4038,9 +3889,8 @@ TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) { } TEST_F(P2PTransportChannelPingTest, TestFailedConnectionNotPingable) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "Do not ping failed connections", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "Do not ping failed connections", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); ch.AddRemoteCandidate( @@ -4057,9 +3907,8 @@ TEST_F(P2PTransportChannelPingTest, TestFailedConnectionNotPingable) { } TEST_F(P2PTransportChannelPingTest, TestSignalStateChanged) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "state change", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "state change", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); ch.AddRemoteCandidate( @@ -4069,9 +3918,8 @@ TEST_F(P2PTransportChannelPingTest, TestSignalStateChanged) { // Pruning the connection reduces the set of active connections and changes // the channel state. conn1->Prune(); - EXPECT_THAT(WaitUntil([&] { return channel_state(); }, - Eq(IceTransportStateInternal::STATE_FAILED), - {.timeout = kDefaultTimeout}), + EXPECT_THAT(DefaultWait().Until([&] { return channel_state(); }, + Eq(IceTransportStateInternal::STATE_FAILED)), IsRtcOk()); } @@ -4082,9 +3930,8 @@ TEST_F(P2PTransportChannelPingTest, TestSignalStateChanged) { // parameters arrive. If a remote candidate is added with the current ICE // ufrag, its pwd and generation will be set properly. TEST_F(P2PTransportChannelPingTest, TestAddRemoteCandidateWithVariousUfrags) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "add candidate", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "add candidate", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); // Add a candidate with a future ufrag. @@ -4108,7 +3955,7 @@ TEST_F(P2PTransportChannelPingTest, TestAddRemoteCandidateWithVariousUfrags) { // Add a candidate with an old ufrag. No connection will be created. ch.AddRemoteCandidate(CreateUdpCandidate(IceCandidateType::kHost, "2.2.2.2", 2, 2, kIceUfrag[1])); - Thread::Current()->ProcessMessages(500); + time_controller_.AdvanceTime(TimeDelta::Millis(500)); EXPECT_TRUE(GetConnectionTo(&ch, "2.2.2.2", 2) == nullptr); // Add a candidate with the current ufrag, its pwd and generation will be @@ -4116,10 +3963,8 @@ TEST_F(P2PTransportChannelPingTest, TestAddRemoteCandidateWithVariousUfrags) { ch.AddRemoteCandidate(CreateUdpCandidate(IceCandidateType::kHost, "3.3.3.3", 3, 0, kIceUfrag[2])); Connection* conn3 = nullptr; - ASSERT_THAT( - WaitUntil([&] { return conn3 = GetConnectionTo(&ch, "3.3.3.3", 3); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + EXPECT_TRUE(DefaultWait().Until( + [&] { return (conn3 = GetConnectionTo(&ch, "3.3.3.3", 3)) != nullptr; })); const Candidate& new_candidate = conn3->remote_candidate(); EXPECT_EQ(kIcePwd[2], new_candidate.password()); EXPECT_EQ(1U, new_candidate.generation()); @@ -4137,9 +3982,8 @@ TEST_F(P2PTransportChannelPingTest, TestAddRemoteCandidateWithVariousUfrags) { } TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "connection resurrection", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "connection resurrection", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); @@ -4160,14 +4004,15 @@ TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) { conn2->ReceivedPingResponse(kLowRtt, "id"); // Wait for conn2 to be selected. - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn2), - {.timeout = kMediumTimeout}), - IsRtcOk()); + EXPECT_THAT( + MediumWait().Until([&] { return ch.selected_connection(); }, Eq(conn2)), + IsRtcOk()); // Destroy the connection to test SignalUnknownAddress. ch.RemoveConnectionForTest(conn1); - EXPECT_THAT(WaitUntil([&] { return GetConnectionTo(&ch, "1.1.1.1", 1); }, - Eq(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + EXPECT_THAT( + MediumWait().Until([&] { return GetConnectionTo(&ch, "1.1.1.1", 1); }, + Eq(nullptr)), + IsRtcOk()); // Create a minimal STUN message with prflx priority. IceMessage request(STUN_BINDING_REQUEST); @@ -4195,10 +4040,8 @@ TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) { } TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "receiving state change", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "receiving state change", 1, &pa); PrepareChannel(&ch); // Default receiving timeout and checking receiving interval should not be too // small. @@ -4210,18 +4053,16 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) { ch.MaybeStartGathering(); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 1)); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); - clock.AdvanceTime(TimeDelta::Seconds(1)); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); conn1->ReceivedPing(); conn1->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "ABC", 3, env.clock().TimeInMicroseconds())); + "ABC", 3, env_.clock().TimeInMicroseconds())); - EXPECT_TRUE(WaitUntil([&] { return ch.receiving(); }, - {.timeout = kShortTimeout, .clock = &clock})); - EXPECT_TRUE(WaitUntil([&] { return !ch.receiving(); }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { return ch.receiving(); })); + EXPECT_TRUE(ShortWait().Until([&] { return !ch.receiving(); })); } // The controlled side will select a connection as the "selected connection" @@ -4231,9 +4072,8 @@ TEST_F(P2PTransportChannelPingTest, TestReceivingStateChange) { // selected connection changes and SignalReadyToSend will be fired if the new // selected connection is writable. TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "receiving state change", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "receiving state change", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); @@ -4251,9 +4091,9 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { // A connection needs to be writable before it is selected for transmission. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + ShortWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); EXPECT_TRUE(ConnectionMatchesChangeEvent( conn1, "remote candidate generation maybe changed")); @@ -4266,9 +4106,9 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn2), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn2)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); EXPECT_TRUE( ConnectionMatchesChangeEvent(conn2, "candidate pair state changed")); @@ -4314,9 +4154,9 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { reset_channel_ready_to_send(); // The selected connection switches after conn4 becomes writable. conn4->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn4), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn4)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn4)); EXPECT_TRUE( ConnectionMatchesChangeEvent(conn4, "candidate pair state changed")); @@ -4329,10 +4169,13 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { // that sends a ping directly when a connection has been nominated // i.e on the ICE_CONTROLLED-side. TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/send_ping_on_nomination_ice_controlled:true/")); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "receiving state change", 1, &pa); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/" + "send_ping_on_nomination_ice_controlled:true/"), + .time = &time_controller_}); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "receiving state change", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLED); @@ -4344,9 +4187,9 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { // A connection needs to be writable before it is selected for transmission. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); // When a higher priority candidate comes in, the new connection is chosen @@ -4356,9 +4199,9 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn2), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn2)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // Now nominate conn1 (low prio), it shall be choosen. @@ -4375,10 +4218,12 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { // that sends a ping directly when switching to a new connection // on the ICE_CONTROLLING-side. TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/send_ping_on_switch_ice_controlling:true/")); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "receiving state change", 1, &pa); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/send_ping_on_switch_ice_controlling:true/"), + .time = &time_controller_}); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "receiving state change", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLING); @@ -4390,9 +4235,9 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { // A connection needs to be writable before it is selected for transmission. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); // When a higher priority candidate comes in, the new connection is chosen @@ -4405,9 +4250,9 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { const int before = conn2->num_pings_sent(); conn2->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn2), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn2)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // And the additional ping should have been sent directly. @@ -4418,10 +4263,12 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { // that sends a ping directly when selecteing a new connection // on the ICE_CONTROLLING-side (i.e also initial selection). TEST_F(P2PTransportChannelPingTest, TestPingOnSelected) { - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/send_ping_on_selected_ice_controlling:true/")); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "receiving state change", 1, &pa); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/send_ping_on_selected_ice_controlling:true/"), + .time = &time_controller_}); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "receiving state change", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLING); @@ -4435,9 +4282,9 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnSelected) { // A connection needs to be writable before it is selected for transmission. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); // And the additional ping should have been sent directly. @@ -4451,9 +4298,8 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnSelected) { // also sends back a ping response and set the ICE pwd in the remote candidate // appropriately. TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "receiving state change", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "receiving state change", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); @@ -4472,9 +4318,9 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) { EXPECT_EQ(conn1->stats().sent_ping_responses, 1u); EXPECT_NE(conn1, ch.selected_connection()); conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); // Another connection is nominated via use_candidate. ch.AddRemoteCandidate( @@ -4512,9 +4358,9 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) { // conn4 is not the selected connection yet because it is not writable. EXPECT_EQ(conn2, ch.selected_connection()); conn4->ReceivedPingResponse(kLowRtt, "id"); // Become writable. - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn4), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn4)), + IsRtcOk()); // Test that the request from an unknown address contains a ufrag from an old // generation. @@ -4534,9 +4380,8 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) { // at which point the controlled side will select that connection as // the "selected connection". TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "receiving state change", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "receiving state change", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); @@ -4545,9 +4390,9 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) { Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); // If a data packet is received on conn2, the selected connection should // switch to conn2 because the controlled side must mirror the media path @@ -4558,7 +4403,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) { ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPingResponse(kLowRtt, "id"); // Become writable and receiving. conn2->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "ABC", 3, env.clock().TimeInMicroseconds())); + "ABC", 3, env_.clock().TimeInMicroseconds())); EXPECT_EQ(conn2, ch.selected_connection()); conn2->ReceivedPingResponse(kLowRtt, "id"); // Become writable. @@ -4579,37 +4424,34 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) { ASSERT_TRUE(conn3 != nullptr); EXPECT_NE(conn3, ch.selected_connection()); // Not writable yet. conn3->ReceivedPingResponse(kLowRtt, "id"); // Become writable. - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn3), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn3)), + IsRtcOk()); // Now another data packet will not switch the selected connection because the // selected connection was nominated by the controlling side. conn2->ReceivedPing(); conn2->ReceivedPingResponse(kLowRtt, "id"); conn2->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "XYZ", 3, env.clock().TimeInMicroseconds())); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn3), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + "XYZ", 3, env_.clock().TimeInMicroseconds())); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn3)), + IsRtcOk()); } TEST_F(P2PTransportChannelPingTest, TestControlledAgentDataReceivingTakesHigherPrecedenceThanPriority) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - clock.AdvanceTime(TimeDelta::Seconds(1)); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "SwitchSelectedConnection", 1, &pa); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "SwitchSelectedConnection", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); // The connections have decreasing priority. Connection* conn1 = - CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", 1, 10, true); + CreateConnectionWithCandidate(&ch, "1.1.1.1", 1, 10, true); ASSERT_TRUE(conn1 != nullptr); - Connection* conn2 = - CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", 2, 9, true); + Connection* conn2 = CreateConnectionWithCandidate(&ch, "2.2.2.2", 2, 9, true); ASSERT_TRUE(conn2 != nullptr); // Initially, connections are selected based on priority. @@ -4619,59 +4461,56 @@ TEST_F(P2PTransportChannelPingTest, // conn2 receives data; it becomes selected. // Advance the clock by 1ms so that the last data receiving timestamp of // conn2 is larger. - SIMULATED_WAIT(false, 1, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1)); conn2->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "XYZ", 3, env.clock().TimeInMicroseconds())); + "XYZ", 3, env_.clock().TimeInMicroseconds())); EXPECT_EQ(1, reset_selected_candidate_pair_switches()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // conn1 also receives data; it becomes selected due to priority again. conn1->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "ABC", 3, env.clock().TimeInMicroseconds())); + "ABC", 3, env_.clock().TimeInMicroseconds())); EXPECT_EQ(1, reset_selected_candidate_pair_switches()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // conn2 received data more recently; it is selected now because it // received data more recently. - SIMULATED_WAIT(false, 1, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1)); // Need to become writable again because it was pruned. conn2->ReceivedPingResponse(kLowRtt, "id"); conn2->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "ABC", 3, env.clock().TimeInMicroseconds())); + "ABC", 3, env_.clock().TimeInMicroseconds())); EXPECT_EQ(1, reset_selected_candidate_pair_switches()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // Make sure sorting won't reselect candidate pair. - SIMULATED_WAIT(false, 10, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(10)); EXPECT_EQ(0, reset_selected_candidate_pair_switches()); } TEST_F(P2PTransportChannelPingTest, TestControlledAgentNominationTakesHigherPrecedenceThanDataReceiving) { - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "SwitchSelectedConnection", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "SwitchSelectedConnection", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); // The connections have decreasing priority. Connection* conn1 = - CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", 1, 10, true); + CreateConnectionWithCandidate(&ch, "1.1.1.1", 1, 10, true); ASSERT_TRUE(conn1 != nullptr); - Connection* conn2 = - CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", 2, 9, true); + Connection* conn2 = CreateConnectionWithCandidate(&ch, "2.2.2.2", 2, 9, true); ASSERT_TRUE(conn2 != nullptr); // conn1 received data; it is the selected connection. // Advance the clock to have a non-zero last-data-receiving time. - SIMULATED_WAIT(false, 1, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1)); conn1->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "XYZ", 3, env.clock().TimeInMicroseconds())); + "XYZ", 3, env_.clock().TimeInMicroseconds())); EXPECT_EQ(1, reset_selected_candidate_pair_switches()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); @@ -4686,33 +4525,30 @@ TEST_F(P2PTransportChannelPingTest, EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // Make sure sorting won't reselect candidate pair. - SIMULATED_WAIT(false, 10, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(10)); EXPECT_EQ(0, reset_selected_candidate_pair_switches()); } TEST_F(P2PTransportChannelPingTest, TestControlledAgentSelectsConnectionWithHigherNomination) { - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); // The connections have decreasing priority. Connection* conn1 = - CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", 1, 10, true); + CreateConnectionWithCandidate(&ch, "1.1.1.1", 1, 10, true); ASSERT_TRUE(conn1 != nullptr); - Connection* conn2 = - CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", 2, 9, true); + Connection* conn2 = CreateConnectionWithCandidate(&ch, "2.2.2.2", 2, 9, true); ASSERT_TRUE(conn2 != nullptr); // conn1 is the selected connection because it has a higher priority, - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); reset_selected_candidate_pair_switches(); @@ -4735,34 +4571,32 @@ TEST_F(P2PTransportChannelPingTest, EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // Make sure sorting won't reselect candidate pair. - SIMULATED_WAIT(false, 100, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(100)); EXPECT_EQ(0, reset_selected_candidate_pair_switches()); } TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) { - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); // The connections have decreasing priority. Connection* conn1 = - CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", /* port= */ 1, + CreateConnectionWithCandidate(&ch, "1.1.1.1", /* port= */ 1, /* priority= */ 10, /* writable= */ true); ASSERT_TRUE(conn1 != nullptr); Connection* conn2 = - CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", /* port= */ 2, + CreateConnectionWithCandidate(&ch, "2.2.2.2", /* port= */ 2, /* priority= */ 9, /* writable= */ true); ASSERT_TRUE(conn2 != nullptr); // conn1 is the selected connection because it has a higher priority, - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); // No estimateded disconnect time at first connect <=> value is 0. EXPECT_EQ(LastEstimatedDisconnectedTimeMs(), 0); @@ -4771,11 +4605,16 @@ TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) { int nomination = 1; { - clock.AdvanceTime(TimeDelta::Seconds(1)); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); // This will not parse as STUN, and is considered data conn1->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "XYZ", 3, env.clock().TimeInMicroseconds())); - clock.AdvanceTime(TimeDelta::Seconds(2)); + "XYZ", 3, env_.clock().TimeInMicroseconds())); + time_controller_.AdvanceTime(TimeDelta::Seconds(2)); + + // To be eligible for selection upon nomination, conn2 must be receiving. + // The real time controller fires timers that cause conn2 to lose receiving + // status during the 2s wait. + conn2->ReceivedPingResponse(kLowRtt, "id"); // conn2 is nominated; it becomes selected. NominateConnection(conn2, nomination++); @@ -4785,13 +4624,16 @@ TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) { } { - clock.AdvanceTime(TimeDelta::Seconds(1)); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); conn2->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "XYZ", 3, env.clock().TimeInMicroseconds())); - clock.AdvanceTime(TimeDelta::Seconds(2)); + "XYZ", 3, env_.clock().TimeInMicroseconds())); + time_controller_.AdvanceTime(TimeDelta::Seconds(2)); ReceivePingOnConnection(conn2, kIceUfrag[1], 1, nomination++); - clock.AdvanceTime(TimeDelta::Millis(500)); + // To be eligible for selection upon nomination, conn1 must be receiving. + conn1->ReceivedPingResponse(kLowRtt, "id"); + + time_controller_.AdvanceTime(TimeDelta::Millis(500)); ReceivePingOnConnection(conn1, kIceUfrag[1], 1, nomination++); EXPECT_EQ(conn1, ch.selected_connection()); @@ -4802,17 +4644,15 @@ TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) { TEST_F(P2PTransportChannelPingTest, TestControlledAgentIgnoresSmallerNomination) { - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); Connection* conn = - CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", 1, 10, false); + CreateConnectionWithCandidate(&ch, "1.1.1.1", 1, 10, false); ReceivePingOnConnection(conn, kIceUfrag[1], 1, 2U); EXPECT_EQ(2U, conn->remote_nomination()); // Smaller nomination is ignored. @@ -4822,20 +4662,17 @@ TEST_F(P2PTransportChannelPingTest, TEST_F(P2PTransportChannelPingTest, TestControlledAgentWriteStateTakesHigherPrecedenceThanNomination) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "SwitchSelectedConnection", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "SwitchSelectedConnection", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); // The connections have decreasing priority. Connection* conn1 = - CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", 1, 10, false); + CreateConnectionWithCandidate(&ch, "1.1.1.1", 1, 10, false); ASSERT_TRUE(conn1 != nullptr); Connection* conn2 = - CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", 2, 9, false); + CreateConnectionWithCandidate(&ch, "2.2.2.2", 2, 9, false); ASSERT_TRUE(conn2 != nullptr); NominateConnection(conn1); @@ -4845,36 +4682,35 @@ TEST_F(P2PTransportChannelPingTest, // conn2 becomes writable; it is selected even though it is not nominated. conn2->ReceivedPingResponse(kLowRtt, "id"); EXPECT_THAT( - WaitUntil([&] { return reset_selected_candidate_pair_switches(); }, Eq(1), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until( + [&] { return reset_selected_candidate_pair_switches(); }, Eq(1)), + IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn2)), IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn2), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn2)); // If conn1 is also writable, it will become selected. conn1->ReceivedPingResponse(kLowRtt, "id"); EXPECT_THAT( - WaitUntil([&] { return reset_selected_candidate_pair_switches(); }, Eq(1), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until( + [&] { return reset_selected_candidate_pair_switches(); }, Eq(1)), + IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1)); // Make sure sorting won't reselect candidate pair. - SIMULATED_WAIT(false, 10, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(10)); EXPECT_EQ(0, reset_selected_candidate_pair_switches()); } // Test that if a new remote candidate has the same address and port with // an old one, it will be used to create a new connection. TEST_F(P2PTransportChannelPingTest, TestAddRemoteCandidateWithAddressReuse) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "candidate reuse", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "candidate reuse", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); const std::string host_address = "1.1.1.1"; @@ -4911,11 +4747,9 @@ TEST_F(P2PTransportChannelPingTest, TestAddRemoteCandidateWithAddressReuse) { // When the current selected connection is strong, lower-priority connections // will be pruned. Otherwise, lower-priority connections are kept. TEST_F(P2PTransportChannelPingTest, TestDontPruneWhenWeak) { - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); @@ -4930,60 +4764,54 @@ TEST_F(P2PTransportChannelPingTest, TestDontPruneWhenWeak) { // lower-priority are pruned. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "2.2.2.2", 2, 10)); - Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2, &clock); + Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPingResponse(kLowRtt, "id"); // Becomes writable and receiving NominateConnection(conn2); - EXPECT_TRUE(WaitUntil([&] { return conn1->pruned(); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { return conn1->pruned(); })); ch.SetIceConfig(CreateIceConfig(TimeDelta::Millis(500), GATHER_ONCE)); // Wait until conn2 becomes not receiving. - EXPECT_TRUE(WaitUntil([&] { return !conn2->receiving(); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { return !conn2->receiving(); })); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "3.3.3.3", 3, 1)); - Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3, &clock); + Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3); ASSERT_TRUE(conn3 != nullptr); // The selected connection should still be conn2. Even through conn3 has lower // priority and is not receiving/writable, it is not pruned because the // selected connection is not receiving. - SIMULATED_WAIT(conn3->pruned(), kShortTimeout.ms(), clock); + EXPECT_FALSE(MediumWait().Until([&] { return conn3->pruned(); })); EXPECT_FALSE(conn3->pruned()); } TEST_F(P2PTransportChannelPingTest, TestDontPruneHighPriorityConnections) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); ch.MaybeStartGathering(); Connection* conn1 = - CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", 1, 100, true); + CreateConnectionWithCandidate(&ch, "1.1.1.1", 1, 100, true); ASSERT_TRUE(conn1 != nullptr); Connection* conn2 = - CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", 2, 200, false); + CreateConnectionWithCandidate(&ch, "2.2.2.2", 2, 200, false); ASSERT_TRUE(conn2 != nullptr); // Even if conn1 is writable, nominated, receiving data, it should not prune // conn2. NominateConnection(conn1); - SIMULATED_WAIT(false, 1, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1)); conn1->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( - "XYZ", 3, env.clock().TimeInMicroseconds())); - SIMULATED_WAIT(conn2->pruned(), 100, clock); + "XYZ", 3, env_.clock().TimeInMicroseconds())); + (void)ShortWait().Until([&] { return conn2->pruned(); }); EXPECT_FALSE(conn2->pruned()); } // Test that GetState returns the state correctly. TEST_F(P2PTransportChannelPingTest, TestGetState) { - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); EXPECT_EQ(IceTransportState::kNew, ch.GetIceTransportState()); PrepareChannel(&ch); ch.MaybeStartGathering(); @@ -4998,8 +4826,8 @@ TEST_F(P2PTransportChannelPingTest, TestGetState) { // Checking candidates that have been added with gathered candidates. ASSERT_GT(ch.connections().size(), 0u); EXPECT_EQ(IceTransportState::kChecking, ch.GetIceTransportState()); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); - Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); + Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn1 != nullptr); ASSERT_TRUE(conn2 != nullptr); // Now there are two connections, so the transport channel is connecting. @@ -5009,15 +4837,13 @@ TEST_F(P2PTransportChannelPingTest, TestGetState) { EXPECT_EQ(IceTransportState::kChecking, ch.GetIceTransportState()); // `conn1` becomes writable and receiving; it then should prune `conn2`. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_TRUE(WaitUntil([&] { return conn2->pruned(); }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { return conn2->pruned(); })); EXPECT_EQ(IceTransportStateInternal::STATE_COMPLETED, ch.GetState()); EXPECT_EQ(IceTransportState::kConnected, ch.GetIceTransportState()); conn1->Prune(); // All connections are pruned. // Need to wait until the channel state is updated. - EXPECT_THAT(WaitUntil([&] { return ch.GetState(); }, - Eq(IceTransportStateInternal::STATE_FAILED), - {.timeout = kShortTimeout, .clock = &clock}), + EXPECT_THAT(ShortWait().Until([&] { return ch.GetState(); }, + Eq(IceTransportStateInternal::STATE_FAILED)), IsRtcOk()); EXPECT_EQ(IceTransportState::kFailed, ch.GetIceTransportState()); } @@ -5025,12 +4851,10 @@ TEST_F(P2PTransportChannelPingTest, TestGetState) { // Test that when a low-priority connection is pruned, it is not deleted // right away, and it can become active and be pruned again. TEST_F(P2PTransportChannelPingTest, TestConnectionPrunedAgain) { - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE); config.receiving_switching_delay = TimeDelta::Millis(800); @@ -5038,13 +4862,13 @@ TEST_F(P2PTransportChannelPingTest, TestConnectionPrunedAgain) { ch.MaybeStartGathering(); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 100)); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(nullptr, ch.selected_connection()); conn1->ReceivedPingResponse(kLowRtt, "id"); // Becomes writable and receiving - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + ShortWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); // Add a low-priority connection `conn2`, which will be pruned, but it will // not be deleted right away. Once the current selected connection becomes not @@ -5052,74 +4876,66 @@ TEST_F(P2PTransportChannelPingTest, TestConnectionPrunedAgain) { // it will become the selected connection. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "2.2.2.2", 2, 1)); - Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2, &clock); + Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); - EXPECT_TRUE(WaitUntil([&] { return !conn2->active(); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { return !conn2->active(); })); // `conn2` should not send a ping yet. EXPECT_EQ(IceCandidatePairState::WAITING, conn2->state()); EXPECT_EQ(IceTransportStateInternal::STATE_COMPLETED, ch.GetState()); // Wait for `conn1` becoming not receiving. - EXPECT_TRUE(WaitUntil([&] { return !conn1->receiving(); }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { return !conn1->receiving(); })); // Make sure conn2 is not deleted. - conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2, &clock); + conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); - EXPECT_THAT(WaitUntil([&] { return conn2->state(); }, - Eq(IceCandidatePairState::IN_PROGRESS), - {.timeout = kDefaultTimeout, .clock = &clock}), + EXPECT_THAT(MediumWait().Until([&] { return conn2->state(); }, + Eq(IceCandidatePairState::IN_PROGRESS)), IsRtcOk()); conn2->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn2), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn2)), + IsRtcOk()); EXPECT_EQ(IceTransportStateInternal::STATE_CONNECTING, ch.GetState()); // When `conn1` comes back again, `conn2` will be pruned again. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = kDefaultTimeout, .clock = &clock}), - IsRtcOk()); - EXPECT_TRUE(WaitUntil([&] { return !conn2->active(); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), + IsRtcOk()); + EXPECT_TRUE(DefaultWait().Until([&] { return !conn2->active(); })); EXPECT_EQ(IceTransportStateInternal::STATE_COMPLETED, ch.GetState()); } // Test that if all connections in a channel has timed out on writing, they // will all be deleted. We use Prune to simulate write_time_out. TEST_F(P2PTransportChannelPingTest, TestDeleteConnectionsIfAllWriteTimedout) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.MaybeStartGathering(); // Have one connection only but later becomes write-time-out. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 100)); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); conn1->ReceivedPing(); // Becomes receiving conn1->Prune(); - EXPECT_TRUE(WaitUntil([&] { return ch.connections().empty(); }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { return ch.connections().empty(); })); // Have two connections but both become write-time-out later. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "2.2.2.2", 2, 1)); - Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2, &clock); + Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2); ASSERT_TRUE(conn2 != nullptr); conn2->ReceivedPing(); // Becomes receiving ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "3.3.3.3", 3, 2)); - Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3, &clock); + Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3); ASSERT_TRUE(conn3 != nullptr); conn3->ReceivedPing(); // Becomes receiving // Now prune both conn2 and conn3; they will be deleted soon. conn2->Prune(); conn3->Prune(); - EXPECT_TRUE(WaitUntil([&] { return ch.connections().empty(); }, - {.timeout = kShortTimeout, .clock = &clock})); + EXPECT_TRUE(ShortWait().Until([&] { return ch.connections().empty(); })); } // Tests that after a port allocator session is started, it will be stopped @@ -5127,9 +4943,8 @@ TEST_F(P2PTransportChannelPingTest, TestDeleteConnectionsIfAllWriteTimedout) { // connection belonging to an old session becomes writable, it won't stop // the current port allocator session. TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(CreateIceConfig(TimeDelta::Seconds(2), GATHER_ONCE)); ch.MaybeStartGathering(); @@ -5163,9 +4978,8 @@ TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) { // These ports may still have connections that need a correct role, in case that // the connections on it may still receive stun pings. TEST_F(P2PTransportChannelPingTest, TestIceRoleUpdatedOnRemovedPort) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", ICE_CANDIDATE_COMPONENT_DEFAULT, + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", ICE_CANDIDATE_COMPONENT_DEFAULT, &pa); // Starts with ICEROLE_CONTROLLING. PrepareChannel(&ch); @@ -5191,9 +5005,8 @@ TEST_F(P2PTransportChannelPingTest, TestIceRoleUpdatedOnRemovedPort) { // pings sent by those connections until they're replaced by newer-generation // connections. TEST_F(P2PTransportChannelPingTest, TestIceRoleUpdatedOnPortAfterIceRestart) { - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", ICE_CANDIDATE_COMPONENT_DEFAULT, + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", ICE_CANDIDATE_COMPONENT_DEFAULT, &pa); // Starts with ICEROLE_CONTROLLING. PrepareChannel(&ch); @@ -5216,11 +5029,8 @@ TEST_F(P2PTransportChannelPingTest, TestIceRoleUpdatedOnPortAfterIceRestart) { // will be destroyed. The port will only be destroyed after it is marked as // "pruned." TEST_F(P2PTransportChannelPingTest, TestPortDestroyedAfterTimeoutAndPruned) { - ScopedFakeClock fake_clock; - const Environment env = CreateEnvironment(); - - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", ICE_CANDIDATE_COMPONENT_DEFAULT, + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", ICE_CANDIDATE_COMPONENT_DEFAULT, &pa); PrepareChannel(&ch); ch.SetIceRole(ICEROLE_CONTROLLED); @@ -5234,7 +5044,7 @@ TEST_F(P2PTransportChannelPingTest, TestPortDestroyedAfterTimeoutAndPruned) { // Simulate 2 minutes going by. This should be enough time for the port to // time out. for (int second = 0; second < 120; ++second) { - fake_clock.AdvanceTime(TimeDelta::Seconds(1)); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); } EXPECT_EQ(nullptr, GetConnectionTo(&ch, "1.1.1.1", 1)); // Port will not be removed because it is not pruned yet. @@ -5243,19 +5053,20 @@ TEST_F(P2PTransportChannelPingTest, TestPortDestroyedAfterTimeoutAndPruned) { // If the session prunes all ports, the port will be destroyed. ch.allocator_session()->PruneAllPorts(); - EXPECT_THAT(WaitUntil([&] { return GetPort(&ch); }, Eq(nullptr), - {.clock = &fake_clock}), - IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return GetPrunedPort(&ch); }, Eq(nullptr), - {.clock = &fake_clock}), + EXPECT_THAT(DefaultWait().Until([&] { return GetPort(&ch); }, Eq(nullptr)), IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return GetPrunedPort(&ch); }, Eq(nullptr)), + IsRtcOk()); } TEST_F(P2PTransportChannelPingTest, TestMaxOutstandingPingsFieldTrial) { - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/max_outstanding_pings:3/")); - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "max", 1, &pa); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/max_outstanding_pings:3/"), + .time = &time_controller_}); + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "max", 1, &pa); ch.SetIceConfig(ch.config()); PrepareChannel(&ch); ch.MaybeStartGathering(); @@ -5269,11 +5080,9 @@ TEST_F(P2PTransportChannelPingTest, TestMaxOutstandingPingsFieldTrial) { ASSERT_TRUE(conn1 != nullptr); ASSERT_TRUE(conn2 != nullptr); - EXPECT_TRUE(WaitUntil( - [&] { - return conn1->num_pings_sent() == 3 && conn2->num_pings_sent() == 3; - }, - {.timeout = kDefaultTimeout})); + EXPECT_TRUE(DefaultWait().Until([&] { + return conn1->num_pings_sent() == 3 && conn2->num_pings_sent() == 3; + })); // Check that these connections don't send any more pings. EXPECT_EQ(nullptr, ch.FindNextPingableConnection()); @@ -5286,11 +5095,11 @@ class P2PTransportChannelMostLikelyToWorkFirstTest network_manager_.AddInterface(kPublicAddrs[0]); } - BasicPortAllocator& CreatePortAllocator(const Environment& env) { - turn_server_.emplace(env, Thread::Current(), ss(), kTurnUdpIntAddr, + BasicPortAllocator& CreatePortAllocator(const Environment& env_) { + turn_server_.emplace(env_, Thread::Current(), ss(), kTurnUdpIntAddr, kTurnUdpExtAddr); port_allocator_ = CreateBasicPortAllocator( - env, &network_manager_, packet_socket_factory(), ServerAddresses(), + env_, &network_manager_, packet_socket_factory(), ServerAddresses(), kTurnUdpIntAddr, SocketAddress()); port_allocator_->set_flags(port_allocator_->flags() | PORTALLOCATOR_DISABLE_STUN | @@ -5300,10 +5109,10 @@ class P2PTransportChannelMostLikelyToWorkFirstTest } P2PTransportChannel& StartTransportChannel( - const Environment& env, + const Environment& env_, bool prioritize_most_likely_to_work, TimeDelta stable_writable_connection_ping_interval) { - channel_ = std::make_unique(env, "checks", 1, + channel_ = std::make_unique(env_, "checks", 1, port_allocator_.get()); IceConfig config = channel_->config(); config.prioritize_most_likely_candidate_pairs = @@ -5337,6 +5146,22 @@ class P2PTransportChannelMostLikelyToWorkFirstTest EXPECT_EQ(conn->remote_candidate().type(), remote_candidate_type); } + protected: + Waiter DefaultWait() { + return Waiter({.timeout = kDefaultTimeout, .clock = &time_controller_}); + } + Waiter MediumWait() { + return Waiter({.timeout = kMediumTimeout, .clock = &time_controller_}); + } + Waiter ShortWait() { + return Waiter({.timeout = kShortTimeout, .clock = &time_controller_}); + } + Waiter Wait(TimeDelta timeout) { + return Waiter({.timeout = timeout, .clock = &time_controller_}); + } + + FakeNetworkManager& network_manager() { return network_manager_; } + private: std::unique_ptr port_allocator_; FakeNetworkManager network_manager_{Thread::Current()}; @@ -5349,13 +5174,11 @@ class P2PTransportChannelMostLikelyToWorkFirstTest // we have a selected connection. TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestRelayRelayFirstWhenNothingPingedYet) { - const Environment env = CreateEnvironment(); const TimeDelta max_strong_interval = TimeDelta::Millis(500); - CreatePortAllocator(env); + CreatePortAllocator(env_); P2PTransportChannel& ch = - StartTransportChannel(env, true, max_strong_interval); - EXPECT_THAT(WaitUntil([&] { return ch.ports().size(); }, Eq(2), - {.timeout = kDefaultTimeout}), + StartTransportChannel(env_, true, max_strong_interval); + EXPECT_THAT(DefaultWait().Until([&] { return ch.ports().size(); }, Eq(2)), IsRtcOk()); EXPECT_EQ(ch.ports()[0]->Type(), IceCandidateType::kHost); EXPECT_EQ(ch.ports()[1]->Type(), IceCandidateType::kRelay); @@ -5365,9 +5188,9 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "2.2.2.2", 2, 2)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(4), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(4)), + IsRtcOk()); // Relay/Relay should be the first pingable connection. Connection* conn = FindNextPingableConnectionAndPingIt(&ch); @@ -5418,21 +5241,19 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // in the first round. TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestRelayRelayFirstWhenEverythingPinged) { - const Environment env = CreateEnvironment(); - CreatePortAllocator(env); + CreatePortAllocator(env_); P2PTransportChannel& ch = - StartTransportChannel(env, true, TimeDelta::Millis(500)); - EXPECT_THAT(WaitUntil([&] { return ch.ports().size(); }, Eq(2), - {.timeout = kDefaultTimeout}), + StartTransportChannel(env_, true, TimeDelta::Millis(500)); + EXPECT_THAT(DefaultWait().Until([&] { return ch.ports().size(); }, Eq(2)), IsRtcOk()); EXPECT_EQ(ch.ports()[0]->Type(), IceCandidateType::kHost); EXPECT_EQ(ch.ports()[1]->Type(), IceCandidateType::kRelay); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 1)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(2), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(2)), + IsRtcOk()); // Initially, only have Local/Local and Local/Relay. VerifyNextPingableConnection(IceCandidateType::kHost, @@ -5443,9 +5264,9 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // Remote Relay candidate arrives. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kRelay, "2.2.2.2", 2, 2)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(4), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(4)), + IsRtcOk()); // Relay/Relay should be the first since it hasn't been pinged before. VerifyNextPingableConnection(IceCandidateType::kRelay, @@ -5465,21 +5286,19 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // before we re-ping Relay/Relay connections again. TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestNoStarvationOnNonRelayConnection) { - const Environment env = CreateEnvironment(); - CreatePortAllocator(env); + CreatePortAllocator(env_); P2PTransportChannel& ch = - StartTransportChannel(env, true, TimeDelta::Millis(500)); - EXPECT_THAT(WaitUntil([&] { return ch.ports().size(); }, Eq(2), - {.timeout = kDefaultTimeout}), + StartTransportChannel(env_, true, TimeDelta::Millis(500)); + EXPECT_THAT(DefaultWait().Until([&] { return ch.ports().size(); }, Eq(2)), IsRtcOk()); EXPECT_EQ(ch.ports()[0]->Type(), IceCandidateType::kHost); EXPECT_EQ(ch.ports()[1]->Type(), IceCandidateType::kRelay); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kRelay, "1.1.1.1", 1, 1)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(2), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(2)), + IsRtcOk()); // Initially, only have Relay/Relay and Local/Relay. Ping Relay/Relay first. VerifyNextPingableConnection(IceCandidateType::kRelay, @@ -5492,9 +5311,9 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // Remote Local candidate arrives. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "2.2.2.2", 2, 2)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(4), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(4)), + IsRtcOk()); // Local/Local should be the first since it hasn't been pinged before. VerifyNextPingableConnection(IceCandidateType::kHost, @@ -5514,13 +5333,14 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // I.e that we never create connection between relay and non-relay. TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestSkipRelayToNonRelayConnectionsFieldTrial) { - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/")); - CreatePortAllocator(env); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/"), + .time = &time_controller_}); + CreatePortAllocator(env_); P2PTransportChannel& ch = - StartTransportChannel(env, true, TimeDelta::Millis(500)); - EXPECT_THAT(WaitUntil([&] { return ch.ports().size(); }, Eq(2), - {.timeout = kDefaultTimeout}), + StartTransportChannel(env_, true, TimeDelta::Millis(500)); + EXPECT_THAT(DefaultWait().Until([&] { return ch.ports().size(); }, Eq(2)), IsRtcOk()); EXPECT_EQ(ch.ports()[0]->Type(), IceCandidateType::kHost); EXPECT_EQ(ch.ports()[1]->Type(), IceCandidateType::kRelay); @@ -5528,33 +5348,31 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // Remote Relay candidate arrives. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kRelay, "1.1.1.1", 1, 1)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(1), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(1)), + IsRtcOk()); // Remote Local candidate arrives. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "2.2.2.2", 2, 2)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(2), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(2)), + IsRtcOk()); } // Test the ping sequence is UDP Relay/Relay followed by TCP Relay/Relay, // followed by the rest. TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestTcpTurn) { - const Environment env = CreateEnvironment(); RelayServerConfig config; config.credentials = kRelayCredentials; config.ports.push_back(ProtocolAddress(kTurnTcpIntAddr, PROTO_TCP)); - CreatePortAllocator(env).AddTurnServerForTesting(config); + CreatePortAllocator(env_).AddTurnServerForTesting(config); // Add a Tcp Turn server. turn_server().AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); P2PTransportChannel& ch = - StartTransportChannel(env, true, TimeDelta::Millis(500)); - EXPECT_THAT(WaitUntil([&] { return ch.ports().size(); }, Eq(3), - {.timeout = kDefaultTimeout}), + StartTransportChannel(env_, true, TimeDelta::Millis(500)); + EXPECT_THAT(DefaultWait().Until([&] { return ch.ports().size(); }, Eq(3)), IsRtcOk()); EXPECT_EQ(ch.ports()[0]->Type(), IceCandidateType::kHost); EXPECT_EQ(ch.ports()[1]->Type(), IceCandidateType::kRelay); @@ -5563,9 +5381,9 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestTcpTurn) { // Remote Relay candidate arrives. ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kRelay, "1.1.1.1", 1, 1)); - EXPECT_THAT(WaitUntil([&] { return ch.connections().size(); }, Eq(3), - {.timeout = kDefaultTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch.connections().size(); }, Eq(3)), + IsRtcOk()); // UDP Relay/Relay should be pinged first. VerifyNextPingableConnection(IceCandidateType::kRelay, @@ -5584,10 +5402,10 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestTcpTurn) { // when the address is a hostname. The destruction should happen even // if the channel is not destroyed. TEST(P2PTransportChannelResolverTest, HostnameCandidateIsResolved) { - const Environment env = CreateEnvironment(); + const Environment env = CreateTestEnvironment(); ResolverFactoryFixture resolver_fixture; std::unique_ptr socket_server = CreateDefaultSocketServer(); - AutoSocketServerThread main_thread(socket_server.get()); + test::RunLoop main_thread(socket_server.get()); FakePortAllocator allocator(env, socket_server.get()); IceTransportInit init(env); init.set_port_allocator(&allocator); @@ -5598,9 +5416,8 @@ TEST(P2PTransportChannelResolverTest, HostnameCandidateIsResolved) { hostname_candidate.set_address(hostname_address); channel->AddRemoteCandidate(hostname_candidate); - ASSERT_THAT(WaitUntil([&] { return channel->remote_candidates().size(); }, - Eq(1u), {.timeout = kDefaultTimeout}), - IsRtcOk()); + ASSERT_TRUE( + WaitUntil([&] { return channel->remote_candidates().size() == 1; })); const RemoteCandidate& candidate = channel->remote_candidates()[0]; EXPECT_FALSE(candidate.address().IsUnresolvedIP()); } @@ -5611,27 +5428,26 @@ TEST(P2PTransportChannelResolverTest, HostnameCandidateIsResolved) { // done. TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithMdnsName) { - const Environment env = CreateEnvironment(); // ep1 and ep2 will only gather host candidates with addresses // kPublicAddrs[0] and kPublicAddrs[1], respectively. - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); // ICE parameter will be set up when creating the channels. set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); - GetEndpoint(0)->network_manager_.set_mdns_responder( + GetEndpoint(0)->network_manager().set_mdns_responder( std::make_unique(Thread::Current())); ResolverFactoryFixture resolver_fixture; - GetEndpoint(1)->async_dns_resolver_factory_ = &resolver_fixture; - CreateChannels(env); + GetEndpoint(1)->set_async_dns_resolver_factory(&resolver_fixture); + CreateChannels(); // Pause sending candidates from both endpoints until we find out what port // number is assgined to ep1's host candidate. PauseCandidates(0); PauseCandidates(1); ASSERT_THAT( - WaitUntil([&] { return GetEndpoint(0)->saved_candidates_.size(); }, - Eq(1u), {.timeout = kMediumTimeout}), + DefaultWait().Until( + [&] { return GetEndpoint(0)->saved_candidates().size(); }, Eq(1u)), IsRtcOk()); - const auto& local_candidate = GetEndpoint(0)->saved_candidates_[0].candidate; + const auto& local_candidate = GetEndpoint(0)->saved_candidates()[0].candidate; // The IP address of ep1's host candidate should be obfuscated. EXPECT_TRUE(local_candidate.address().IsUnresolvedIP()); // This is the underlying private IP address of the same candidate at ep1. @@ -5642,18 +5458,19 @@ TEST_F(P2PTransportChannelTest, // pair and start to ping. After receiving the ping, ep2 discovers a prflx // remote candidate and form a candidate pair as well. ResumeCandidates(1); - ASSERT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); // ep2 should have the selected connection connected to the prflx remote // candidate. const Connection* selected_connection = nullptr; - ASSERT_THAT(WaitUntil( + ASSERT_THAT(MediumWait().Until( [&] { return selected_connection = ep2_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), + Ne(nullptr)), IsRtcOk()); EXPECT_TRUE(selected_connection->remote_candidate().is_prflx()); EXPECT_EQ(kIceUfrag[0], selected_connection->remote_candidate().username()); @@ -5662,11 +5479,9 @@ TEST_F(P2PTransportChannelTest, resolver_fixture.SetAddressToReturn(local_address); ResumeCandidates(0); // Verify ep2's selected connection is updated to use the 'local' candidate. - EXPECT_TRUE(WaitUntil( - [&] { - return ep2_ch1()->selected_connection()->remote_candidate().is_local(); - }, - {.timeout = kMediumTimeout})); + EXPECT_TRUE(MediumWait().Until([&] { + return ep2_ch1()->selected_connection()->remote_candidate().is_local(); + })); EXPECT_EQ(selected_connection, ep2_ch1()->selected_connection()); DestroyChannels(); @@ -5678,30 +5493,29 @@ TEST_F(P2PTransportChannelTest, // address after the resolution completes. TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateDuringResolvingHostCandidateWithMdnsName) { - const Environment env = CreateEnvironment(); ResolverFactoryFixture resolver_fixture; // Prevent resolution until triggered by FireDelayedResolution. resolver_fixture.DelayResolution(); // ep1 and ep2 will only gather host candidates with addresses // kPublicAddrs[0] and kPublicAddrs[1], respectively. - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); // ICE parameter will be set up when creating the channels. set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); - GetEndpoint(0)->network_manager_.set_mdns_responder( + GetEndpoint(0)->network_manager().set_mdns_responder( std::make_unique(Thread::Current())); - GetEndpoint(1)->async_dns_resolver_factory_ = &resolver_fixture; - CreateChannels(env); + GetEndpoint(1)->set_async_dns_resolver_factory(&resolver_fixture); + CreateChannels(); // Pause sending candidates from both endpoints until we find out what port // number is assgined to ep1's host candidate. PauseCandidates(0); PauseCandidates(1); ASSERT_THAT( - WaitUntil([&] { return GetEndpoint(0)->saved_candidates_.size(); }, - Eq(1u), {.timeout = kMediumTimeout}), + MediumWait().Until( + [&] { return GetEndpoint(0)->saved_candidates().size(); }, Eq(1u)), IsRtcOk()); - const auto& local_candidate = GetEndpoint(0)->saved_candidates_[0].candidate; + const auto& local_candidate = GetEndpoint(0)->saved_candidates()[0].candidate; // The IP address of ep1's host candidate should be obfuscated. ASSERT_TRUE(local_candidate.address().IsUnresolvedIP()); // This is the underlying private IP address of the same candidate at ep1. @@ -5713,9 +5527,10 @@ TEST_F(P2PTransportChannelTest, // by ep1. Let ep2 signal its host candidate with an IP address to ep1, so // that ep1 can form a candidate pair, select it and start to ping ep2. ResumeCandidates(1); - ASSERT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); // Let the mock resolver of ep2 receives the correct resolution. resolver_fixture.SetAddressToReturn(local_address); @@ -5724,20 +5539,19 @@ TEST_F(P2PTransportChannelTest, // // There is a caveat in our implementation associated with this expectation. // See the big comment in P2PTransportChannel::OnUnknownAddress. - ASSERT_THAT(WaitUntil([&] { return ep2_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + MediumWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); EXPECT_TRUE(ep2_ch1()->selected_connection()->remote_candidate().is_prflx()); // ep2 should also be able resolve the hostname candidate. The resolved remote // host candidate should be merged with the prflx remote candidate. resolver_fixture.FireDelayedResolution(); - EXPECT_TRUE(WaitUntil( - [&] { - return ep2_ch1()->selected_connection()->remote_candidate().is_local(); - }, - {.timeout = kMediumTimeout})); + EXPECT_TRUE(MediumWait().Until([&] { + return ep2_ch1()->selected_connection()->remote_candidate().is_local(); + })); EXPECT_EQ(1u, ep2_ch1()->remote_candidates().size()); DestroyChannels(); @@ -5747,28 +5561,27 @@ TEST_F(P2PTransportChannelTest, // which is obfuscated by an mDNS name, and if the peer can complete the name // resolution with the correct IP address, we can have a p2p connection. TEST_F(P2PTransportChannelTest, CanConnectWithHostCandidateWithMdnsName) { - const Environment env = CreateEnvironment(); ResolverFactoryFixture resolver_fixture; // ep1 and ep2 will only gather host candidates with addresses // kPublicAddrs[0] and kPublicAddrs[1], respectively. - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); // ICE parameter will be set up when creating the channels. set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); - GetEndpoint(0)->network_manager_.set_mdns_responder( + GetEndpoint(0)->network_manager().set_mdns_responder( std::make_unique(Thread::Current())); - GetEndpoint(1)->async_dns_resolver_factory_ = &resolver_fixture; - CreateChannels(env); + GetEndpoint(1)->set_async_dns_resolver_factory(&resolver_fixture); + CreateChannels(); // Pause sending candidates from both endpoints until we find out what port // number is assgined to ep1's host candidate. PauseCandidates(0); PauseCandidates(1); ASSERT_THAT( - WaitUntil([&] { return GetEndpoint(0)->saved_candidates_.size(); }, - Eq(1u), {.timeout = kMediumTimeout}), + MediumWait().Until( + [&] { return GetEndpoint(0)->saved_candidates().size(); }, Eq(1u)), IsRtcOk()); const auto& local_candidate_ep1 = - GetEndpoint(0)->saved_candidates_[0].candidate; + GetEndpoint(0)->saved_candidates()[0].candidate; // The IP address of ep1's host candidate should be obfuscated. EXPECT_TRUE(local_candidate_ep1.address().IsUnresolvedIP()); // This is the underlying private IP address of the same candidate at ep1, @@ -5782,9 +5595,10 @@ TEST_F(P2PTransportChannelTest, CanConnectWithHostCandidateWithMdnsName) { // We should be able to receive a ping from ep2 and establish a connection // with a peer reflexive candidate from ep2. - ASSERT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); EXPECT_TRUE(ep1_ch1()->selected_connection()->local_candidate().is_local()); EXPECT_TRUE(ep1_ch1()->selected_connection()->remote_candidate().is_prflx()); @@ -5799,36 +5613,35 @@ TEST_F(P2PTransportChannelTest, CanConnectWithHostCandidateWithMdnsName) { // this remote host candidate in stats. TEST_F(P2PTransportChannelTest, CandidatesSanitizedInStatsWhenMdnsObfuscationEnabled) { - const Environment env = CreateEnvironment(); ResolverFactoryFixture resolver_fixture; // ep1 and ep2 will gather host candidates with addresses // kPublicAddrs[0] and kPublicAddrs[1], respectively. ep1 also gathers a srflx // and a relay candidates. - ConfigureEndpoints(env, OPEN, OPEN, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags | PORTALLOCATOR_DISABLE_TCP, kOnlyLocalPorts); // ICE parameter will be set up when creating the channels. set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); - GetEndpoint(0)->network_manager_.set_mdns_responder( + GetEndpoint(0)->network_manager().set_mdns_responder( std::make_unique(Thread::Current())); - GetEndpoint(1)->async_dns_resolver_factory_ = &resolver_fixture; - CreateChannels(env); + GetEndpoint(1)->set_async_dns_resolver_factory(&resolver_fixture); + CreateChannels(); // Pause sending candidates from both endpoints until we find out what port // number is assigned to ep1's host candidate. PauseCandidates(0); PauseCandidates(1); // Ep1 has a UDP host, a srflx and a relay candidates. ASSERT_THAT( - WaitUntil([&] { return GetEndpoint(0)->saved_candidates_.size(); }, - Eq(3u), {.timeout = kMediumTimeout}), + MediumWait().Until( + [&] { return GetEndpoint(0)->saved_candidates().size(); }, Eq(3u)), IsRtcOk()); ASSERT_THAT( - WaitUntil([&] { return GetEndpoint(1)->saved_candidates_.size(); }, - Eq(1u), {.timeout = kMediumTimeout}), + MediumWait().Until( + [&] { return GetEndpoint(1)->saved_candidates().size(); }, Eq(1u)), IsRtcOk()); - for (const auto& candidates_data : GetEndpoint(0)->saved_candidates_) { + for (const auto& candidates_data : GetEndpoint(0)->saved_candidates()) { const auto& local_candidate_ep1 = candidates_data.candidate; if (local_candidate_ep1.is_local()) { // This is the underlying private IP address of the same candidate at ep1, @@ -5842,16 +5655,16 @@ TEST_F(P2PTransportChannelTest, ResumeCandidates(0); ResumeCandidates(1); - ASSERT_THAT(WaitUntil([&] { return ep1_ch1()->gathering_state(); }, - Eq(kIceGatheringComplete), {.timeout = kMediumTimeout}), + ASSERT_THAT(MediumWait().Until([&] { return ep1_ch1()->gathering_state(); }, + Eq(kIceGatheringComplete)), IsRtcOk()); // We should have the following candidate pairs on both endpoints: // ep1_host <-> ep2_host, ep1_srflx <-> ep2_host, ep1_relay <-> ep2_host - ASSERT_THAT(WaitUntil([&] { return ep1_ch1()->connections().size(); }, Eq(3u), - {.timeout = kMediumTimeout}), + ASSERT_THAT(MediumWait().Until( + [&] { return ep1_ch1()->connections().size(); }, Eq(3u)), IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ep2_ch1()->connections().size(); }, Eq(3u), - {.timeout = kMediumTimeout}), + ASSERT_THAT(MediumWait().Until( + [&] { return ep2_ch1()->connections().size(); }, Eq(3u)), IsRtcOk()); IceTransportStats ice_transport_stats1; @@ -5897,11 +5710,9 @@ TEST_F(P2PTransportChannelTest, TEST_F(P2PTransportChannelTest, ConnectingIncreasesSelectedCandidatePairChanges) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); - CreateChannels(env); + CreateChannels(); IceTransportStats ice_transport_stats; ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -5909,8 +5720,8 @@ TEST_F(P2PTransportChannelTest, // Let the channels connect. EXPECT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kMediumTimeout, .clock = &clock}), + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -5921,11 +5732,9 @@ TEST_F(P2PTransportChannelTest, TEST_F(P2PTransportChannelTest, DisconnectedIncreasesSelectedCandidatePairChanges) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); - CreateChannels(env); + CreateChannels(); IceTransportStats ice_transport_stats; ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -5933,8 +5742,8 @@ TEST_F(P2PTransportChannelTest, // Let the channels connect. EXPECT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kMediumTimeout, .clock = &clock}), + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -5945,8 +5754,8 @@ TEST_F(P2PTransportChannelTest, con->Prune(); } EXPECT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Eq(nullptr), - {.timeout = kMediumTimeout, .clock = &clock}), + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Eq(nullptr)), IsRtcOk()); ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -5957,11 +5766,9 @@ TEST_F(P2PTransportChannelTest, TEST_F(P2PTransportChannelTest, NewSelectionIncreasesSelectedCandidatePairChanges) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); - CreateChannels(env); + CreateChannels(); IceTransportStats ice_transport_stats; ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -5969,8 +5776,8 @@ TEST_F(P2PTransportChannelTest, // Let the channels connect. EXPECT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kMediumTimeout, .clock = &clock}), + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); @@ -5984,14 +5791,11 @@ TEST_F(P2PTransportChannelTest, con->Prune(); } } - EXPECT_TRUE( - WaitUntil( - [&] { - return ep1_ch1()->selected_connection() != nullptr && - (ep1_ch1()->GetStats(&ice_transport_stats), - ice_transport_stats.selected_candidate_pair_changes >= 2u); - }, - {.timeout = kMediumTimeout, .clock = &clock})); + EXPECT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->selected_connection() != nullptr && + (ep1_ch1()->GetStats(&ice_transport_stats), + ice_transport_stats.selected_candidate_pair_changes >= 2u); + })); ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); EXPECT_GE(ice_transport_stats.selected_candidate_pair_changes, 2u); @@ -6003,27 +5807,26 @@ TEST_F(P2PTransportChannelTest, // when it is queried via GetSelectedCandidatePair. TEST_F(P2PTransportChannelTest, SelectedCandidatePairSanitizedWhenMdnsObfuscationEnabled) { - const Environment env = CreateEnvironment(); ResolverFactoryFixture resolver_fixture; // ep1 and ep2 will gather host candidates with addresses // kPublicAddrs[0] and kPublicAddrs[1], respectively. - ConfigureEndpoints(env, OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); + ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); // ICE parameter will be set up when creating the channels. set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); - GetEndpoint(0)->network_manager_.set_mdns_responder( + GetEndpoint(0)->network_manager().set_mdns_responder( std::make_unique(Thread::Current())); - GetEndpoint(1)->async_dns_resolver_factory_ = &resolver_fixture; - CreateChannels(env); + GetEndpoint(1)->set_async_dns_resolver_factory(&resolver_fixture); + CreateChannels(); // Pause sending candidates from both endpoints until we find out what port // number is assigned to ep1's host candidate. PauseCandidates(0); PauseCandidates(1); ASSERT_THAT( - WaitUntil([&] { return GetEndpoint(0)->saved_candidates_.size(); }, - Eq(1u), {.timeout = kMediumTimeout}), + MediumWait().Until( + [&] { return GetEndpoint(0)->saved_candidates().size(); }, Eq(1u)), IsRtcOk()); - const auto& candidates_data = GetEndpoint(0)->saved_candidates_[0]; + const auto& candidates_data = GetEndpoint(0)->saved_candidates()[0]; const auto& local_candidate_ep1 = candidates_data.candidate; ASSERT_TRUE(local_candidate_ep1.is_local()); // This is the underlying private IP address of the same candidate at ep1, @@ -6035,12 +5838,10 @@ TEST_F(P2PTransportChannelTest, ResumeCandidates(0); ResumeCandidates(1); - ASSERT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection() != nullptr && - ep2_ch1()->selected_connection() != nullptr; - }, - {.timeout = kMediumTimeout})); + ASSERT_TRUE(MediumWait().Until([&] { + return ep1_ch1()->selected_connection() != nullptr && + ep2_ch1()->selected_connection() != nullptr; + })); const auto pair_ep1 = ep1_ch1()->GetSelectedCandidatePair(); ASSERT_TRUE(pair_ep1.has_value()); @@ -6057,23 +5858,20 @@ TEST_F(P2PTransportChannelTest, TEST_F(P2PTransportChannelTest, NoPairOfLocalRelayCandidateWithRemoteMdnsCandidate) { - const Environment env = CreateEnvironment(); const int kOnlyRelayPorts = PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_TCP; // We use one endpoint to test the behavior of adding remote candidates, and // this endpoint only gathers relay candidates. - ConfigureEndpoints(env, OPEN, OPEN, kOnlyRelayPorts, - kDefaultPortAllocatorFlags); - GetEndpoint(0)->cd1_.ch_ = CreateChannel( - env, 0, ICE_CANDIDATE_COMPONENT_DEFAULT, kIceParams[0], kIceParams[1]); + ConfigureEndpoints(OPEN, OPEN, kOnlyRelayPorts, kDefaultPortAllocatorFlags); + GetEndpoint(0)->cd1().set_ch(CreateChannel(0, ICE_CANDIDATE_COMPONENT_DEFAULT, + kIceParams[0], kIceParams[1])); IceConfig config; // Start gathering and we should have only a single relay port. ep1_ch1()->SetIceConfig(config); ep1_ch1()->MaybeStartGathering(); - EXPECT_THAT(WaitUntil([&] { return ep1_ch1()->gathering_state(); }, - Eq(IceGatheringState::kIceGatheringComplete), - {.timeout = kDefaultTimeout}), + EXPECT_THAT(MediumWait().Until([&] { return ep1_ch1()->gathering_state(); }, + Eq(IceGatheringState::kIceGatheringComplete)), IsRtcOk()); EXPECT_EQ(1u, ep1_ch1()->ports().size()); // Add a plain remote host candidate and three remote mDNS candidates with the @@ -6130,7 +5928,6 @@ class MockMdnsResponder : public MdnsResponderInterface { TEST_F(P2PTransportChannelTest, SrflxCandidateCanBeGatheredBeforeMdnsCandidateToCreateConnection) { - const Environment env = CreateEnvironment(); // ep1 and ep2 will only gather host and srflx candidates with base addresses // kPublicAddrs[0] and kPublicAddrs[1], respectively, and we use a shared // socket in gathering. @@ -6138,7 +5935,7 @@ TEST_F(P2PTransportChannelTest, PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_ENABLE_SHARED_SOCKET; // ep1 is configured with a NAT so that we do gather a srflx candidate. - ConfigureEndpoints(env, NAT_FULL_CONE, OPEN, kOnlyLocalAndStunPorts, + ConfigureEndpoints(NAT_FULL_CONE, OPEN, kOnlyLocalAndStunPorts, kOnlyLocalAndStunPorts); // ICE parameter will be set up when creating the channels. set_remote_ice_parameter_source(FROM_SETICEPARAMETERS); @@ -6148,15 +5945,16 @@ TEST_F(P2PTransportChannelTest, EXPECT_CALL(*mock_mdns_responder, CreateNameForAddress(_, _)) .Times(1) .WillOnce(Return()); - GetEndpoint(0)->network_manager_.set_mdns_responder( + GetEndpoint(0)->network_manager().set_mdns_responder( std::move(mock_mdns_responder)); - CreateChannels(env); + CreateChannels(); // We should be able to form a srflx-host connection to ep2. - ASSERT_THAT(WaitUntil([&] { return ep1_ch1()->selected_connection(); }, - Ne(nullptr), {.timeout = kMediumTimeout}), - IsRtcOk()); + ASSERT_THAT( + DefaultWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), + IsRtcOk()); EXPECT_TRUE(ep1_ch1()->selected_connection()->local_candidate().is_stun()); EXPECT_TRUE(ep1_ch1()->selected_connection()->remote_candidate().is_local()); @@ -6170,52 +5968,45 @@ TEST_F(P2PTransportChannelTest, // removed and are still usable for necessary route switching. TEST_F(P2PTransportChannelTest, SurfaceHostCandidateOnCandidateFilterChangeFromRelayToAll) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints( - env, OPEN, OPEN, + OPEN, OPEN, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET); auto* ep1 = GetEndpoint(0); auto* ep2 = GetEndpoint(1); - ep1->allocator_->SetCandidateFilter(CF_RELAY); - ep2->allocator_->SetCandidateFilter(CF_RELAY); + ep1->allocator()->SetCandidateFilter(CF_RELAY); + ep2->allocator()->SetCandidateFilter(CF_RELAY); // Enable continual gathering and also resurfacing gathered candidates upon // the candidate filter changed in the ICE configuration. IceConfig ice_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); ice_config.surface_ice_candidates_on_ice_transport_type_changed = true; - CreateChannels(env, ice_config, ice_config); + CreateChannels(ice_config, ice_config); ASSERT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + MediumWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); ASSERT_THAT( - WaitUntil([&] { return ep2_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); EXPECT_TRUE(ep1_ch1()->selected_connection()->local_candidate().is_relay()); EXPECT_TRUE(ep2_ch1()->selected_connection()->local_candidate().is_relay()); // Loosen the candidate filter at ep1. - ep1->allocator_->SetCandidateFilter(CF_ALL); - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection() != nullptr && - ep1_ch1()->selected_connection()->local_candidate().is_local(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ep1->allocator()->SetCandidateFilter(CF_ALL); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection() != nullptr && + ep1_ch1()->selected_connection()->local_candidate().is_local(); + })); EXPECT_TRUE(ep1_ch1()->selected_connection()->remote_candidate().is_relay()); // Loosen the candidate filter at ep2. - ep2->allocator_->SetCandidateFilter(CF_ALL); - EXPECT_TRUE(WaitUntil( - [&] { - return ep2_ch1()->selected_connection() != nullptr && - ep2_ch1()->selected_connection()->local_candidate().is_local(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ep2->allocator()->SetCandidateFilter(CF_ALL); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep2_ch1()->selected_connection() != nullptr && + ep2_ch1()->selected_connection()->local_candidate().is_local(); + })); // We have migrated to a host-host candidate pair. EXPECT_TRUE(ep2_ch1()->selected_connection()->remote_candidate().is_local()); @@ -6226,11 +6017,9 @@ TEST_F(P2PTransportChannelTest, fw()->AddRule(false, FP_ANY, kPublicAddrs[1], kTurnUdpExtAddr); // We should be able to reuse the previously gathered relay candidates. - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->local_candidate().is_relay(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection()->local_candidate().is_relay(); + })); EXPECT_TRUE(ep1_ch1()->selected_connection()->remote_candidate().is_relay()); DestroyChannels(); } @@ -6240,8 +6029,6 @@ TEST_F(P2PTransportChannelTest, // changing the candidate filter. TEST_F(P2PTransportChannelTest, SurfaceSrflxCandidateOnCandidateFilterChangeFromRelayToNoHost) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); // We need an actual NAT so that the host candidate is not equivalent to the // srflx candidate; otherwise, the host candidate would still surface even // though we disable it via the candidate filter below. This is a result of @@ -6250,46 +6037,42 @@ TEST_F(P2PTransportChannelTest, // 2. We keep the host candidate in this case in CheckCandidateFilter even // though we intend to filter them. ConfigureEndpoints( - env, NAT_FULL_CONE, NAT_FULL_CONE, + NAT_FULL_CONE, NAT_FULL_CONE, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET); auto* ep1 = GetEndpoint(0); auto* ep2 = GetEndpoint(1); - ep1->allocator_->SetCandidateFilter(CF_RELAY); - ep2->allocator_->SetCandidateFilter(CF_RELAY); + ep1->allocator()->SetCandidateFilter(CF_RELAY); + ep2->allocator()->SetCandidateFilter(CF_RELAY); // Enable continual gathering and also resurfacing gathered candidates upon // the candidate filter changed in the ICE configuration. IceConfig ice_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); ice_config.surface_ice_candidates_on_ice_transport_type_changed = true; - CreateChannels(env, ice_config, ice_config); + CreateChannels(ice_config, ice_config); ASSERT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); ASSERT_THAT( - WaitUntil([&] { return ep2_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); const uint32_t kCandidateFilterNoHost = CF_ALL & ~CF_HOST; // Loosen the candidate filter at ep1. - ep1->allocator_->SetCandidateFilter(kCandidateFilterNoHost); - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection() != nullptr && - ep1_ch1()->selected_connection()->local_candidate().is_stun(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ep1->allocator()->SetCandidateFilter(kCandidateFilterNoHost); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection() != nullptr && + ep1_ch1()->selected_connection()->local_candidate().is_stun(); + })); EXPECT_TRUE(ep1_ch1()->selected_connection()->remote_candidate().is_relay()); // Loosen the candidate filter at ep2. - ep2->allocator_->SetCandidateFilter(kCandidateFilterNoHost); - EXPECT_TRUE(WaitUntil( - [&] { - return ep2_ch1()->selected_connection() != nullptr && - ep2_ch1()->selected_connection()->local_candidate().is_stun(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ep2->allocator()->SetCandidateFilter(kCandidateFilterNoHost); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep2_ch1()->selected_connection() != nullptr && + ep2_ch1()->selected_connection()->local_candidate().is_stun(); + })); // We have migrated to a srflx-srflx candidate pair. EXPECT_TRUE(ep2_ch1()->selected_connection()->remote_candidate().is_stun()); @@ -6299,11 +6082,9 @@ TEST_F(P2PTransportChannelTest, fw()->AddRule(false, FP_ANY, kPrivateAddrs[0], kTurnUdpExtAddr); fw()->AddRule(false, FP_ANY, kPrivateAddrs[1], kTurnUdpExtAddr); // We should be able to reuse the previously gathered relay candidates. - EXPECT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection()->local_candidate().is_relay(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection()->local_candidate().is_relay(); + })); EXPECT_TRUE(ep1_ch1()->selected_connection()->remote_candidate().is_relay()); DestroyChannels(); } @@ -6315,37 +6096,34 @@ TEST_F(P2PTransportChannelTest, // gathering stopped. TEST_F(P2PTransportChannelTest, CannotSurfaceTheNewlyAllowedOnFilterChangeIfNotGatheringContinually) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints( - env, OPEN, OPEN, + OPEN, OPEN, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET); auto* ep1 = GetEndpoint(0); auto* ep2 = GetEndpoint(1); - ep1->allocator_->SetCandidateFilter(CF_RELAY); - ep2->allocator_->SetCandidateFilter(CF_RELAY); + ep1->allocator()->SetCandidateFilter(CF_RELAY); + ep2->allocator()->SetCandidateFilter(CF_RELAY); // Only gather once. IceConfig ice_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_ONCE); ice_config.surface_ice_candidates_on_ice_transport_type_changed = true; - CreateChannels(env, ice_config, ice_config); + CreateChannels(ice_config, ice_config); ASSERT_THAT( - WaitUntil([&] { return ep1_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until([&] { return ep1_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); ASSERT_THAT( - WaitUntil([&] { return ep2_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); // Loosen the candidate filter at ep1. - ep1->allocator_->SetCandidateFilter(CF_ALL); + ep1->allocator()->SetCandidateFilter(CF_ALL); // Wait for a period for any potential surfacing of new candidates. - SIMULATED_WAIT(false, kDefaultTimeout.ms(), clock); + time_controller_.AdvanceTime(kDefaultTimeout); EXPECT_TRUE(ep1_ch1()->selected_connection()->local_candidate().is_relay()); // Loosen the candidate filter at ep2. - ep2->allocator_->SetCandidateFilter(CF_ALL); + ep2->allocator()->SetCandidateFilter(CF_ALL); EXPECT_TRUE(ep2_ch1()->selected_connection()->local_candidate().is_relay()); DestroyChannels(); } @@ -6355,17 +6133,14 @@ TEST_F(P2PTransportChannelTest, // match the filter, are not removed. TEST_F(P2PTransportChannelTest, RestrictingCandidateFilterDoesNotRemoveRegatheredCandidates) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); - ConfigureEndpoints( - env, OPEN, OPEN, + OPEN, OPEN, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET); auto* ep1 = GetEndpoint(0); auto* ep2 = GetEndpoint(1); - ep1->allocator_->SetCandidateFilter(CF_ALL); - ep2->allocator_->SetCandidateFilter(CF_ALL); + ep1->allocator()->SetCandidateFilter(CF_ALL); + ep2->allocator()->SetCandidateFilter(CF_ALL); // Enable continual gathering and also resurfacing gathered candidates upon // the candidate filter changed in the ICE configuration. IceConfig ice_config = @@ -6376,32 +6151,23 @@ TEST_F(P2PTransportChannelTest, // gathering when we have a strongly connected candidate pair. PauseCandidates(0); PauseCandidates(1); - CreateChannels(env, ice_config, ice_config); + CreateChannels(ice_config, ice_config); // We have gathered host, srflx and relay candidates. - EXPECT_THAT(WaitUntil([&] { return ep1->saved_candidates_.size(); }, Eq(3u), - {.timeout = kDefaultTimeout, .clock = &clock}), + EXPECT_THAT(DefaultWait().Until( + [&] { return ep1->saved_candidates().size(); }, Eq(3u)), IsRtcOk()); ResumeCandidates(0); ResumeCandidates(1); - ASSERT_TRUE( - WaitUntil( - [&] { - return ep1_ch1()->selected_connection() != nullptr && - ep1_ch1() - ->selected_connection() - ->local_candidate() - .is_local() && - ep2_ch1()->selected_connection() != nullptr && - ep1_ch1() - ->selected_connection() - ->remote_candidate() - .is_local(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection() != nullptr && + ep1_ch1()->selected_connection()->local_candidate().is_local() && + ep2_ch1()->selected_connection() != nullptr && + ep1_ch1()->selected_connection()->remote_candidate().is_local(); + })); ASSERT_THAT( - WaitUntil([&] { return ep2_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); // Test that we have a host-host candidate pair selected and the number of // candidates signaled to the remote peer stays the same. @@ -6415,16 +6181,16 @@ TEST_F(P2PTransportChannelTest, test_invariants(); // Set a more restrictive candidate filter at ep1. - ep1->allocator_->SetCandidateFilter(CF_HOST | CF_REFLEXIVE); - SIMULATED_WAIT(false, kDefaultTimeout.ms(), clock); + ep1->allocator()->SetCandidateFilter(CF_HOST | CF_REFLEXIVE); + time_controller_.AdvanceTime(kDefaultTimeout); test_invariants(); - ep1->allocator_->SetCandidateFilter(CF_HOST); - SIMULATED_WAIT(false, kDefaultTimeout.ms(), clock); + ep1->allocator()->SetCandidateFilter(CF_HOST); + time_controller_.AdvanceTime(kDefaultTimeout); test_invariants(); - ep1->allocator_->SetCandidateFilter(CF_NONE); - SIMULATED_WAIT(false, kDefaultTimeout.ms(), clock); + ep1->allocator()->SetCandidateFilter(CF_NONE); + time_controller_.AdvanceTime(kDefaultTimeout); test_invariants(); DestroyChannels(); } @@ -6436,18 +6202,19 @@ TEST_F(P2PTransportChannelTest, // i.e surface_ice_candidates_on_ice_transport_type_changed requires // coordination outside of webrtc to function properly. TEST_F(P2PTransportChannelTest, SurfaceRequiresCoordination) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/")); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/"), + .time = &time_controller_}); ConfigureEndpoints( - env, OPEN, OPEN, + OPEN, OPEN, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET, kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET); auto* ep1 = GetEndpoint(0); auto* ep2 = GetEndpoint(1); - ep1->allocator_->SetCandidateFilter(CF_RELAY); - ep2->allocator_->SetCandidateFilter(CF_ALL); + ep1->allocator()->SetCandidateFilter(CF_RELAY); + ep2->allocator()->SetCandidateFilter(CF_ALL); // Enable continual gathering and also resurfacing gathered candidates upon // the candidate filter changed in the ICE configuration. IceConfig ice_config = @@ -6458,47 +6225,38 @@ TEST_F(P2PTransportChannelTest, SurfaceRequiresCoordination) { // gathering when we have a strongly connected candidate pair. PauseCandidates(0); PauseCandidates(1); - CreateChannels(env, ice_config, ice_config); + CreateChannels(ice_config, ice_config); // On the caller we only have relay, // on the callee we have host, srflx and relay. - EXPECT_THAT(WaitUntil([&] { return ep1->saved_candidates_.size(); }, Eq(1u), - {.timeout = kDefaultTimeout, .clock = &clock}), + EXPECT_THAT(DefaultWait().Until( + [&] { return ep1->saved_candidates().size(); }, Eq(1u)), IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return ep2->saved_candidates_.size(); }, Eq(3u), - {.timeout = kDefaultTimeout, .clock = &clock}), + EXPECT_THAT(DefaultWait().Until( + [&] { return ep2->saved_candidates().size(); }, Eq(3u)), IsRtcOk()); ResumeCandidates(0); ResumeCandidates(1); - ASSERT_TRUE( - WaitUntil( - [&] { - return ep1_ch1()->selected_connection() != nullptr && - ep1_ch1() - ->selected_connection() - ->local_candidate() - .is_relay() && - ep2_ch1()->selected_connection() != nullptr && - ep1_ch1() - ->selected_connection() - ->remote_candidate() - .is_relay(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection() != nullptr && + ep1_ch1()->selected_connection()->local_candidate().is_relay() && + ep2_ch1()->selected_connection() != nullptr && + ep1_ch1()->selected_connection()->remote_candidate().is_relay(); + })); ASSERT_THAT( - WaitUntil([&] { return ep2_ch1()->selected_connection(); }, Ne(nullptr), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until([&] { return ep2_ch1()->selected_connection(); }, + Ne(nullptr)), IsRtcOk()); // Wait until the callee discards it's candidates // since they don't manage to connect. - SIMULATED_WAIT(false, 300000, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(300000)); // And then loosen caller candidate filter. - ep1->allocator_->SetCandidateFilter(CF_ALL); + ep1->allocator()->SetCandidateFilter(CF_ALL); - SIMULATED_WAIT(false, kDefaultTimeout.ms(), clock); + time_controller_.AdvanceTime(kDefaultTimeout); // No p2p connection will be made, it will remain on relay. EXPECT_TRUE(ep1_ch1()->selected_connection() != nullptr && @@ -6510,122 +6268,118 @@ TEST_F(P2PTransportChannelTest, SurfaceRequiresCoordination) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening0) { - constexpr int kMargin = 10; - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/initial_select_dampening:0/")); - - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/initial_select_dampening:0/"), + .time = &time_controller_}); + + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 100)); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(nullptr, ch.selected_connection()); conn1->ReceivedPingResponse(kLowRtt, "id"); // Becomes writable and receiving // It shall not be selected until 0ms has passed....i.e it should be connected // directly. EXPECT_THAT( - WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = TimeDelta::Millis(kMargin), .clock = &clock}), + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), IsRtcOk()); } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening) { - constexpr int kMargin = 10; - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/initial_select_dampening:100/")); - - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/initial_select_dampening:100/"), + .time = &time_controller_}); + + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 100)); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(nullptr, ch.selected_connection()); conn1->ReceivedPingResponse(kLowRtt, "id"); // Becomes writable and receiving // It shall not be selected until 100ms has passed. - SIMULATED_WAIT(conn1 == ch.selected_connection(), 100 - kMargin, clock); + (void)DefaultWait().Until([&] { return conn1 == ch.selected_connection(); }); EXPECT_THAT( - WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = TimeDelta::Millis(2 * kMargin), .clock = &clock}), + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), IsRtcOk()); } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningPingReceived) { - constexpr int kMargin = 10; - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/initial_select_dampening_ping_received:100/")); - - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/initial_select_dampening_ping_received:100/"), + .time = &time_controller_}); + + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 100)); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(nullptr, ch.selected_connection()); conn1->ReceivedPingResponse(kLowRtt, "id"); // Becomes writable and receiving conn1->ReceivedPing("id1"); // // It shall not be selected until 100ms has passed. - SIMULATED_WAIT(conn1 == ch.selected_connection(), 100 - kMargin, clock); + (void)DefaultWait().Until([&] { return conn1 == ch.selected_connection(); }); EXPECT_THAT( - WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = TimeDelta::Millis(2 * kMargin), .clock = &clock}), + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), IsRtcOk()); } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningBoth) { - constexpr int kMargin = 10; - ScopedFakeClock clock; - clock.AdvanceTime(TimeDelta::Seconds(1)); - const Environment env = CreateEnvironment(CreateTestFieldTrialsPtr( - "WebRTC-IceFieldTrials/" - "initial_select_dampening:100,initial_select_dampening_ping_received:" - "50/")); - - FakePortAllocator pa(env, ss()); - P2PTransportChannel ch(env, "test channel", 1, &pa); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); + env_ = CreateTestEnvironment({.field_trials = CreateTestFieldTrialsPtr( + "WebRTC-IceFieldTrials/" + "initial_select_dampening:100,initial_" + "select_dampening_ping_received:" + "50/"), + .time = &time_controller_}); + + FakePortAllocator pa(env_, ss()); + P2PTransportChannel ch(env_, "test channel", 1, &pa); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); ch.AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "1.1.1.1", 1, 100)); - Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock); + Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1); ASSERT_TRUE(conn1 != nullptr); EXPECT_EQ(nullptr, ch.selected_connection()); conn1->ReceivedPingResponse(kLowRtt, "id"); // Becomes writable and receiving // It shall not be selected until 100ms has passed....but only wait ~50 now. - SIMULATED_WAIT(conn1 == ch.selected_connection(), 50 - kMargin, clock); + (void)DefaultWait().Until([&] { return conn1 == ch.selected_connection(); }); // Now receiving ping and new timeout should kick in. conn1->ReceivedPing("id1"); // EXPECT_THAT( - WaitUntil([&] { return ch.selected_connection(); }, Eq(conn1), - {.timeout = TimeDelta::Millis(2 * kMargin), .clock = &clock}), + DefaultWait().Until([&] { return ch.selected_connection(); }, Eq(conn1)), IsRtcOk()); } TEST(P2PTransportChannelIceControllerTest, InjectIceController) { - const Environment env = CreateEnvironment(); + const Environment env = CreateTestEnvironment(); std::unique_ptr socket_server = CreateDefaultSocketServer(); - AutoSocketServerThread main_thread(socket_server.get()); + test::RunLoop main_thread(socket_server.get()); MockIceControllerFactory factory; FakePortAllocator pa(env, socket_server.get()); EXPECT_CALL(factory, RecordIceControllerCreated()).Times(1); @@ -6638,9 +6392,9 @@ TEST(P2PTransportChannelIceControllerTest, InjectIceController) { } TEST(P2PTransportChannel, InjectActiveIceController) { - const Environment env = CreateEnvironment(); + const Environment env = CreateTestEnvironment(); std::unique_ptr socket_server = CreateDefaultSocketServer(); - AutoSocketServerThread main_thread(socket_server.get()); + test::RunLoop main_thread(socket_server.get()); MockActiveIceControllerFactory factory; FakePortAllocator pa(env, socket_server.get()); EXPECT_CALL(factory, RecordActiveIceControllerCreated()).Times(1); @@ -6693,10 +6447,9 @@ class ForgetLearnedStateControllerFactory }; TEST_F(P2PTransportChannelPingTest, TestForgetLearnedState) { - const Environment env = CreateEnvironment(); ForgetLearnedStateControllerFactory factory; - FakePortAllocator pa(env, ss()); - IceTransportInit init(env); + FakePortAllocator pa(env_, ss()); + IceTransportInit init(env_); init.set_port_allocator(&pa); init.set_ice_controller_factory(&factory); auto ch = @@ -6716,9 +6469,9 @@ TEST_F(P2PTransportChannelPingTest, TestForgetLearnedState) { // Wait for conn1 to be selected. conn1->ReceivedPingResponse(kLowRtt, "id"); - EXPECT_THAT(WaitUntil([&] { return ch->selected_connection(); }, Eq(conn1), - {.timeout = kMediumTimeout}), - IsRtcOk()); + EXPECT_THAT( + DefaultWait().Until([&] { return ch->selected_connection(); }, Eq(conn1)), + IsRtcOk()); conn2->ReceivedPingResponse(kLowRtt, "id"); EXPECT_TRUE(conn2->writable()); @@ -6730,16 +6483,14 @@ TEST_F(P2PTransportChannelPingTest, TestForgetLearnedState) { // We don't have a mock Connection, so verify this by checking that it // is no longer writable. - EXPECT_TRUE(WaitUntil([&] { return !conn2->writable(); }, - {.timeout = kMediumTimeout})); + EXPECT_TRUE(MediumWait().Until([&] { return !conn2->writable(); })); } TEST_F(P2PTransportChannelTest, DisableDnsLookupsWithTransportPolicyRelay) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); auto* ep1 = GetEndpoint(0); - ep1->allocator_->SetCandidateFilter(CF_RELAY); + ep1->allocator()->SetCandidateFilter(CF_RELAY); std::unique_ptr mock_async_resolver = std::make_unique(); @@ -6751,9 +6502,9 @@ TEST_F(P2PTransportChannelTest, DisableDnsLookupsWithTransportPolicyRelay) { .WillByDefault( [&mock_async_resolver]() { return std::move(mock_async_resolver); }); - ep1->async_dns_resolver_factory_ = &mock_async_resolver_factory; + ep1->set_async_dns_resolver_factory(&mock_async_resolver_factory); - CreateChannels(env); + CreateChannels(); ep1_ch1()->AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "hostname.test", 1, 100)); @@ -6762,11 +6513,10 @@ TEST_F(P2PTransportChannelTest, DisableDnsLookupsWithTransportPolicyRelay) { } TEST_F(P2PTransportChannelTest, DisableDnsLookupsWithTransportPolicyNone) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); auto* ep1 = GetEndpoint(0); - ep1->allocator_->SetCandidateFilter(CF_NONE); + ep1->allocator()->SetCandidateFilter(CF_NONE); std::unique_ptr mock_async_resolver = std::make_unique(); @@ -6778,9 +6528,9 @@ TEST_F(P2PTransportChannelTest, DisableDnsLookupsWithTransportPolicyNone) { .WillByDefault( [&mock_async_resolver]() { return std::move(mock_async_resolver); }); - ep1->async_dns_resolver_factory_ = &mock_async_resolver_factory; + ep1->set_async_dns_resolver_factory(&mock_async_resolver_factory); - CreateChannels(env); + CreateChannels(); ep1_ch1()->AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "hostname.test", 1, 100)); @@ -6789,11 +6539,10 @@ TEST_F(P2PTransportChannelTest, DisableDnsLookupsWithTransportPolicyNone) { } TEST_F(P2PTransportChannelTest, EnableDnsLookupsWithTransportPolicyNoHost) { - const Environment env = CreateEnvironment(); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); auto* ep1 = GetEndpoint(0); - ep1->allocator_->SetCandidateFilter(CF_ALL & ~CF_HOST); + ep1->allocator()->SetCandidateFilter(CF_ALL & ~CF_HOST); std::unique_ptr mock_async_resolver = std::make_unique(); @@ -6806,9 +6555,9 @@ TEST_F(P2PTransportChannelTest, EnableDnsLookupsWithTransportPolicyNoHost) { .WillOnce( [&mock_async_resolver]() { return std::move(mock_async_resolver); }); - ep1->async_dns_resolver_factory_ = &mock_async_resolver_factory; + ep1->set_async_dns_resolver_factory(&mock_async_resolver_factory); - CreateChannels(env); + CreateChannels(); ep1_ch1()->AddRemoteCandidate( CreateUdpCandidate(IceCandidateType::kHost, "hostname.test", 1, 100)); @@ -6834,13 +6583,11 @@ class LocalNetworkAccessPermissionTest TEST_P(LocalNetworkAccessPermissionTest, LiteralAddresses) { const auto [address, lna_fake_result] = GetParam(); - - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); + FakePortAllocator pa(env_, ss()); FakeLocalNetworkAccessPermissionFactory lna_permission_factory( lna_fake_result); - IceTransportInit init(env); + IceTransportInit init(env_); init.set_port_allocator(&pa); init.set_lna_permission_factory(&lna_permission_factory); @@ -6852,8 +6599,8 @@ TEST_P(LocalNetworkAccessPermissionTest, LiteralAddresses) { CreateUdpCandidate(IceCandidateType::kHost, address, 5000, 1)); ASSERT_THAT( - WaitUntil([&] { return ch->PermissionQueriesOutstandingForTesting(); }, - Eq(0)), + DefaultWait().Until( + [&] { return ch->PermissionQueriesOutstandingForTesting(); }, Eq(0)), IsRtcOk()); if (lna_fake_result == LnaFakeResult::kPermissionNotNeeded || lna_fake_result == LnaFakeResult::kPermissionGranted) { @@ -6865,16 +6612,14 @@ TEST_P(LocalNetworkAccessPermissionTest, LiteralAddresses) { TEST_P(LocalNetworkAccessPermissionTest, UnresolvedAddresses) { const auto [address, lna_fake_result] = GetParam(); - - const Environment env = CreateEnvironment(); - FakePortAllocator pa(env, ss()); + FakePortAllocator pa(env_, ss()); FakeLocalNetworkAccessPermissionFactory lna_permission_factory( lna_fake_result); ResolverFactoryFixture resolver_fixture; resolver_fixture.SetAddressToReturn({address, 5000}); - IceTransportInit init(env); + IceTransportInit init(env_); init.set_port_allocator(&pa); init.set_lna_permission_factory(&lna_permission_factory); init.set_async_dns_resolver_factory(&resolver_fixture); @@ -6887,8 +6632,8 @@ TEST_P(LocalNetworkAccessPermissionTest, UnresolvedAddresses) { CreateUdpCandidate(IceCandidateType::kHost, "fake.test", 5000, 1)); ASSERT_THAT( - WaitUntil([&] { return ch->PermissionQueriesOutstandingForTesting(); }, - Eq(0)), + DefaultWait().Until( + [&] { return ch->PermissionQueriesOutstandingForTesting(); }, Eq(0)), IsRtcOk()); if (lna_fake_result == LnaFakeResult::kPermissionNotNeeded || lna_fake_result == LnaFakeResult::kPermissionGranted) { @@ -6917,18 +6662,18 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnected) { std::string("WebRTC-IceFieldTrials/stop_gather_on_strongly_connected:") + (stop_gather_on_strongly_connected ? "true/" : "false/"); - ScopedFakeClock clock; - const Environment env = - CreateEnvironment(CreateTestFieldTrialsPtr(field_trial)); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr(field_trial), + .time = &time_controller_}); // Use local + relay constexpr uint32_t flags = kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET | PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_TCP; - ConfigureEndpoints(env, OPEN, OPEN, flags, flags); + ConfigureEndpoints(OPEN, OPEN, flags, flags); auto* ep1 = GetEndpoint(0); auto* ep2 = GetEndpoint(1); - ep1->allocator_->SetCandidateFilter(CF_ALL); - ep2->allocator_->SetCandidateFilter(CF_ALL); + ep1->allocator()->SetCandidateFilter(CF_ALL); + ep2->allocator()->SetCandidateFilter(CF_ALL); // Use step delay 3s which is long enough for // connection to be established before managing to gather relay candidates. @@ -6937,18 +6682,16 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnected) { SetAllocationStepDelay(1, delay); IceConfig ice_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); - CreateChannels(env, ice_config, ice_config); + CreateChannels(ice_config, ice_config); PauseCandidates(0); PauseCandidates(1); // We have gathered host candidates but not relay. - ASSERT_TRUE(WaitUntil( - [&] { - return ep1->saved_candidates_.size() == 1u && - ep2->saved_candidates_.size() == 1u; - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1->saved_candidates().size() == 1u && + ep2->saved_candidates().size() == 1u; + })); ResumeCandidates(0); ResumeCandidates(1); @@ -6956,30 +6699,25 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnected) { PauseCandidates(0); PauseCandidates(1); - ASSERT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->remote_candidates().size() == 1 && - ep2_ch1()->remote_candidates().size() == 1; - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->remote_candidates().size() == 1 && + ep2_ch1()->remote_candidates().size() == 1; + })); - ASSERT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection() && - ep2_ch1()->selected_connection(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection(); + })); - clock.AdvanceTime(TimeDelta::Millis(10 * delay)); + time_controller_.AdvanceTime(TimeDelta::Millis(10 * delay)); if (stop_gather_on_strongly_connected) { // The relay candidates gathered has not been propagated to channel. - EXPECT_EQ(ep1->saved_candidates_.size(), 0u); - EXPECT_EQ(ep2->saved_candidates_.size(), 0u); + EXPECT_EQ(ep1->saved_candidates().size(), 0u); + EXPECT_EQ(ep2->saved_candidates().size(), 0u); } else { // The relay candidates gathered has been propagated to channel. - EXPECT_EQ(ep1->saved_candidates_.size(), 1u); - EXPECT_EQ(ep2->saved_candidates_.size(), 1u); + EXPECT_EQ(ep1->saved_candidates().size(), 1u); + EXPECT_EQ(ep2->saved_candidates().size(), 1u); } } @@ -6989,20 +6727,20 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnectedMultiHomed) { std::string("WebRTC-IceFieldTrials/stop_gather_on_strongly_connected:") + (stop_gather_on_strongly_connected ? "true/" : "false/"); - ScopedFakeClock clock; - const Environment env = - CreateEnvironment(CreateTestFieldTrialsPtr(field_trial)); + env_ = CreateTestEnvironment( + {.field_trials = CreateTestFieldTrialsPtr(field_trial), + .time = &time_controller_}); // Use local + relay constexpr uint32_t flags = kDefaultPortAllocatorFlags | PORTALLOCATOR_ENABLE_SHARED_SOCKET | PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_TCP; AddAddress(0, kAlternateAddrs[0]); - ConfigureEndpoints(env, OPEN, OPEN, flags, flags); + ConfigureEndpoints(OPEN, OPEN, flags, flags); auto* ep1 = GetEndpoint(0); auto* ep2 = GetEndpoint(1); - ep1->allocator_->SetCandidateFilter(CF_ALL); - ep2->allocator_->SetCandidateFilter(CF_ALL); + ep1->allocator()->SetCandidateFilter(CF_ALL); + ep2->allocator()->SetCandidateFilter(CF_ALL); // Use step delay 3s which is long enough for // connection to be established before managing to gather relay candidates. @@ -7011,18 +6749,16 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnectedMultiHomed) { SetAllocationStepDelay(1, delay); IceConfig ice_config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); - CreateChannels(env, ice_config, ice_config); + CreateChannels(ice_config, ice_config); PauseCandidates(0); PauseCandidates(1); // We have gathered host candidates but not relay. - ASSERT_TRUE(WaitUntil( - [&] { - return ep1->saved_candidates_.size() == 2u && - ep2->saved_candidates_.size() == 1u; - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1->saved_candidates().size() == 2u && + ep2->saved_candidates().size() == 1u; + })); ResumeCandidates(0); ResumeCandidates(1); @@ -7030,48 +6766,41 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnectedMultiHomed) { PauseCandidates(0); PauseCandidates(1); - ASSERT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->remote_candidates().size() == 1 && - ep2_ch1()->remote_candidates().size() == 2; - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->remote_candidates().size() == 1 && + ep2_ch1()->remote_candidates().size() == 2; + })); - ASSERT_TRUE(WaitUntil( - [&] { - return ep1_ch1()->selected_connection() && - ep2_ch1()->selected_connection(); - }, - {.timeout = kDefaultTimeout, .clock = &clock})); + ASSERT_TRUE(DefaultWait().Until([&] { + return ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection(); + })); - clock.AdvanceTime(TimeDelta::Millis(10 * delay)); + time_controller_.AdvanceTime(TimeDelta::Millis(10 * delay)); if (stop_gather_on_strongly_connected) { // The relay candidates gathered has not been propagated to channel. - EXPECT_EQ(ep1->saved_candidates_.size(), 0u); - EXPECT_EQ(ep2->saved_candidates_.size(), 0u); + EXPECT_EQ(ep1->saved_candidates().size(), 0u); + EXPECT_EQ(ep2->saved_candidates().size(), 0u); } else { // The relay candidates gathered has been propagated. - EXPECT_EQ(ep1->saved_candidates_.size(), 2u); - EXPECT_EQ(ep2->saved_candidates_.size(), 1u); + EXPECT_EQ(ep1->saved_candidates().size(), 2u); + EXPECT_EQ(ep2->saved_candidates().size(), 1u); } } // Tests no candidates are generated with old ice ufrag/passwd after an ice // restart even if continual gathering is enabled. TEST_F(P2PTransportChannelTest, TestIceNoOldCandidatesAfterIceRestart) { - ScopedFakeClock clock; - const Environment env = CreateEnvironment(); AddAddress(0, kAlternateAddrs[0]); - ConfigureEndpoints(env, OPEN, OPEN, kDefaultPortAllocatorFlags, + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, kDefaultPortAllocatorFlags); // gathers continually. IceConfig config = CreateIceConfig(TimeDelta::Seconds(1), GATHER_CONTINUALLY); - CreateChannels(env, config, config); + CreateChannels(config, config); - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); PauseCandidates(0); @@ -7079,11 +6808,11 @@ TEST_F(P2PTransportChannelTest, TestIceNoOldCandidatesAfterIceRestart) { ep1_ch1()->MaybeStartGathering(); EXPECT_THAT( - WaitUntil([&] { return GetEndpoint(0)->saved_candidates_.size(); }, Gt(0), - {.timeout = kDefaultTimeout, .clock = &clock}), + DefaultWait().Until( + [&] { return GetEndpoint(0)->saved_candidates().size(); }, Gt(0)), IsRtcOk()); - for (const auto& cd : GetEndpoint(0)->saved_candidates_) { + for (const auto& cd : GetEndpoint(0)->saved_candidates()) { EXPECT_EQ(cd.candidate.username(), kIceUfrag[3]); } @@ -7104,13 +6833,12 @@ class P2PTransportChannelTestDtlsInStun : public P2PTransportChannelTestBase { protected: void Run(bool ep1_support, bool ep2_support) { - const Environment env = CreateEnvironment(); - CreatePortAllocators(env); + CreatePortAllocators(); IceConfig ep1_config; ep1_config.dtls_handshake_in_stun = ep1_support; IceConfig ep2_config; ep2_config.dtls_handshake_in_stun = ep2_support; - CreateChannels(env, ep1_config, ep2_config); + CreateChannels(ep1_config, ep2_config); if (ep1_support) { ep1_ch1()->SetDtlsStunPiggybackCallbacks(DtlsStunPiggybackCallbacks( [&](auto type) { return data_to_piggyback_func(type); }, @@ -7121,8 +6849,8 @@ class P2PTransportChannelTestDtlsInStun : public P2PTransportChannelTestBase { [&](auto type) { return data_to_piggyback_func(type); }, [&](auto data, auto ack) { piggyback_data_received(data, ack); })); } - EXPECT_TRUE(WaitUntil([&] { return CheckConnected(ep1_ch1(), ep2_ch1()); }, - {.timeout = kDefaultTimeout, .clock = &clock_})); + EXPECT_TRUE(DefaultWait().Until( + [&] { return CheckConnected(ep1_ch1(), ep2_ch1()); })); DestroyChannels(); } @@ -7132,10 +6860,9 @@ class P2PTransportChannelTestDtlsInStun : public P2PTransportChannelTestBase { return make_pair(absl::string_view(pending_packet_), std::nullopt); } - void piggyback_data_received(std::optional> data, + void piggyback_data_received(std::optional> data, std::optional> ack) {} - ScopedFakeClock clock_; Buffer pending_packet_; }; @@ -7159,7 +6886,8 @@ class P2PTransportChannelRegatheringTest : public P2PTransportChannelPingTest { public: void SetupChannel(IceConfig config) { // Use a fixed environment for the allocator and channel. - env_ = std::make_unique(CreateEnvironment()); + env_ = std::make_unique( + CreateTestEnvironment({.time = time_controller_.GetClock()})); allocator_ = std::make_unique(*env_, ss()); channel_ = std::make_unique(*env_, "regather", 1, allocator_.get()); @@ -7196,7 +6924,6 @@ class P2PTransportChannelRegatheringTest : public P2PTransportChannelPingTest { TEST_F(P2PTransportChannelRegatheringTest, IceRegatheringDoesNotOccurIfSessionNotCleared) { - ScopedFakeClock clock; IceConfig config; config.regather_on_failed_networks_interval = TimeDelta::Millis(2000); SetupChannel(config); @@ -7205,13 +6932,11 @@ TEST_F(P2PTransportChannelRegatheringTest, // Expect no regathering in the last 10s. // We use WaitUntil with a condition that is always false to wait for timeout, // but we expect it to fail (return false). - EXPECT_FALSE(WaitUntil([&] { return false; }, - {.timeout = TimeDelta::Seconds(10), .clock = &clock})); + EXPECT_FALSE(DefaultWait().Until([&] { return false; })); EXPECT_EQ(0, GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE)); } TEST_F(P2PTransportChannelRegatheringTest, IceRegatheringRepeatsAsScheduled) { - ScopedFakeClock clock; IceConfig config; config.regather_on_failed_networks_interval = TimeDelta::Millis(2000); SetupChannel(config); @@ -7219,30 +6944,23 @@ TEST_F(P2PTransportChannelRegatheringTest, IceRegatheringRepeatsAsScheduled) { channel_->allocator_session()->ClearGettingPorts(); // Expect no regathering immediately (wait < 2000ms). - EXPECT_FALSE( - WaitUntil([&] { return false; }, - {.timeout = TimeDelta::Millis(1900), .clock = &clock})); + EXPECT_FALSE(Wait(TimeDelta::Millis(1900)).Until([&] { return false; })); EXPECT_EQ(0, GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE)); // Expect regathering to happen once after 2s. - EXPECT_TRUE(WaitUntil( - [&] { - return GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE) == 1; - }, - {.timeout = TimeDelta::Millis(500), .clock = &clock})); + EXPECT_TRUE(DefaultWait().Until([&] { + return GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE) == 1; + })); // Expect regathering to happen for another 5 times in 11s with 2s interval. // Total 6. - EXPECT_TRUE(WaitUntil( - [&] { - return GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE) == 6; - }, - {.timeout = TimeDelta::Seconds(11), .clock = &clock})); + EXPECT_TRUE(Wait(TimeDelta::Seconds(11)).Until([&] { + return GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE) == 6; + })); } TEST_F(P2PTransportChannelRegatheringTest, ScheduleOfIceRegatheringOnFailedNetworksCanBeReplaced) { - ScopedFakeClock clock; IceConfig config; config.regather_on_failed_networks_interval = TimeDelta::Millis(2000); SetupChannel(config); @@ -7254,17 +6972,14 @@ TEST_F(P2PTransportChannelRegatheringTest, // Expect no regathering from the previous schedule (Wait 3s, previous was // 2s). Since we reset the schedule, it should restart counting 5s from now. - EXPECT_FALSE(WaitUntil([&] { return false; }, - {.timeout = TimeDelta::Seconds(3), .clock = &clock})); + EXPECT_FALSE(MediumWait().Until([&] { return false; })); EXPECT_EQ(0, GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE)); // Expect regathering to happen twice in the last 11s (total 14s) with 5s // interval. First at 5s, second at 10s. - EXPECT_TRUE(WaitUntil( - [&] { - return GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE) == 2; - }, - {.timeout = TimeDelta::Seconds(8), .clock = &clock})); + EXPECT_TRUE(Wait(TimeDelta::Seconds(8)).Until([&] { + return GetRegatheringCount(IceRegatheringReason::NETWORK_FAILURE) == 2; + })); } } // namespace diff --git a/p2p/base/port.cc b/p2p/base/port.cc index 932ff549459..e7ef807bd53 100644 --- a/p2p/base/port.cc +++ b/p2p/base/port.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,6 @@ #include "absl/functional/any_invocable.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/local_network_access_permission.h" #include "api/sequence_checker.h" @@ -468,8 +468,7 @@ bool Port::GetStunMessage(const char* data, // Parse the request message. If the packet is not a complete and correct // STUN message, then ignore it. std::unique_ptr stun_msg(new IceMessage()); - ByteBufferReader buf( - MakeArrayView(reinterpret_cast(data), size)); + ByteBufferReader buf(std::span(reinterpret_cast(data), size)); if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { return false; } diff --git a/p2p/base/port_allocator_unittest.cc b/p2p/base/port_allocator_unittest.cc index 9e68874f52b..10f9d2a478e 100644 --- a/p2p/base/port_allocator_unittest.cc +++ b/p2p/base/port_allocator_unittest.cc @@ -22,9 +22,9 @@ #include "rtc_base/ip_address.h" #include "rtc_base/net_helper.h" #include "rtc_base/socket_address.h" -#include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/gtest.h" +#include "test/run_loop.h" using ::webrtc::CreateEnvironment; using ::webrtc::IceCandidateType; @@ -98,7 +98,7 @@ class PortAllocatorTest : public ::testing::Test { } std::unique_ptr vss_; - webrtc::AutoSocketServerThread main_; + webrtc::test::RunLoop main_; std::unique_ptr allocator_; webrtc::SocketAddress stun_server_1{"11.11.11.11", 3478}; webrtc::SocketAddress stun_server_2{"22.22.22.22", 3478}; diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc index d144ea6efef..45d1d903974 100644 --- a/p2p/base/port_unittest.cc +++ b/p2p/base/port_unittest.cc @@ -22,10 +22,9 @@ #include "absl/memory/memory.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" +#include "api/async_dns_resolver.h" #include "api/candidate.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/field_trials.h" #include "api/packet_socket_factory.h" #include "api/test/mock_packet_socket_factory.h" @@ -56,8 +55,6 @@ #include "rtc_base/checks.h" #include "rtc_base/crypto_random.h" #include "rtc_base/dscp.h" -#include "rtc_base/fake_clock.h" -#include "rtc_base/gunit.h" #include "rtc_base/logging.h" #include "rtc_base/net_helper.h" #include "rtc_base/network.h" @@ -65,12 +62,14 @@ #include "rtc_base/network_constants.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" +#include "rtc_base/system/unused.h" #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/create_test_environment.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" namespace webrtc { @@ -82,8 +81,8 @@ using ::testing::IsTrue; using ::testing::NotNull; using ::testing::Return; -constexpr int kDefaultTimeout = 3000; -constexpr int kShortTimeout = 1000; +constexpr TimeDelta kDefaultTimeout = TimeDelta::Millis(3000); +constexpr TimeDelta kShortTimeout = TimeDelta::Millis(1000); constexpr TimeDelta kMaxExpectedSimulatedRtt = TimeDelta::Millis(200); constexpr TimeDelta kEpsilon = TimeDelta::Millis(1); const SocketAddress kLocalAddr1("192.168.1.2", 0); @@ -132,7 +131,6 @@ bool WriteStunMessage(const StunMessage& msg, ByteBufferWriter* buf) { return msg.Write(buf); } - bool GetStunMessageFromBufferWriter(TestPort* port, ByteBufferWriter* buf, const SocketAddress& addr, @@ -146,20 +144,22 @@ void SendPingAndReceiveResponse(Connection* lconn, TestPort* lport, Connection* rconn, TestPort* rport, - ScopedFakeClock* clock, + GlobalSimulatedTimeController& time_controller, int64_t ms) { lconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller}), + IsRtcOk()); ASSERT_GT(lport->last_stun_buf().size(), 0u); rconn->OnReadPacket( ReceivedIpPacket(lport->last_stun_buf(), SocketAddress(), std::nullopt)); - clock->AdvanceTime(TimeDelta::Millis(ms)); - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + time_controller.AdvanceTime(TimeDelta::Millis(ms)); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller}), + IsRtcOk()); ASSERT_GT(rport->last_stun_buf().size(), 0u); lconn->OnReadPacket( ReceivedIpPacket(rport->last_stun_buf(), SocketAddress(), std::nullopt)); @@ -168,7 +168,9 @@ void SendPingAndReceiveResponse(Connection* lconn, class TestChannel { public: // Takes ownership of `p1` (but not `p2`). - explicit TestChannel(std::unique_ptr p1) : port_(std::move(p1)) { + TestChannel(std::unique_ptr p1, + GlobalSimulatedTimeController& time_controller) + : time_controller_(time_controller), port_(std::move(p1)) { port_->SubscribePortComplete(this, [this](Port* port) { OnPortComplete(port); }); port_->SubscribeUnknownAddress( @@ -188,7 +190,10 @@ class TestChannel { const SocketAddress& remote_address() { return remote_address_; } std::string remote_fragment() { return remote_frag_; } - void Start() { port_->PrepareAddress(); } + void Start() { + port_->PrepareAddress(); + time_controller_.AdvanceTime(TimeDelta::Zero()); + } void CreateConnection(const Candidate& remote_candidate) { RTC_DCHECK(!conn_); conn_ = port_->CreateConnection(remote_candidate, Port::ORIGIN_MESSAGE); @@ -298,6 +303,7 @@ class TestChannel { connection_ready_to_send_ = true; } + GlobalSimulatedTimeController& time_controller_; IceMode ice_mode_ = ICEMODE_FULL; std::unique_ptr port_; @@ -314,14 +320,16 @@ class PortTest : public ::testing::Test { public: PortTest() : ss_(new VirtualSocketServer()), - main_(ss_.get()), + time_controller_(Timestamp::Millis(1000), ss_.get()), + main_(time_controller_.GetMainThread()), + env_(CreateTestEnvironment({.time = &time_controller_})), socket_factory_(ss_.get()), nat_factory1_(ss_.get(), kNatAddr1, SocketAddress()), nat_factory2_(ss_.get(), kNatAddr2, SocketAddress()), nat_socket_factory1_(&nat_factory1_), nat_socket_factory2_(&nat_factory2_), - stun_server_(TestStunServer::Create(env_, kStunAddr, *ss_, main_)), - turn_server_(env_, &main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), + stun_server_(TestStunServer::Create(env_, kStunAddr, *ss_, *main_)), + turn_server_(env_, main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), username_(CreateRandomString(ICE_UFRAG_LENGTH)), password_(CreateRandomString(ICE_PWD_LENGTH)), role_conflict_(false), @@ -331,10 +339,13 @@ class PortTest : public ::testing::Test { // Workaround for tests that trigger async destruction of objects that we // need to give an opportunity here to run, before proceeding with other // teardown. - Thread::Current()->ProcessMessages(0); + time_controller_.AdvanceTime(TimeDelta::Zero()); } protected: + VirtualSocketServer* vss() { return ss_.get(); } + const Environment& env() const { return env_; } + std::string password() { return password_; } void TestLocalToLocal() { @@ -442,7 +453,7 @@ class PortTest : public ::testing::Test { std::unique_ptr CreateUdpPort(const SocketAddress& addr, PacketSocketFactory* socket_factory) { auto port = UDPPort::Create({.env = env_, - .network_thread = &main_, + .network_thread = main_, .socket_factory = socket_factory, .network = MakeNetwork(addr), .ice_username_fragment = username_, @@ -458,7 +469,7 @@ class PortTest : public ::testing::Test { PacketSocketFactory* socket_factory) { auto port = UDPPort::Create( {.env = env_, - .network_thread = &main_, + .network_thread = main_, .socket_factory = socket_factory, .network = MakeNetworkMultipleAddrs(global_addr, link_local_addr), .ice_username_fragment = username_, @@ -473,7 +484,7 @@ class PortTest : public ::testing::Test { std::unique_ptr CreateTcpPort(const SocketAddress& addr, PacketSocketFactory* socket_factory) { auto port = TCPPort::Create({.env = env_, - .network_thread = &main_, + .network_thread = main_, .socket_factory = socket_factory, .network = MakeNetwork(addr), .ice_username_fragment = username_, @@ -488,7 +499,7 @@ class PortTest : public ::testing::Test { ServerAddresses stun_servers; stun_servers.insert(kStunAddr); auto port = StunPort::Create({.env = env_, - .network_thread = &main_, + .network_thread = main_, .socket_factory = socket_factory, .network = MakeNetwork(addr), .ice_username_fragment = username_, @@ -520,7 +531,7 @@ class PortTest : public ::testing::Test { config.credentials = kRelayCredentials; ProtocolAddress server_address(server_addr, int_proto); CreateRelayPortArgs args = {.env = env_}; - args.network_thread = &main_; + args.network_thread = main_; args.socket_factory = socket_factory; args.network = MakeNetwork(addr); args.username = username_; @@ -535,8 +546,8 @@ class PortTest : public ::testing::Test { std::unique_ptr CreateNatServer(const SocketAddress& addr, NATType type) { - return std::make_unique(env_, type, main_, ss_.get(), addr, addr, - main_, ss_.get(), addr); + return std::make_unique(env_, type, *main_, ss_.get(), addr, + addr, *main_, ss_.get(), addr); } static const char* StunName(NATType type) { switch (type) { @@ -586,20 +597,23 @@ class PortTest : public ::testing::Test { // TCP reconnecting mechanism before entering this function. void ConnectStartedChannels(TestChannel* ch1, TestChannel* ch2) { ASSERT_TRUE(ch1->conn()); - EXPECT_THAT(WaitUntil([&] { return ch1->conn()->connected(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); // for TCP connect + EXPECT_THAT( + WaitUntil([&] { return ch1->conn()->connected(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // for TCP connect ch1->Ping(); - EXPECT_TRUE(WaitUntil([&] { return !ch2->remote_address().IsNil(); }, - {.timeout = TimeDelta::Millis(kShortTimeout)})); + EXPECT_TRUE( + WaitUntil([&] { return !ch2->remote_address().IsNil(); }, + {.timeout = kShortTimeout, .clock = &time_controller_})); // Send a ping from dst to src. ch2->AcceptConnection(GetCandidate(ch1->port())); ch2->Ping(); - EXPECT_THAT(WaitUntil([&] { return ch2->conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch2->conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } // This connects and disconnects the provided channels in the same sequence as @@ -628,12 +642,14 @@ class PortTest : public ::testing::Test { tcp_conn2->socket()->GetLocalAddress())); // Wait for both OnClose are delivered. - EXPECT_THAT(WaitUntil([&] { return !ch1->conn()->connected(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return !ch2->conn()->connected(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return !ch1->conn()->connected(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return !ch2->conn()->connected(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Ensure redundant SignalClose events on TcpConnection won't break tcp // reconnection. Chromium will fire SignalClose for all outstanding IPC @@ -644,9 +660,10 @@ class PortTest : public ::testing::Test { // Speed up destroying ch2's connection such that the test is ready to // accept a new connection from ch1 before ch1's connection destroys itself. ch2->Stop(); - EXPECT_THAT(WaitUntil([&] { return ch2->conn(); }, IsNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch2->conn(); }, IsNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } void TestTcpReconnect(bool ping_after_disconnected, @@ -660,30 +677,32 @@ class PortTest : public ::testing::Test { port2->set_component(ICE_CANDIDATE_COMPONENT_DEFAULT); // Set up channels and ensure both ports will be deleted. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); EXPECT_EQ(0, ch1.complete_count()); EXPECT_EQ(0, ch2.complete_count()); ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Initial connecting the channel, create connection on channel1. ch1.CreateConnection(GetCandidate(ch2.port())); ConnectStartedChannels(&ch1, &ch2); // Shorten the timeout period. - const int kTcpReconnectTimeout = kDefaultTimeout; + const TimeDelta kTcpReconnectTimeout = kDefaultTimeout; static_cast(ch1.conn()) - ->set_reconnection_timeout(kTcpReconnectTimeout); + ->set_reconnection_timeout(kTcpReconnectTimeout.ms()); static_cast(ch2.conn()) - ->set_reconnection_timeout(kTcpReconnectTimeout); + ->set_reconnection_timeout(kTcpReconnectTimeout.ms()); EXPECT_FALSE(ch1.connection_ready_to_send()); EXPECT_FALSE(ch2.connection_ready_to_send()); @@ -705,15 +724,17 @@ class PortTest : public ::testing::Test { } // Wait for channel's outgoing TCPConnection connected. - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Verify that we could still connect channels. ConnectStartedChannels(&ch1, &ch2); EXPECT_THAT( - WaitUntil([&] { return ch1.connection_ready_to_send(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kTcpReconnectTimeout)}), + WaitUntil( + [&] { return ch1.connection_ready_to_send(); }, IsTrue(), + {.timeout = kTcpReconnectTimeout, .clock = &time_controller_}), IsRtcOk()); // Channel2 is the passive one so a new connection is created during // reconnect. This new connection should never have issued ENOTCONN @@ -724,8 +745,8 @@ class PortTest : public ::testing::Test { // Since the reconnection never happens, the connections should have been // destroyed after the timeout. EXPECT_THAT(WaitUntil([&] { return !ch1.conn(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kTcpReconnectTimeout + - kDefaultTimeout)}), + {.timeout = kTcpReconnectTimeout + kDefaultTimeout, + .clock = &time_controller_}), IsRtcOk()); EXPECT_TRUE(!ch2.conn()); } @@ -733,12 +754,14 @@ class PortTest : public ::testing::Test { // Tear down and ensure that goes smoothly. ch1.Stop(); ch2.Stop(); - EXPECT_THAT(WaitUntil([&] { return ch1.conn(); }, IsNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return ch2.conn(); }, IsNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn(); }, IsNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch2.conn(); }, IsNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } std::unique_ptr CreateStunMessage(StunMessageType type) { @@ -758,12 +781,14 @@ class PortTest : public ::testing::Test { absl::string_view username, absl::string_view password, const FieldTrialsView* field_trials = nullptr) { - Port::PortParametersRef args = {.env = CreateEnvironment(field_trials), - .network_thread = &main_, - .socket_factory = &socket_factory_, - .network = MakeNetwork(addr), - .ice_username_fragment = username, - .ice_password = password}; + Port::PortParametersRef args = { + .env = CreateTestEnvironment( + {.field_trials = field_trials, .time = &time_controller_}), + .network_thread = main_, + .socket_factory = &socket_factory_, + .network = MakeNetwork(addr), + .ice_username_fragment = username, + .ice_password = password}; auto port = std::make_unique(args, 0, 0); port->SubscribeRoleConflict([this]() { OnRoleConflict(); }); return port; @@ -783,7 +808,7 @@ class PortTest : public ::testing::Test { absl::string_view username, absl::string_view password) { Port::PortParametersRef args = {.env = env_, - .network_thread = &main_, + .network_thread = main_, .socket_factory = &socket_factory_, .network = network, .ice_username_fragment = username, @@ -796,7 +821,7 @@ class PortTest : public ::testing::Test { std::unique_ptr CreateRawTestPort() { Port::PortParametersRef args = { .env = env_, - .network_thread = &main_, + .network_thread = main_, .socket_factory = &socket_factory_, .network = MakeNetwork(kLocalAddr1), }; @@ -818,18 +843,16 @@ class PortTest : public ::testing::Test { return &nat_socket_factory1_; } - VirtualSocketServer* vss() { return ss_.get(); } - - const Environment& env() const { return env_; } + protected: + std::unique_ptr ss_; + GlobalSimulatedTimeController time_controller_; + Thread* main_; + const Environment env_; - private: - const Environment env_ = CreateTestEnvironment(); // When a "create port" helper method is called with an IP, we create a // Network with that IP and add it to this list. Using a list instead of a // vector so that when it grows, pointers aren't invalidated. std::list networks_; - std::unique_ptr ss_; - AutoSocketServerThread main_; BasicPacketSocketFactory socket_factory_; std::unique_ptr nat_server1_; std::unique_ptr nat_server2_; @@ -853,40 +876,43 @@ void PortTest::TestConnectivity(absl::string_view name1, bool same_addr1, bool same_addr2, bool possible) { - ScopedFakeClock clock; RTC_LOG(LS_INFO) << "Test: " << name1 << " to " << name2 << ": "; port1->set_component(ICE_CANDIDATE_COMPONENT_DEFAULT); port2->set_component(ICE_CANDIDATE_COMPONENT_DEFAULT); // Set up channels and ensure both ports will be deleted. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); EXPECT_EQ(0, ch1.complete_count()); EXPECT_EQ(0, ch2.complete_count()); // Acquire addresses. ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Send a ping from src to dst. This may or may not make it. ch1.CreateConnection(GetCandidate(ch2.port())); ASSERT_TRUE(ch1.conn() != nullptr); - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); // for TCP connect - ch1.Ping(); - SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // for TCP connect + ch1.Ping(); if (accept) { + EXPECT_THAT( + WaitUntil([&] { return !ch2.remote_address().IsNil(); }, IsTrue(), + {.timeout = kShortTimeout, .clock = &time_controller_}), + IsRtcOk()); + // We are able to send a ping from src to dst. This is the case when // sending to UDP ports and cone NATs. EXPECT_TRUE(ch1.remote_address().IsNil()); @@ -901,12 +927,13 @@ void PortTest::TestConnectivity(absl::string_view name1, // Send a ping from dst to src. ch2.AcceptConnection(GetCandidate(ch1.port())); ASSERT_TRUE(ch2.conn() != nullptr); + ch2.Ping(); - EXPECT_THAT(WaitUntil([&] { return ch2.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch2.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } else { // We can't send a ping from src to dst, so flip it around. This will happen // when the destination NAT is addr/port restricted or symmetric. @@ -916,9 +943,12 @@ void PortTest::TestConnectivity(absl::string_view name1, // Send a ping from dst to src. Again, this may or may not make it. ch2.CreateConnection(GetCandidate(ch1.port())); ASSERT_TRUE(ch2.conn() != nullptr); + ch2.Ping(); - SIMULATED_WAIT(ch2.conn()->write_state() == Connection::STATE_WRITABLE, - kShortTimeout, clock); + RTC_UNUSED( + WaitUntil([&] { return ch2.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kShortTimeout, .clock = &time_controller_})); if (same_addr1 && same_addr2) { // The new ping got back to the source. @@ -929,25 +959,24 @@ void PortTest::TestConnectivity(absl::string_view name1, // through. So we will have to do another. if (ch1.conn()->write_state() == Connection::STATE_WRITE_INIT) { ch1.Ping(); - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } } else if (!same_addr1 && possible) { // The new ping went to the candidate address, but that address was bad. // This will happen when the source NAT is symmetric. - EXPECT_TRUE(ch1.remote_address().IsNil()); - EXPECT_TRUE(ch2.remote_address().IsNil()); + // With simulated time and some NAT types (like Turn/AR-Nat), the packet + // can be delivered early. // However, since we have now sent a ping to the source IP, we should be // able to get a ping from it. This gives us the real source address. ch1.Ping(); EXPECT_THAT( - WaitUntil( - [&] { return !ch2.remote_address().IsNil(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout), .clock = &clock}), + WaitUntil([&] { return !ch2.remote_address().IsNil(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), IsRtcOk()); EXPECT_FALSE(ch2.conn()->receiving()); EXPECT_TRUE(ch1.remote_address().IsNil()); @@ -955,12 +984,13 @@ void PortTest::TestConnectivity(absl::string_view name1, // Pick up the actual address and establish the connection. ch2.AcceptConnection(GetCandidate(ch1.port())); ASSERT_TRUE(ch2.conn() != nullptr); + ch2.Ping(); - EXPECT_THAT(WaitUntil([&] { return ch2.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch2.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } else if (!same_addr2 && possible) { // The new ping came in, but from an unexpected address. This will happen // when the destination NAT is symmetric. @@ -969,18 +999,21 @@ void PortTest::TestConnectivity(absl::string_view name1, // Update our address and complete the connection. ch1.AcceptConnection(GetCandidate(ch2.port())); + ch1.Ping(); - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } else { // (!possible) // There should be s no way for the pings to reach each other. Check it. EXPECT_TRUE(ch1.remote_address().IsNil()); EXPECT_TRUE(ch2.remote_address().IsNil()); ch1.Ping(); - SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock); + RTC_UNUSED( + WaitUntil([&] { return !ch2.remote_address().IsNil(); }, IsTrue(), + {.timeout = kShortTimeout, .clock = &time_controller_})); EXPECT_TRUE(ch1.remote_address().IsNil()); EXPECT_TRUE(ch2.remote_address().IsNil()); } @@ -990,9 +1023,13 @@ void PortTest::TestConnectivity(absl::string_view name1, ASSERT_TRUE(ch1.conn() != nullptr); ASSERT_TRUE(ch2.conn() != nullptr); if (possible) { - EXPECT_TRUE(ch1.conn()->receiving()); + EXPECT_THAT(WaitUntil([&] { return ch1.conn()->receiving(); }, IsTrue(), + {.clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(Connection::STATE_WRITABLE, ch1.conn()->write_state()); - EXPECT_TRUE(ch2.conn()->receiving()); + EXPECT_THAT(WaitUntil([&] { return ch2.conn()->receiving(); }, IsTrue(), + {.clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(Connection::STATE_WRITABLE, ch2.conn()->write_state()); } else { EXPECT_FALSE(ch1.conn()->receiving()); @@ -1004,16 +1041,84 @@ void PortTest::TestConnectivity(absl::string_view name1, // Tear down and ensure that goes smoothly. ch1.Stop(); ch2.Stop(); - EXPECT_THAT(WaitUntil([&] { return ch1.conn(); }, IsNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return ch2.conn(); }, IsNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn(); }, IsNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch2.conn(); }, IsNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } +class FakePacketSocketFactory : public PacketSocketFactory { + public: + FakePacketSocketFactory() = default; + ~FakePacketSocketFactory() override = default; + + std::unique_ptr CreateUdpSocket( + const Environment& env, + const SocketAddress& address, + uint16_t min_port, + uint16_t max_port) override { + EXPECT_THAT(next_udp_socket_, NotNull()); + return std::move(next_udp_socket_); + } + + std::unique_ptr CreateClientUdpSocket( + const Environment& env, + const SocketAddress& local_address, + const SocketAddress& remote_address, + uint16_t min_port, + uint16_t max_port, + const PacketSocketTcpOptions& options) override { + EXPECT_THAT(next_udp_socket_, NotNull()); + return std::move(next_udp_socket_); + } + + std::unique_ptr CreateServerTcpSocket( + const Environment& env, + const SocketAddress& local_address, + uint16_t min_port, + uint16_t max_port, + int opts) override { + EXPECT_THAT(next_server_tcp_socket_, NotNull()); + return std::move(next_server_tcp_socket_); + } + + std::unique_ptr CreateClientTcpSocket( + const Environment& env, + const SocketAddress& local_address, + const SocketAddress& remote_address, + const PacketSocketTcpOptions& opts) override { + EXPECT_TRUE(next_client_tcp_socket_.has_value()); + auto result = std::move(*next_client_tcp_socket_); + next_client_tcp_socket_.reset(); + return result; + } + + void set_next_udp_socket(std::unique_ptr next_udp_socket) { + next_udp_socket_ = std::move(next_udp_socket); + } + void set_next_server_tcp_socket( + std::unique_ptr next_server_tcp_socket) { + next_server_tcp_socket_ = std::move(next_server_tcp_socket); + } + void set_next_client_tcp_socket( + std::unique_ptr next_client_tcp_socket) { + next_client_tcp_socket_ = std::move(next_client_tcp_socket); + } + std::unique_ptr CreateAsyncDnsResolver() + override { + return nullptr; + } + + private: + std::unique_ptr next_udp_socket_; + std::unique_ptr next_server_tcp_socket_; + std::optional> next_client_tcp_socket_; +}; + class FakeAsyncPacketSocket : public AsyncPacketSocket { public: // Returns current local address. Address may be set to NULL if the @@ -1233,13 +1338,14 @@ TEST_F(PortTest, TestTcpNeverConnect) { port1->set_component(ICE_CANDIDATE_COMPONENT_DEFAULT); // Set up a channel and ensure the port will be deleted. - TestChannel ch1(std::move(port1)); + TestChannel ch1(std::move(port1), time_controller_); EXPECT_EQ(0, ch1.complete_count()); ch1.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); std::unique_ptr server( vss()->CreateSocket(kLocalAddr2.family(), SOCK_STREAM)); @@ -1251,9 +1357,10 @@ TEST_F(PortTest, TestTcpNeverConnect) { ch1.CreateConnection(c); EXPECT_TRUE(ch1.conn()); - EXPECT_THAT(WaitUntil([&] { return !ch1.conn(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); // for TCP connect + EXPECT_THAT( + WaitUntil([&] { return !ch1.conn(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // for TCP connect } /* TODO(?): Enable these once testrelayserver can accept external TCP. @@ -1283,17 +1390,19 @@ TEST_F(PortTest, TestSslTcpToSslTcpRelay) { // ii) it has not received anything for kDeadConnectionReceiveTimeout since // last receiving. TEST_F(PortTest, TestConnectionDead) { - TestChannel ch1(CreateUdpPort(kLocalAddr1)); - TestChannel ch2(CreateUdpPort(kLocalAddr2)); + TestChannel ch1(CreateUdpPort(kLocalAddr1), time_controller_); + TestChannel ch2(CreateUdpPort(kLocalAddr2), time_controller_); // Acquire address. ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Test case that the connection has never received anything. Timestamp before_created = env().clock().CurrentTime(); @@ -1303,18 +1412,19 @@ TEST_F(PortTest, TestConnectionDead) { ASSERT_NE(conn, nullptr); // It is not dead if it is after kMinConnectionLifetime but not pruned. conn->UpdateState(after_created + kMinConnectionLifetime + kEpsilon); - Thread::Current()->ProcessMessages(0); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(ch1.conn() != nullptr); // It is not dead if it is before kMinConnectionLifetime and pruned. conn->UpdateState(before_created + kMinConnectionLifetime - kEpsilon); conn->Prune(); - Thread::Current()->ProcessMessages(0); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(ch1.conn() != nullptr); // It will be dead after kMinConnectionLifetime and pruned. conn->UpdateState(after_created + kMinConnectionLifetime + kEpsilon); - EXPECT_THAT(WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Test case that the connection has received something. // Create a connection again and receive a ping. @@ -1327,27 +1437,30 @@ TEST_F(PortTest, TestConnectionDead) { // The connection will be dead after kDeadConnectionReceiveTimeout conn->UpdateState(before_last_receiving + kDeadConnectionReceiveTimeout - kEpsilon); - Thread::Current()->ProcessMessages(100); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(ch1.conn() != nullptr); conn->UpdateState(after_last_receiving + kDeadConnectionReceiveTimeout + kEpsilon); - EXPECT_THAT(WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } TEST_F(PortTest, TestConnectionDeadWithDeadConnectionTimeout) { - TestChannel ch1(CreateUdpPort(kLocalAddr1)); - TestChannel ch2(CreateUdpPort(kLocalAddr2)); + TestChannel ch1(CreateUdpPort(kLocalAddr1), time_controller_); + TestChannel ch2(CreateUdpPort(kLocalAddr2), time_controller_); // Acquire address. ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Note: set field trials manually since they are parsed by // P2PTransportChannel but P2PTransportChannel is not used in this test. @@ -1365,12 +1478,13 @@ TEST_F(PortTest, TestConnectionDeadWithDeadConnectionTimeout) { Timestamp after_last_receiving = env().clock().CurrentTime(); // The connection will be dead after 90s conn->UpdateState(before_last_receiving + TimeDelta::Seconds(90) - kEpsilon); - Thread::Current()->ProcessMessages(100); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(ch1.conn() != nullptr); conn->UpdateState(after_last_receiving + TimeDelta::Seconds(90) + kEpsilon); - EXPECT_THAT(WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } TEST_F(PortTest, TestConnectionDeadOutstandingPing) { @@ -1381,17 +1495,19 @@ TEST_F(PortTest, TestConnectionDeadOutstandingPing) { port2->SetIceRole(ICEROLE_CONTROLLED); port2->SetIceTiebreaker(kTiebreaker2); - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Acquire address. ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Note: set field trials manually since they are parsed by // P2PTransportChannel but P2PTransportChannel is not used in this test. @@ -1412,13 +1528,14 @@ TEST_F(PortTest, TestConnectionDeadOutstandingPing) { // The connection will be dead 30s after the ping was sent. conn->UpdateState(send_ping_timestamp + kDeadConnectionReceiveTimeout - kEpsilon); - Thread::Current()->ProcessMessages(100); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(ch1.conn() != nullptr); conn->UpdateState(send_ping_timestamp + kDeadConnectionReceiveTimeout + kEpsilon); - EXPECT_THAT(WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn(); }, Eq(nullptr), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); } // This test case verifies standard ICE features in STUN messages. Currently it @@ -1450,16 +1567,18 @@ TEST_F(PortTest, TestLoopbackCall) { lport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); conn->Ping(); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); IceMessage* msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); conn->OnReadPacket( ReceivedIpPacket(lport->last_stun_buf(), SocketAddress(), std::nullopt)); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_RESPONSE, msg->type()); @@ -1472,9 +1591,10 @@ TEST_F(PortTest, TestLoopbackCall) { lport->CreateConnection(lport->Candidates()[1], Port::ORIGIN_MESSAGE); conn1->Ping(); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); std::unique_ptr modified_req( @@ -1496,9 +1616,10 @@ TEST_F(PortTest, TestLoopbackCall) { conn1->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( reinterpret_cast(buf->Data()), buf->Length(), /*packet_time_us=*/-1)); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE, msg->type()); } @@ -1526,24 +1647,25 @@ TEST_F(PortTest, TestIceRoleConflict) { rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); rconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); IceMessage* msg = rport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); // Send rport binding request to lport. lconn->OnReadPacket( ReceivedIpPacket(rport->last_stun_buf(), SocketAddress(), std::nullopt)); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type()); EXPECT_TRUE(role_conflict()); } TEST_F(PortTest, TestTcpNoDelay) { - ScopedFakeClock clock; auto port1 = CreateTcpPort(kLocalAddr1); port1->SetIceRole(ICEROLE_CONTROLLING); int option_value = -1; @@ -1556,28 +1678,30 @@ TEST_F(PortTest, TestTcpNoDelay) { // Set up a connection, and verify that option is set on connected sockets at // both ends. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Acquire addresses. ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Connect and send a ping from src to dst. ch1.CreateConnection(GetCandidate(ch2.port())); ASSERT_TRUE(ch1.conn() != nullptr); - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); // for TCP connect + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // for TCP connect ch1.Ping(); - SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock); + EXPECT_THAT(WaitUntil([&] { return !ch2.remote_address().IsNil(); }, IsTrue(), + {.clock = &time_controller_}), + IsRtcOk()); // Accept the connection. ch2.AcceptConnection(GetCandidate(ch1.port())); @@ -1849,9 +1973,10 @@ TEST_F(PortTest, TestSendStunMessage) { lconn->Ping(); // Check that it's a proper BINDING-REQUEST. - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); IceMessage* msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); EXPECT_FALSE(msg->IsLegacy()); @@ -1953,9 +2078,10 @@ TEST_F(PortTest, TestSendStunMessage) { rconn->Ping(); rconn->Ping(); rconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); msg = rport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); const StunUInt64Attribute* ice_controlled_attr = @@ -2026,9 +2152,10 @@ TEST_F(PortTest, TestNomination) { // Send ping (including the nomination value) from `lconn` to `rconn`. This // should set the remote nomination of `rconn`. lconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_GT(lport->last_stun_buf().size(), 0u); rconn->OnReadPacket( ReceivedIpPacket(lport->last_stun_buf(), SocketAddress(), std::nullopt)); @@ -2041,9 +2168,10 @@ TEST_F(PortTest, TestNomination) { // This should result in an acknowledgment sent back from `rconn` to `lconn`, // updating the acknowledged nomination of `lconn`. - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_GT(rport->last_stun_buf().size(), 0u); lconn->OnReadPacket( ReceivedIpPacket(rport->last_stun_buf(), SocketAddress(), std::nullopt)); @@ -2056,8 +2184,6 @@ TEST_F(PortTest, TestNomination) { } TEST_F(PortTest, TestRoundTripTime) { - ScopedFakeClock clock; - auto lport = CreateTestPort(kLocalAddr1, "lfrag", "lpass"); auto rport = CreateTestPort(kLocalAddr2, "rfrag", "rpass"); lport->SetIceRole(ICEROLE_CONTROLLING); @@ -2077,20 +2203,20 @@ TEST_F(PortTest, TestRoundTripTime) { EXPECT_EQ(0u, lconn->stats().total_round_trip_time_ms); EXPECT_FALSE(lconn->stats().current_round_trip_time_ms); - SendPingAndReceiveResponse(lconn, lport.get(), rconn, rport.get(), &clock, - 10); + SendPingAndReceiveResponse(lconn, lport.get(), rconn, rport.get(), + time_controller_, 10); EXPECT_EQ(10u, lconn->stats().total_round_trip_time_ms); ASSERT_TRUE(lconn->stats().current_round_trip_time_ms); EXPECT_EQ(10u, *lconn->stats().current_round_trip_time_ms); - SendPingAndReceiveResponse(lconn, lport.get(), rconn, rport.get(), &clock, - 20); + SendPingAndReceiveResponse(lconn, lport.get(), rconn, rport.get(), + time_controller_, 20); EXPECT_EQ(30u, lconn->stats().total_round_trip_time_ms); ASSERT_TRUE(lconn->stats().current_round_trip_time_ms); EXPECT_EQ(20u, *lconn->stats().current_round_trip_time_ms); - SendPingAndReceiveResponse(lconn, lport.get(), rconn, rport.get(), &clock, - 30); + SendPingAndReceiveResponse(lconn, lport.get(), rconn, rport.get(), + time_controller_, 30); EXPECT_EQ(60u, lconn->stats().total_round_trip_time_ms); ASSERT_TRUE(lconn->stats().current_round_trip_time_ms); EXPECT_EQ(30u, *lconn->stats().current_round_trip_time_ms); @@ -2111,9 +2237,10 @@ TEST_F(PortTest, TestUseCandidateAttribute) { Connection* lconn = lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE); lconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); IceMessage* msg = lport->last_stun_msg(); const StunUInt64Attribute* ice_controlling_attr = msg->GetUInt64(STUN_ATTR_ICE_CONTROLLING); @@ -2171,9 +2298,10 @@ TEST_F(PortTest, TestNetworkCostChange) { // message is handled in rconn, The rconn's remote candidate will have cost // kNetworkCostHigh; EXPECT_EQ(kNetworkCostLow, rconn->remote_candidate().network_cost()); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); IceMessage* msg = lport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); // Pass the binding request to rport. @@ -2181,9 +2309,10 @@ TEST_F(PortTest, TestNetworkCostChange) { ReceivedIpPacket(lport->last_stun_buf(), SocketAddress(), std::nullopt)); // Wait until rport sends the response and then check the remote network cost. - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(kNetworkCostHigh, rconn->remote_candidate().network_cost()); } @@ -2204,9 +2333,10 @@ TEST_F(PortTest, TestNetworkInfoAttribute) { Connection* lconn = lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE); lconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); IceMessage* msg = lport->last_stun_msg(); const StunUInt32Attribute* network_info_attr = msg->GetUInt32(STUN_ATTR_GOOG_NETWORK_INFO); @@ -2224,9 +2354,10 @@ TEST_F(PortTest, TestNetworkInfoAttribute) { Connection* rconn = rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); rconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); msg = rport->last_stun_msg(); network_info_attr = msg->GetUInt32(STUN_ATTR_GOOG_NETWORK_INFO); ASSERT_TRUE(network_info_attr != nullptr); @@ -2513,16 +2644,18 @@ TEST_F(PortTest, // Send request. lconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); rconn->OnReadPacket( ReceivedIpPacket(lport->last_stun_buf(), SocketAddress(), std::nullopt)); // Intercept request and add comprehension required attribute. - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); auto modified_response = rport->last_stun_msg()->Clone(); modified_response->AddAttribute(StunAttribute::CreateUInt32(0x7777)); modified_response->RemoveAttribute(STUN_ATTR_FINGERPRINT); @@ -2602,23 +2735,25 @@ TEST_F(PortTest, TestHandleStunBindingIndication) { rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); rconn->Ping(); - ASSERT_THAT(WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return rport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); IceMessage* msg = rport->last_stun_msg(); EXPECT_EQ(STUN_BINDING_REQUEST, msg->type()); // Send rport binding request to lport. lconn->OnReadPacket( ReceivedIpPacket(rport->last_stun_buf(), SocketAddress(), std::nullopt)); - ASSERT_THAT(WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return lport->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type()); Timestamp last_ping_received1 = lconn->LastPingReceived(); // Adding a delay of 100ms. - Thread::Current()->ProcessMessages(100); + time_controller_.AdvanceTime(TimeDelta::Millis(100)); // Pinging lconn using stun indication message. lconn->OnReadPacket(ReceivedIpPacket::CreateFromLegacy( buf->Data(), buf->Length(), /*packet_time_us=*/-1)); @@ -2733,9 +2868,10 @@ TEST_F(PortTest, TestCandidateFoundation) { tcpport2->Candidates()[0].foundation()); auto stunport = CreateStunPort(kLocalAddr1, nat_socket_factory1()); stunport->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return stunport->Candidates().size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return stunport->Candidates().size(); }, Eq(1U), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_NE(tcpport1->Candidates()[0].foundation(), stunport->Candidates()[0].foundation()); EXPECT_NE(tcpport2->Candidates()[0].foundation(), @@ -2748,9 +2884,10 @@ TEST_F(PortTest, TestCandidateFoundation) { auto turnport1 = CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP); turnport1->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return turnport1->Candidates().size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return turnport1->Candidates().size(); }, Eq(1U), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_NE(udpport1->Candidates()[0].foundation(), turnport1->Candidates()[0].foundation()); EXPECT_NE(udpport2->Candidates()[0].foundation(), @@ -2760,9 +2897,10 @@ TEST_F(PortTest, TestCandidateFoundation) { auto turnport2 = CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP); turnport2->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return turnport2->Candidates().size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return turnport2->Candidates().size(); }, Eq(1U), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(turnport1->Candidates()[0].foundation(), turnport2->Candidates()[0].foundation()); @@ -2774,9 +2912,10 @@ TEST_F(PortTest, TestCandidateFoundation) { auto turnport3 = CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP, kTurnUdpIntAddr2); turnport3->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return turnport3->Candidates().size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return turnport3->Candidates().size(); }, Eq(1U), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_NE(turnport3->Candidates()[0].foundation(), turnport2->Candidates()[0].foundation()); @@ -2787,9 +2926,10 @@ TEST_F(PortTest, TestCandidateFoundation) { auto turnport4 = CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_TCP, PROTO_UDP); turnport4->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return turnport4->Candidates().size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return turnport4->Candidates().size(); }, Eq(1U), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_NE(turnport2->Candidates()[0].foundation(), turnport4->Candidates()[0].foundation()); } @@ -2807,9 +2947,10 @@ TEST_F(PortTest, TestCandidateRelatedAddress) { // socket address. auto stunport = CreateStunPort(kLocalAddr1, nat_socket_factory1()); stunport->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return stunport->Candidates().size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return stunport->Candidates().size(); }, Eq(1U), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Check STUN candidate address. EXPECT_EQ(stunport->Candidates()[0].address().ipaddr(), kNatAddr1.ipaddr()); // Check STUN candidate related address. @@ -2820,9 +2961,10 @@ TEST_F(PortTest, TestCandidateRelatedAddress) { auto turnport = CreateTurnPort(kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP); turnport->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return turnport->Candidates().size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return turnport->Candidates().size(); }, Eq(1U), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), turnport->Candidates()[0].address().ipaddr()); EXPECT_EQ(kNatAddr1.ipaddr(), @@ -2851,9 +2993,12 @@ TEST_F(PortTest, TestConnectionPriority) { lport->AddCandidateAddress(SocketAddress("192.168.1.4", 1234)); rport->set_component(23); rport->AddCandidateAddress(SocketAddress("10.1.1.100", 1234)); + rport->set_type_preference(ICE_TYPE_PREFERENCE_RELAY_DTLS); + rport->AddCandidateAddress(SocketAddress("10.1.1.100", 1234)); EXPECT_EQ(0x7E001E85U, lport->Candidates()[0].priority()); - EXPECT_EQ(0x2001EE9U, rport->Candidates()[0].priority()); + EXPECT_EQ(0x3001EE9U, rport->Candidates()[0].priority()); + EXPECT_EQ(0x2001EE9U, rport->Candidates()[1].priority()); // RFC 5245 // pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0) @@ -2862,19 +3007,22 @@ TEST_F(PortTest, TestConnectionPriority) { Connection* lconn = lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE); #if defined(WEBRTC_WIN) - EXPECT_EQ(0x2001EE9FC003D0BU, lconn->priority()); + EXPECT_EQ(0x3001EE9FC003D0BU, lconn->priority()); #else - EXPECT_EQ(0x2001EE9FC003D0BLLU, lconn->priority()); + EXPECT_EQ(0x3001EE9FC003D0BLLU, lconn->priority()); #endif + lconn = lport->CreateConnection(rport->Candidates()[1], Port::ORIGIN_MESSAGE); + EXPECT_EQ(uint64_t{0x2001EE9FC003D0B}, lconn->priority()); + lport->SetIceRole(ICEROLE_CONTROLLED); rport->SetIceRole(ICEROLE_CONTROLLING); Connection* rconn = rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); #if defined(WEBRTC_WIN) - EXPECT_EQ(0x2001EE9FC003D0AU, rconn->priority()); + EXPECT_EQ(0x3001EE9FC003D0AU, rconn->priority()); #else - EXPECT_EQ(0x2001EE9FC003D0ALLU, rconn->priority()); + EXPECT_EQ(0x3001EE9FC003D0ALLU, rconn->priority()); #endif } @@ -2896,7 +3044,7 @@ TEST_F(PortTest, TestConnectionPriorityWithPriorityAdjustment) { EXPECT_EQ(0x7E001E85U + (kMaxTurnServers << 8), lport->Candidates()[0].priority()); - EXPECT_EQ(0x2001EE9U + (kMaxTurnServers << 8), + EXPECT_EQ(0x3001EE9U + (kMaxTurnServers << 8), rport->Candidates()[0].priority()); // RFC 5245 @@ -2906,9 +3054,9 @@ TEST_F(PortTest, TestConnectionPriorityWithPriorityAdjustment) { Connection* lconn = lport->CreateConnection(rport->Candidates()[0], Port::ORIGIN_MESSAGE); #if defined(WEBRTC_WIN) - EXPECT_EQ(0x2003EE9FC007D0BU, lconn->priority()); + EXPECT_EQ(0x3003EE9FC007D0BU, lconn->priority()); #else - EXPECT_EQ(0x2003EE9FC007D0BLLU, lconn->priority()); + EXPECT_EQ(0x3003EE9FC007D0BLLU, lconn->priority()); #endif lport->SetIceRole(ICEROLE_CONTROLLED); @@ -2917,9 +3065,9 @@ TEST_F(PortTest, TestConnectionPriorityWithPriorityAdjustment) { rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); RTC_LOG(LS_ERROR) << "RCONN " << rconn->priority(); #if defined(WEBRTC_WIN) - EXPECT_EQ(0x2003EE9FC007D0AU, rconn->priority()); + EXPECT_EQ(0x3003EE9FC007D0AU, rconn->priority()); #else - EXPECT_EQ(0x2003EE9FC007D0ALLU, rconn->priority()); + EXPECT_EQ(0x3003EE9FC007D0ALLU, rconn->priority()); #endif } @@ -2930,39 +3078,40 @@ TEST_F(PortTest, TestConnectionPriorityWithPriorityAdjustment) { // the default setup where the RTT is deterministically one, which generates an // estimate given by `MINIMUM_RTT` = 100. TEST_F(PortTest, TestWritableState) { - ScopedFakeClock clock; auto port1 = CreateUdpPort(kLocalAddr1); port1->SetIceRole(ICEROLE_CONTROLLING); auto port2 = CreateUdpPort(kLocalAddr2); port2->SetIceRole(ICEROLE_CONTROLLED); // Set up channels. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Acquire addresses. ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Send a ping from src to dst. ch1.CreateConnection(GetCandidate(ch2.port())); ASSERT_TRUE(ch1.conn() != nullptr); EXPECT_EQ(Connection::STATE_WRITE_INIT, ch1.conn()->write_state()); // for TCP connect - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->connected(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ch1.Ping(); - SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock); + EXPECT_THAT(WaitUntil([&] { return !ch2.remote_address().IsNil(); }, IsTrue(), + {.clock = &time_controller_}), + IsRtcOk()); // Data should be sendable before the connection is accepted. char data[] = "abcd"; @@ -2973,11 +3122,11 @@ TEST_F(PortTest, TestWritableState) { // Accept the connection to return the binding response, transition to // writable, and allow data to be sent. ch2.AcceptConnection(GetCandidate(ch1.port())); - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(data_size, ch1.conn()->Send(data, data_size, options)); // Ask the connection to update state as if enough time has passed to lose @@ -2997,11 +3146,11 @@ TEST_F(PortTest, TestWritableState) { // And now allow the other side to process the pings and send binding // responses. - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Wait long enough for a full timeout (past however long we've already // waited). for (uint32_t i = 1; i <= kConnectionWriteConnectFailures; ++i) { @@ -3023,42 +3172,43 @@ TEST_F(PortTest, TestWritableState) { // the default value given by `kConnectionWriteConnectTimeout` and // `kConnectionWriteConnectFailures`. TEST_F(PortTest, TestWritableStateWithConfiguredThreshold) { - ScopedFakeClock clock; auto port1 = CreateUdpPort(kLocalAddr1); port1->SetIceRole(ICEROLE_CONTROLLING); auto port2 = CreateUdpPort(kLocalAddr2); port2->SetIceRole(ICEROLE_CONTROLLED); // Set up channels. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Acquire addresses. ch1.Start(); ch2.Start(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); - ASSERT_THAT(WaitUntil([&] { return ch2.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch2.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Send a ping from src to dst. ch1.CreateConnection(GetCandidate(ch2.port())); ASSERT_TRUE(ch1.conn() != nullptr); ch1.Ping(); - SIMULATED_WAIT(!ch2.remote_address().IsNil(), kShortTimeout, clock); + EXPECT_THAT(WaitUntil([&] { return !ch2.remote_address().IsNil(); }, IsTrue(), + {.clock = &time_controller_}), + IsRtcOk()); // Accept the connection to return the binding response, transition to // writable, and allow data to be sent. ch2.AcceptConnection(GetCandidate(ch1.port())); - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout), - .clock = &clock}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ch1.conn()->SetUnwritableTimeout(TimeDelta::Seconds(1)); ch1.conn()->set_unwritable_min_checks(3); @@ -3093,8 +3243,8 @@ TEST_F(PortTest, TestTimeoutForNeverWritable) { port2->SetIceRole(ICEROLE_CONTROLLED); // Set up channels. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Acquire addresses. ch1.Start(); @@ -3106,11 +3256,14 @@ TEST_F(PortTest, TestTimeoutForNeverWritable) { // Attempt to go directly to write timeout. for (uint32_t i = 1; i <= kConnectionWriteConnectFailures; ++i) { - ch1.Ping(Timestamp::Millis(i)); + time_controller_.AdvanceTime(TimeDelta::Millis(1)); + ch1.Ping(); } - ch1.conn()->UpdateState(Timestamp::Zero() + kConnectionWriteTimeout + - kMaxExpectedSimulatedRtt); - EXPECT_EQ(Connection::STATE_WRITE_TIMEOUT, ch1.conn()->write_state()); + time_controller_.AdvanceTime(kConnectionWriteTimeout); + ch1.conn()->UpdateState(env_.clock().CurrentTime()); + // The connection should be destroyed because no data received before the + // write timeout. + EXPECT_THAT(ch1.conn(), IsNull()); } // This test verifies the connection setup between ICEMODE_FULL @@ -3125,16 +3278,17 @@ TEST_F(PortTest, TestIceLiteConnectivity) { auto ice_lite_port = CreateTestPort(kLocalAddr2, "rfrag", "rpass", ICEROLE_CONTROLLED, kTiebreaker2); // Setup TestChannel. This behaves like FULL mode client. - TestChannel ch1(std::move(ice_full_port)); + TestChannel ch1(std::move(ice_full_port), time_controller_); ch1.SetIceMode(ICEMODE_FULL); // Start gathering candidates. ch1.Start(); ice_lite_port->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_FALSE(ice_lite_port->Candidates().empty()); ch1.CreateConnection(GetCandidate(ice_lite_port.get())); @@ -3149,7 +3303,7 @@ TEST_F(PortTest, TestIceLiteConnectivity) { // from port. ASSERT_THAT( WaitUntil([&] { return ice_full_port_ptr->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), IsRtcOk()); IceMessage* msg = ice_full_port_ptr->last_stun_msg(); EXPECT_TRUE(msg->GetByteString(STUN_ATTR_USE_CANDIDATE) == nullptr); @@ -3168,13 +3322,15 @@ TEST_F(PortTest, TestIceLiteConnectivity) { SocketAddress(), std::nullopt)); // Verifying full mode connection becomes writable from the response. - EXPECT_THAT(WaitUntil([&] { return ch1.conn()->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return ch1.nominated(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.conn()->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return ch1.nominated(); }, IsTrue(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // Clear existing stun messsages. Otherwise we will process old stun // message right after we send ping. @@ -3183,7 +3339,7 @@ TEST_F(PortTest, TestIceLiteConnectivity) { ch1.Ping(); ASSERT_THAT( WaitUntil([&] { return ice_full_port_ptr->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), IsRtcOk()); msg = ice_full_port_ptr->last_stun_msg(); EXPECT_TRUE(msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr); @@ -3247,7 +3403,7 @@ TEST_P(GoogPingTest, TestGoogPingAnnounceEnable) { auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass", ICEROLE_CONTROLLED, kTiebreaker2); - TestChannel ch1(std::move(port1_unique)); + TestChannel ch1(std::move(port1_unique), time_controller_); // Block usage of STUN_ATTR_USE_CANDIDATE so that // ch1.conn() will sent GOOG_PING_REQUEST directly. // This only makes test a bit shorter... @@ -3256,9 +3412,10 @@ TEST_P(GoogPingTest, TestGoogPingAnnounceEnable) { ch1.Start(); port2->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_FALSE(port2->Candidates().empty()); ch1.CreateConnection(GetCandidate(port2.get())); @@ -3269,9 +3426,10 @@ TEST_P(GoogPingTest, TestGoogPingAnnounceEnable) { // Send ping. ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* request1 = port1->last_stun_msg(); ASSERT_EQ(trials.enable_goog_ping, @@ -3299,9 +3457,10 @@ TEST_P(GoogPingTest, TestGoogPingAnnounceEnable) { port2->Reset(); ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* request2 = port1->last_stun_msg(); // It should be a GOOG_PING if both of these are TRUE @@ -3343,7 +3502,7 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBinding) { auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass", ICEROLE_CONTROLLED, kTiebreaker2); - TestChannel ch1(std::move(port1_unique)); + TestChannel ch1(std::move(port1_unique), time_controller_); // Block usage of STUN_ATTR_USE_CANDIDATE so that // ch1.conn() will sent GOOG_PING_REQUEST directly. // This only makes test a bit shorter... @@ -3352,9 +3511,10 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBinding) { ch1.Start(); port2->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_FALSE(port2->Candidates().empty()); ch1.CreateConnection(GetCandidate(port2.get())); @@ -3365,9 +3525,10 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBinding) { // Send ping. ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* request1 = port1->last_stun_msg(); ASSERT_TRUE(GetSupportedGoogPingVersion(request1) && @@ -3416,7 +3577,7 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBindingResponse) { auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass", ICEROLE_CONTROLLED, kTiebreaker2); - TestChannel ch1(std::move(port1_unique)); + TestChannel ch1(std::move(port1_unique), time_controller_); // Block usage of STUN_ATTR_USE_CANDIDATE so that // ch1.conn() will sent GOOG_PING_REQUEST directly. // This only makes test a bit shorter... @@ -3425,9 +3586,10 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBindingResponse) { ch1.Start(); port2->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_FALSE(port2->Candidates().empty()); ch1.CreateConnection(GetCandidate(port2.get())); @@ -3438,9 +3600,10 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBindingResponse) { // Send ping. ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* request1 = port1->last_stun_msg(); ASSERT_TRUE(GetSupportedGoogPingVersion(request1) && @@ -3489,9 +3652,10 @@ TEST_F(PortTest, TestGoogPingUnsupportedVersionInStunBindingResponse) { port2->Reset(); ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); // This should now be a STUN_BINDING...without a kGoogPingVersion const IceMessage* request2 = port1->last_stun_msg(); @@ -3521,7 +3685,7 @@ TEST_F(PortTest, TestChangeInAttributeMakesGoogPingFallsbackToStunBinding) { auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass", ICEROLE_CONTROLLED, kTiebreaker2); - TestChannel ch1(std::move(port1_unique)); + TestChannel ch1(std::move(port1_unique), time_controller_); // Block usage of STUN_ATTR_USE_CANDIDATE so that // ch1.conn() will sent GOOG_PING_REQUEST directly. // This only makes test a bit shorter... @@ -3530,9 +3694,10 @@ TEST_F(PortTest, TestChangeInAttributeMakesGoogPingFallsbackToStunBinding) { ch1.Start(); port2->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_FALSE(port2->Candidates().empty()); ch1.CreateConnection(GetCandidate(port2.get())); @@ -3543,9 +3708,10 @@ TEST_F(PortTest, TestChangeInAttributeMakesGoogPingFallsbackToStunBinding) { // Send ping. ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* msg = port1->last_stun_msg(); auto* con = port2->CreateConnection(port1->Candidates()[0], Port::ORIGIN_MESSAGE); @@ -3567,9 +3733,10 @@ TEST_F(PortTest, TestChangeInAttributeMakesGoogPingFallsbackToStunBinding) { port2->Reset(); ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* msg2 = port1->last_stun_msg(); // It should be a GOOG_PING if both of these are TRUE @@ -3590,9 +3757,10 @@ TEST_F(PortTest, TestChangeInAttributeMakesGoogPingFallsbackToStunBinding) { ch1.conn()->set_use_candidate_attr(!ch1.conn()->use_candidate_attr()); ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* msg3 = port1->last_stun_msg(); // It should be a STUN_BINDING_REQUEST @@ -3613,7 +3781,7 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) { auto port2 = CreateTestPort(kLocalAddr2, "rfrag", "rpass", ICEROLE_CONTROLLED, kTiebreaker2); - TestChannel ch1(std::move(port1_unique)); + TestChannel ch1(std::move(port1_unique), time_controller_); // Block usage of STUN_ATTR_USE_CANDIDATE so that // ch1.conn() will sent GOOG_PING_REQUEST directly. // This only makes test a bit shorter... @@ -3622,9 +3790,10 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) { ch1.Start(); port2->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return ch1.complete_count(); }, Eq(1), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return ch1.complete_count(); }, Eq(1), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); ASSERT_FALSE(port2->Candidates().empty()); ch1.CreateConnection(GetCandidate(port2.get())); @@ -3635,9 +3804,10 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) { // Send ping. ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* msg = port1->last_stun_msg(); auto* con = port2->CreateConnection(port1->Candidates()[0], Port::ORIGIN_MESSAGE); @@ -3659,9 +3829,10 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) { port2->Reset(); ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* msg2 = port1->last_stun_msg(); // It should be a GOOG_PING. @@ -3689,9 +3860,10 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) { port2->Reset(); ch1.Ping(); - ASSERT_THAT(WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), - {.timeout = TimeDelta::Millis(kDefaultTimeout)}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return port1->last_stun_msg(); }, NotNull(), + {.timeout = kDefaultTimeout, .clock = &time_controller_}), + IsRtcOk()); const IceMessage* msg3 = port1->last_stun_msg(); // It should be a STUN_BINDING_REQUEST @@ -3704,7 +3876,6 @@ TEST_F(PortTest, TestErrorResponseMakesGoogPingFallBackToStunBinding) { // port will time out after connectivity is lost, if they are not marked as // "keep alive until pruned." TEST_F(PortTest, TestPortTimeoutIfNotKeptAlive) { - ScopedFakeClock clock; int timeout_delay = 100; auto port1 = CreateUdpPort(kLocalAddr1); ConnectToSignalDestroyed(port1.get()); @@ -3719,23 +3890,22 @@ TEST_F(PortTest, TestPortTimeoutIfNotKeptAlive) { port2->SetIceTiebreaker(kTiebreaker2); // Set up channels and ensure both ports will be deleted. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Simulate a connection that succeeds, and then is destroyed. StartConnectAndStopChannels(&ch1, &ch2); // After the connection is destroyed, the port will be destroyed because // none of them is marked as "keep alive until pruned. - EXPECT_THAT( - WaitUntil([&] { return ports_destroyed(); }, Eq(2), {.clock = &clock}), - IsRtcOk()); + EXPECT_THAT(WaitUntil([&] { return ports_destroyed(); }, Eq(2), + {.clock = &time_controller_}), + IsRtcOk()); } // Test that if after all connection are destroyed, new connections are created // and destroyed again, ports won't be destroyed until a timeout period passes // after the last set of connections are all destroyed. TEST_F(PortTest, TestPortTimeoutAfterNewConnectionCreatedAndDestroyed) { - ScopedFakeClock clock; int timeout_delay = 100; auto port1 = CreateUdpPort(kLocalAddr1); ConnectToSignalDestroyed(port1.get()); @@ -3751,12 +3921,12 @@ TEST_F(PortTest, TestPortTimeoutAfterNewConnectionCreatedAndDestroyed) { port2->SetIceTiebreaker(kTiebreaker2); // Set up channels and ensure both ports will be deleted. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Simulate a connection that succeeds, and then is destroyed. StartConnectAndStopChannels(&ch1, &ch2); - SIMULATED_WAIT(ports_destroyed() > 0, 80, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(80)); EXPECT_EQ(0, ports_destroyed()); // Start the second set of connection and destroy them. @@ -3765,20 +3935,19 @@ TEST_F(PortTest, TestPortTimeoutAfterNewConnectionCreatedAndDestroyed) { ch1.Stop(); ch2.Stop(); - SIMULATED_WAIT(ports_destroyed() > 0, 80, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(80)); EXPECT_EQ(0, ports_destroyed()); // The ports on both sides should be destroyed after timeout. - EXPECT_THAT( - WaitUntil([&] { return ports_destroyed(); }, Eq(2), {.clock = &clock}), - IsRtcOk()); + EXPECT_THAT(WaitUntil([&] { return ports_destroyed(); }, Eq(2), + {.clock = &time_controller_}), + IsRtcOk()); } // This test case verifies that neither the controlling port nor the controlled // port will time out after connectivity is lost if they are marked as "keep // alive until pruned". They will time out after they are pruned. TEST_F(PortTest, TestPortNotTimeoutUntilPruned) { - ScopedFakeClock clock; int timeout_delay = 100; auto port1 = CreateUdpPort(kLocalAddr1); ConnectToSignalDestroyed(port1.get()); @@ -3798,23 +3967,23 @@ TEST_F(PortTest, TestPortNotTimeoutUntilPruned) { port2->set_component(ICE_CANDIDATE_COMPONENT_DEFAULT); // Set up channels and keep the port alive. - TestChannel ch1(std::move(port1)); - TestChannel ch2(std::move(port2)); + TestChannel ch1(std::move(port1), time_controller_); + TestChannel ch2(std::move(port2), time_controller_); // Simulate a connection that succeeds, and then is destroyed. But ports // are kept alive. Ports won't be destroyed. StartConnectAndStopChannels(&ch1, &ch2); ch1.port()->KeepAliveUntilPruned(); ch2.port()->KeepAliveUntilPruned(); - SIMULATED_WAIT(ports_destroyed() > 0, 150, clock); + time_controller_.AdvanceTime(TimeDelta::Millis(150)); EXPECT_EQ(0, ports_destroyed()); // If they are pruned now, they will be destroyed right away. ch1.port()->Prune(); ch2.port()->Prune(); // The ports on both sides should be destroyed after timeout. - EXPECT_THAT( - WaitUntil([&] { return ports_destroyed(); }, Eq(2), {.clock = &clock}), - IsRtcOk()); + EXPECT_THAT(WaitUntil([&] { return ports_destroyed(); }, Eq(2), + {.clock = &time_controller_}), + IsRtcOk()); } TEST_F(PortTest, TestSupportsProtocol) { @@ -3877,7 +4046,7 @@ TEST_F(PortTest, TestAddConnectionWithSameAddress) { EXPECT_EQ(2u, conn_in_use->remote_candidate().generation()); // Make sure the new connection was not deleted. - Thread::Current()->ProcessMessages(300); + time_controller_.AdvanceTime(TimeDelta::Millis(300)); EXPECT_TRUE(port->GetConnection(address) != nullptr); } diff --git a/p2p/base/stun_dictionary_unittest.cc b/p2p/base/stun_dictionary_unittest.cc index 0e292f85eac..506443bee99 100644 --- a/p2p/base/stun_dictionary_unittest.cc +++ b/p2p/base/stun_dictionary_unittest.cc @@ -22,10 +22,11 @@ #include "rtc_base/socket_address.h" #include "test/gtest.h" +namespace webrtc { + namespace { -void Sync(webrtc::StunDictionaryView& dictionary, - webrtc::StunDictionaryWriter& writer) { +void Sync(StunDictionaryView& dictionary, StunDictionaryWriter& writer) { int pending = writer.Pending(); auto delta = writer.CreateDelta(); if (delta == nullptr) { @@ -43,18 +44,17 @@ void Sync(webrtc::StunDictionaryView& dictionary, } } -void XorToggle(webrtc::StunByteStringAttribute& attr, size_t byte) { +void XorToggle(StunByteStringAttribute& attr, size_t byte) { ASSERT_TRUE(attr.length() > byte); uint8_t val = attr.GetByte(byte); uint8_t new_val = val ^ (128 - (byte & 255)); attr.SetByte(byte, new_val); } -std::unique_ptr Crop( - const webrtc::StunByteStringAttribute& attr, +std::unique_ptr Crop( + const StunByteStringAttribute& attr, int new_length) { - auto new_attr = - std::make_unique(attr.type()); + auto new_attr = std::make_unique(attr.type()); std::string content = std::string(attr.string_view()); content.erase(new_length); new_attr->CopyBytes(content); @@ -63,8 +63,6 @@ std::unique_ptr Crop( } // namespace -namespace webrtc { - constexpr int kKey1 = 100; TEST(StunDictionary, CreateEmptyDictionaryWriter) { diff --git a/p2p/base/stun_port_unittest.cc b/p2p/base/stun_port_unittest.cc index fbdb5348454..71d577976fd 100644 --- a/p2p/base/stun_port_unittest.cc +++ b/p2p/base/stun_port_unittest.cc @@ -28,9 +28,9 @@ #include "api/field_trials_view.h" #include "api/packet_socket_factory.h" #include "api/test/mock_async_dns_resolver.h" -#include "api/test/rtc_error_matchers.h" #include "api/transport/stun.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "p2p/base/basic_packet_socket_factory.h" #include "p2p/base/port.h" #include "p2p/base/stun_request.h" @@ -43,8 +43,6 @@ #include "rtc_base/checks.h" #include "rtc_base/crypto_random.h" #include "rtc_base/dscp.h" -#include "rtc_base/fake_clock.h" -#include "rtc_base/gunit.h" #include "rtc_base/ip_address.h" #include "rtc_base/mdns_responder_interface.h" #include "rtc_base/net_helpers.h" @@ -62,6 +60,7 @@ #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" namespace webrtc { @@ -79,23 +78,22 @@ using ::testing::SetArgPointee; const SocketAddress kPrivateIP("192.168.1.12", 0); const SocketAddress kMsdnAddress("unittest-mdns-host-name.local", 0); const SocketAddress kPublicIP("212.116.91.133", 0); -const SocketAddress kNatAddr(kPublicIP.ipaddr(), webrtc::NAT_SERVER_UDP_PORT); +const SocketAddress kNatAddr(kPublicIP.ipaddr(), NAT_SERVER_UDP_PORT); const SocketAddress kStunServerAddr1("34.38.54.120", 5000); const SocketAddress kStunServerAddr2("34.38.54.120", 4000); const SocketAddress kPrivateIPv6("2001:4860:4860::8844", 0); const SocketAddress kPublicIPv6("2002:4860:4860::8844", 5000); -const SocketAddress kNatAddrIPv6(kPublicIPv6.ipaddr(), - webrtc::NAT_SERVER_UDP_PORT); +const SocketAddress kNatAddrIPv6(kPublicIPv6.ipaddr(), NAT_SERVER_UDP_PORT); const SocketAddress kStunServerAddrIPv6Addr("2003:4860:4860::8844", 5000); const SocketAddress kBadAddr("0.0.0.1", 5000); const SocketAddress kIPv6BadAddr("::ffff:0:1", 5000); const SocketAddress kValidHostnameAddr("valid-hostname", 5000); const SocketAddress kBadHostnameAddr("not-a-real-hostname", 5000); -// STUN timeout (with all retries) is webrtc::STUN_TOTAL_TIMEOUT. +// STUN timeout (with all retries) is STUN_TOTAL_TIMEOUT. // Add some margin of error for slow bots. -constexpr int kTimeoutMs = webrtc::STUN_TOTAL_TIMEOUT; +constexpr TimeDelta kTimeout = TimeDelta::Millis(STUN_TOTAL_TIMEOUT + 5000); // stun prio = 100 (srflx) << 24 | 30 (IPv4) << 8 | 256 - 1 (component) constexpr uint32_t kStunCandidatePriority = (100 << 24) | (30 << 8) | (256 - 1); // stun prio = 100 (srflx) << 24 | 40 (IPv6) << 8 | 256 - 1 (component) @@ -105,81 +103,76 @@ constexpr uint64_t kTiebreakerDefault = 44444; struct IPAddressTypeTestConfig { absl::string_view address; - webrtc::IPAddressType address_type; + IPAddressType address_type; }; -// Used by the test framework to print the param value for parameterized tests. -std::string PrintToString(const IPAddressTypeTestConfig& param) { - return std::string(param.address); -} - -class FakeMdnsResponder : public webrtc::MdnsResponderInterface { +class FakeMdnsResponder : public MdnsResponderInterface { public: - void CreateNameForAddress(const webrtc::IPAddress& addr, + void CreateNameForAddress(const IPAddress& addr, NameCreatedCallback callback) override { callback(addr, kMsdnAddress.HostAsSensitiveURIString()); } - void RemoveNameForAddress(const webrtc::IPAddress& addr, + void RemoveNameForAddress(const IPAddress& addr, NameRemovedCallback callback) override {} }; -class FakeMdnsResponderProvider : public webrtc::MdnsResponderProvider { +class FakeMdnsResponderProvider : public MdnsResponderProvider { public: FakeMdnsResponderProvider() : mdns_responder_(new FakeMdnsResponder()) {} - webrtc::MdnsResponderInterface* GetMdnsResponder() const override { + MdnsResponderInterface* GetMdnsResponder() const override { return mdns_responder_.get(); } private: - std::unique_ptr mdns_responder_; + std::unique_ptr mdns_responder_; }; // Base class for tests connecting a StunPort to a fake STUN server -// (webrtc::StunServer). -class StunPortTestBase : public ::testing::Test { +// (StunServer). +class StunPortTest : public ::testing::Test { public: - StunPortTestBase() - : StunPortTestBase(kPrivateIP.ipaddr(), - {kStunServerAddr1, kStunServerAddr2}, - kNatAddr) {} - - StunPortTestBase(const webrtc::IPAddress address, - const std::set& stun_server_addresses, - const webrtc::SocketAddress& nat_server_address) - : env_(CreateTestEnvironment()), - ss_(new webrtc::VirtualSocketServer()), - thread_(ss_.get()), + StunPortTest() + : StunPortTest(kPrivateIP.ipaddr(), + {kStunServerAddr1, kStunServerAddr2}, + kNatAddr) {} + + StunPortTest(const IPAddress address, + const std::set& stun_server_addresses, + const SocketAddress& nat_server_address) + : ss_(std::make_unique()), + time_controller_(Timestamp::Zero(), ss_.get()), + env_(CreateTestEnvironment({.time = &time_controller_})), + network_thread_(time_controller_.GetMainThread()), nat_factory_(ss_.get(), nat_server_address, nat_server_address), nat_socket_factory_(&nat_factory_), mdns_responder_provider_(new FakeMdnsResponderProvider()), - nat_server_(CreateNatServer(nat_server_address, webrtc::NAT_OPEN_CONE)), done_(false), error_(false), stun_keepalive_delay_(TimeDelta::Millis(1)) { + nat_server_ = CreateNatServer(nat_server_address, NAT_OPEN_CONE); network_ = MakeNetwork(address); RTC_CHECK(address.family() == nat_server_address.family()); for (const auto& addr : stun_server_addresses) { RTC_CHECK(addr.family() == address.family()); stun_servers_.push_back( - webrtc::TestStunServer::Create(env_, addr, *ss_, thread_)); + TestStunServer::Create(env_, addr, *ss_, *network_thread_)); } } - std::unique_ptr CreateNatServer(const SocketAddress& addr, - webrtc::NATType type) { - return std::make_unique( - env_, type, thread_, ss_.get(), addr, addr, thread_, ss_.get(), addr); + std::unique_ptr CreateNatServer(const SocketAddress& addr, + NATType type) { + return std::make_unique(env_, type, *network_thread_, ss_.get(), + addr, addr, *network_thread_, ss_.get(), + addr); } - virtual webrtc::PacketSocketFactory* socket_factory() { - return &nat_socket_factory_; - } + virtual PacketSocketFactory* socket_factory() { return &nat_socket_factory_; } - webrtc::SocketServer* ss() const { return ss_.get(); } - webrtc::UDPPort* port() const { return stun_port_.get(); } - webrtc::AsyncPacketSocket* socket() const { return socket_.get(); } + SocketServer* ss() const { return ss_.get(); } + UDPPort* port() const { return stun_port_.get(); } + AsyncPacketSocket* socket() const { return socket_.get(); } bool done() const { return done_; } bool error() const { return error_; } @@ -187,32 +180,32 @@ class StunPortTestBase : public ::testing::Test { return stun_port_->request_manager().HasRequestForTest(msg_type); } - void SetNetworkType(webrtc::AdapterType adapter_type) { + void SetNetworkType(AdapterType adapter_type) { network_->set_type(adapter_type); } - void CreateStunPort(const webrtc::SocketAddress& server_addr, - const webrtc::FieldTrialsView* field_trials = nullptr) { + void CreateStunPort(const SocketAddress& server_addr, + const FieldTrialsView* field_trials = nullptr) { ServerAddresses stun_servers; stun_servers.insert(server_addr); CreateStunPort(stun_servers, field_trials); } void CreateStunPort(const ServerAddresses& stun_servers, - const webrtc::FieldTrialsView* field_trials = nullptr) { + const FieldTrialsView* field_trials = nullptr) { // Overwrite field trials if provided. EnvironmentFactory env_factory(env_); env_factory.Set(field_trials); Environment env = env_factory.Create(); - stun_port_ = webrtc::StunPort::Create( - {.env = env, - .network_thread = &thread_, - .socket_factory = socket_factory(), - .network = network_, - .ice_username_fragment = webrtc::CreateRandomString(16), - .ice_password = webrtc::CreateRandomString(22)}, - 0, 0, stun_servers, std::nullopt); + stun_port_ = + StunPort::Create({.env = env, + .network_thread = network_thread_, + .socket_factory = socket_factory(), + .network = network_, + .ice_username_fragment = CreateRandomString(16), + .ice_password = CreateRandomString(22)}, + 0, 0, stun_servers, std::nullopt); stun_port_->SetIceTiebreaker(kTiebreakerDefault); stun_port_->set_stun_keepalive_delay(stun_keepalive_delay_); // If `stun_keepalive_lifetime_` is not set, let the stun port choose its @@ -245,20 +238,19 @@ class StunPortTestBase : public ::testing::Test { } ASSERT_TRUE(socket_ != nullptr); socket_->RegisterReceivedPacketCallback( - [&](webrtc::AsyncPacketSocket* socket, - const webrtc::ReceivedIpPacket& packet) { + [&](AsyncPacketSocket* socket, const ReceivedIpPacket& packet) { OnReadPacket(socket, packet); }); ServerAddresses stun_servers; stun_servers.insert(server_addr); - stun_port_ = webrtc::UDPPort::Create( - {.env = env_, - .network_thread = &thread_, - .socket_factory = socket_factory(), - .network = network_, - .ice_username_fragment = webrtc::CreateRandomString(16), - .ice_password = webrtc::CreateRandomString(22)}, - socket_.get(), false, std::nullopt); + stun_port_ = + UDPPort::Create({.env = env_, + .network_thread = network_thread_, + .socket_factory = socket_factory(), + .network = network_, + .ice_username_fragment = CreateRandomString(16), + .ice_password = CreateRandomString(22)}, + socket_.get(), false, std::nullopt); stun_port_->set_server_addresses(stun_servers); ASSERT_TRUE(stun_port_ != nullptr); stun_port_->SetIceTiebreaker(kTiebreakerDefault); @@ -268,18 +260,20 @@ class StunPortTestBase : public ::testing::Test { [this](Port* port) { OnPortError(port); }); } - void PrepareAddress() { stun_port_->PrepareAddress(); } + void PrepareAddress() { + stun_port_->PrepareAddress(); + time_controller_.AdvanceTime(TimeDelta::Zero()); + } - void OnReadPacket(webrtc::AsyncPacketSocket* socket, - const webrtc::ReceivedIpPacket& packet) { + void OnReadPacket(AsyncPacketSocket* socket, const ReceivedIpPacket& packet) { stun_port_->HandleIncomingPacket(socket, packet); } void SendData(const char* data, size_t len) { stun_port_->HandleIncomingPacket( - socket_.get(), webrtc::ReceivedIpPacket::CreateFromLegacy( - data, len, /* packet_time_us */ -1, - webrtc::SocketAddress("22.22.22.22", 0))); + socket_.get(), + ReceivedIpPacket::CreateFromLegacy(data, len, /* packet_time_us */ -1, + SocketAddress("22.22.22.22", 0))); } void EnableMdnsObfuscation() { @@ -287,17 +281,16 @@ class StunPortTestBase : public ::testing::Test { } protected: - void OnPortComplete(webrtc::Port* /* port */) { + void OnPortComplete(Port* /* port */) { ASSERT_FALSE(done_); done_ = true; error_ = false; } - void OnPortError(webrtc::Port* /* port */) { + void OnPortError(Port* /* port */) { done_ = true; error_ = true; } - void OnCandidateError(webrtc::Port* /* port */, - const webrtc::IceCandidateErrorEvent& event) { + void OnCandidateError(Port* /* port */, const IceCandidateErrorEvent& event) { error_event_ = event; } void SetKeepaliveDelay(TimeDelta delay) { stun_keepalive_delay_ = delay; } @@ -306,53 +299,45 @@ class StunPortTestBase : public ::testing::Test { stun_keepalive_lifetime_ = lifetime; } - webrtc::Network* MakeNetwork(const webrtc::IPAddress& addr) { + Network* MakeNetwork(const IPAddress& addr) { networks_.emplace_back( std::make_unique("unittest", "unittest", addr, 32)); networks_.back()->AddIP(addr); return networks_.back().get(); } - webrtc::TestStunServer* stun_server_1() { return stun_servers_[0].get(); } - webrtc::TestStunServer* stun_server_2() { return stun_servers_[1].get(); } + TestStunServer* stun_server_1() { return stun_servers_[0].get(); } + TestStunServer* stun_server_2() { return stun_servers_[1].get(); } - webrtc::AutoSocketServerThread& thread() { return thread_; } - webrtc::SocketFactory* nat_factory() { return &nat_factory_; } + Thread& thread() { return *network_thread_; } + SocketFactory* nat_factory() { return &nat_factory_; } - private: + protected: + std::unique_ptr ss_; + GlobalSimulatedTimeController time_controller_; const Environment env_; - std::vector> networks_; - webrtc::Network* network_; + std::vector> networks_; + Network* network_; - std::unique_ptr ss_; - webrtc::AutoSocketServerThread thread_; - webrtc::NATSocketFactory nat_factory_; - webrtc::BasicPacketSocketFactory nat_socket_factory_; + Thread* network_thread_; + NATSocketFactory nat_factory_; + BasicPacketSocketFactory nat_socket_factory_; // Note that stun_port_ can refer to socket_, so must be destroyed // before it. - std::unique_ptr socket_; - std::unique_ptr stun_port_; - std::vector stun_servers_; - std::unique_ptr mdns_responder_provider_; - std::unique_ptr nat_server_; + std::unique_ptr socket_; + std::unique_ptr stun_port_; + std::vector stun_servers_; + std::unique_ptr mdns_responder_provider_; + std::unique_ptr nat_server_; bool done_; bool error_; TimeDelta stun_keepalive_delay_; std::optional stun_keepalive_lifetime_; protected: - webrtc::IceCandidateErrorEvent error_event_; -}; - -class StunPortTestWithRealClock : public StunPortTestBase {}; - -class FakeClockBase { - public: - webrtc::ScopedFakeClock fake_clock; + IceCandidateErrorEvent error_event_; }; -class StunPortTest : public FakeClockBase, public StunPortTestBase {}; - // Test that we can create a STUN port. TEST_F(StunPortTest, TestCreateStunPort) { CreateStunPort(kStunServerAddr1); @@ -371,11 +356,8 @@ TEST_F(StunPortTest, TestCreateUdpPort) { TEST_F(StunPortTest, TestPrepareAddress) { CreateStunPort(kStunServerAddr1); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address())); std::string expected_server_url = "stun:" + kStunServerAddr1.ToString(); @@ -386,19 +368,11 @@ TEST_F(StunPortTest, TestPrepareAddress) { TEST_F(StunPortTest, TestPrepareAddressFail) { CreateStunPort(kBadAddr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_TRUE(error()); EXPECT_EQ(0U, port()->Candidates().size()); - EXPECT_THAT( - webrtc::WaitUntil([&] { return error_event_.error_code; }, - Eq(webrtc::STUN_ERROR_SERVER_NOT_REACHABLE), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE); EXPECT_NE(error_event_.error_text.find('.'), std::string::npos); EXPECT_NE(error_event_.address.find(kPrivateIP.HostAsSensitiveURIString()), std::string::npos); @@ -411,11 +385,8 @@ TEST_F(StunPortTest, TestPrepareAddressFail) { TEST_F(StunPortTest, TestServerAddressFamilyMismatch) { CreateStunPort(kStunServerAddrIPv6Addr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_TRUE(error()); EXPECT_EQ(0U, port()->Candidates().size()); EXPECT_EQ(0, error_event_.error_code); @@ -426,42 +397,35 @@ class StunPortWithMockDnsResolverTest : public StunPortTest { StunPortWithMockDnsResolverTest() : StunPortTest(), socket_factory_(nat_factory()) {} - webrtc::PacketSocketFactory* socket_factory() override { - return &socket_factory_; - } + PacketSocketFactory* socket_factory() override { return &socket_factory_; } void SetDnsResolverExpectations( - webrtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) { + MockDnsResolvingPacketSocketFactory::Expectations expectations) { socket_factory_.SetExpectations(expectations); } private: - webrtc::MockDnsResolvingPacketSocketFactory socket_factory_; + MockDnsResolvingPacketSocketFactory socket_factory_; }; // Test that we can get an address from a STUN server specified by a hostname. TEST_F(StunPortWithMockDnsResolverTest, TestPrepareAddressHostname) { - SetDnsResolverExpectations( - [](webrtc::MockAsyncDnsResolver* resolver, - webrtc::MockAsyncDnsResolverResult* resolver_result) { - EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _)) - .WillOnce([](const webrtc::SocketAddress& /* addr */, - int /* family */, - absl::AnyInvocable callback) { callback(); }); - - EXPECT_CALL(*resolver, result) - .WillRepeatedly(ReturnPointee(resolver_result)); - EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); - EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) - .WillOnce(DoAll(SetArgPointee<1>(kStunServerAddr1), Return(true))); - }); + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _)) + .WillOnce([](const SocketAddress& /* addr */, int /* family */, + absl::AnyInvocable callback) { callback(); }); + + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); + EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) + .WillOnce(DoAll(SetArgPointee<1>(kStunServerAddr1), Return(true))); + }); CreateStunPort(kValidHostnameAddr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address())); EXPECT_EQ(kStunCandidatePriority, port()->Candidates()[0].priority()); @@ -471,47 +435,47 @@ TEST_F(StunPortWithMockDnsResolverTest, TestPrepareAddressHostnameWithPriorityAdjustment) { FieldTrials field_trials = CreateTestFieldTrials( "WebRTC-IncreaseIceCandidatePriorityHostSrflx/Enabled/"); - SetDnsResolverExpectations( - [](webrtc::MockAsyncDnsResolver* resolver, - webrtc::MockAsyncDnsResolverResult* resolver_result) { - EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _)) - .WillOnce([](const webrtc::SocketAddress& /* addr */, - int /* family */, - absl::AnyInvocable callback) { callback(); }); - EXPECT_CALL(*resolver, result) - .WillRepeatedly(ReturnPointee(resolver_result)); - EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); - EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) - .WillOnce(DoAll(SetArgPointee<1>(kStunServerAddr1), Return(true))); - }); + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET, _)) + .WillOnce([](const SocketAddress& /* addr */, int /* family */, + absl::AnyInvocable callback) { callback(); }); + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); + EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) + .WillOnce(DoAll(SetArgPointee<1>(kStunServerAddr1), Return(true))); + }); CreateStunPort(kValidHostnameAddr, &field_trials); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address())); - EXPECT_EQ(kStunCandidatePriority + (webrtc::kMaxTurnServers << 8), + EXPECT_EQ(kStunCandidatePriority + (kMaxTurnServers << 8), port()->Candidates()[0].priority()); } // Test that we handle hostname lookup failures properly. -TEST_F(StunPortTestWithRealClock, TestPrepareAddressHostnameFail) { +// Test that we fail to get an address from a STUN server specified by an +// invalid hostname. +TEST_F(StunPortWithMockDnsResolverTest, TestPrepareAddressHostnameFail) { + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kBadHostnameAddr, /*family=*/AF_INET, _)) + .WillOnce([](const SocketAddress& /* addr */, int /* family */, + absl::AnyInvocable callback) { callback(); }); + + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(-2)); + }); CreateStunPort(kBadHostnameAddr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs)}), - webrtc::IsRtcOk()); - EXPECT_TRUE(error()); + EXPECT_TRUE(WaitUntil([this] { return error(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_EQ(0U, port()->Candidates().size()); - EXPECT_THAT( - webrtc::WaitUntil([&] { return error_event_.error_code; }, - Eq(webrtc::STUN_ERROR_SERVER_NOT_REACHABLE), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs)}), - webrtc::IsRtcOk()); + EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE); } // This test verifies keepalive response messages don't result in @@ -520,14 +484,11 @@ TEST_F(StunPortTest, TestKeepAliveResponse) { SetKeepaliveDelay(TimeDelta::Millis(500)); CreateStunPort(kStunServerAddr1); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPublicIP.EqualIPs(port()->Candidates()[0].address())); - SIMULATED_WAIT(false, 1000, fake_clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1000)); EXPECT_EQ(1U, port()->Candidates().size()); } @@ -535,11 +496,8 @@ TEST_F(StunPortTest, TestKeepAliveResponse) { TEST_F(StunPortTest, TestSharedSocketPrepareAddress) { CreateSharedUdpPort(kStunServerAddr1, nullptr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(2U, port()->Candidates().size()); EXPECT_EQ(port()->Candidates()[0].type(), IceCandidateType::kHost); EXPECT_TRUE(kPrivateIP.EqualIPs(port()->Candidates()[0].address())); @@ -550,20 +508,28 @@ TEST_F(StunPortTest, TestSharedSocketPrepareAddress) { // Test that we still get a local candidate with invalid stun server hostname. // Also verifing that UDPPort can receive packets when stun address can't be // resolved. -TEST_F(StunPortTestWithRealClock, +TEST_F(StunPortWithMockDnsResolverTest, TestSharedSocketPrepareAddressInvalidHostname) { + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kBadHostnameAddr, /*family=*/AF_INET, _)) + .WillOnce([](const SocketAddress& /* addr */, int /* family */, + absl::AnyInvocable callback) { callback(); }); + + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(-2)); + }); CreateSharedUdpPort(kBadHostnameAddr, nullptr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs)}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPrivateIP.EqualIPs(port()->Candidates()[0].address())); // Send data to port after it's ready. This is to make sure, UDP port can // handle data with unresolved stun server address. - std::string data = "some random data, sending to webrtc::Port."; + std::string data = "some random data, sending to Port."; SendData(data.c_str(), data.length()); // No crash is success. } @@ -574,11 +540,8 @@ TEST_F(StunPortTest, TestStunCandidateGeneratedWithMdnsObfuscationEnabled) { EnableMdnsObfuscation(); CreateSharedUdpPort(kStunServerAddr1, nullptr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(2U, port()->Candidates().size()); // One of the generated candidates is a local candidate and the other is a @@ -607,11 +570,8 @@ TEST_F(StunPortTest, TestNoDuplicatedAddressWithTwoStunServers) { CreateStunPort(stun_servers); EXPECT_EQ(IceCandidateType::kSrflx, port()->Type()); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_EQ(1U, port()->Candidates().size()); EXPECT_EQ(port()->Candidates()[0].relay_protocol(), ""); } @@ -625,18 +585,11 @@ TEST_F(StunPortTest, TestMultipleStunServersWithBadServer) { CreateStunPort(stun_servers); EXPECT_EQ(IceCandidateType::kSrflx, port()->Type()); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_EQ(1U, port()->Candidates().size()); std::string server_url = "stun:" + kBadAddr.ToString(); - ASSERT_THAT( - webrtc::WaitUntil([&] { return error_event_.url; }, Eq(server_url), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_EQ(error_event_.url, server_url); } // Test that two candidates are allocated if the two STUN servers return @@ -653,11 +606,8 @@ TEST_F(StunPortTest, TestTwoCandidatesWithTwoStunServersAcrossNat) { CreateStunPort(stun_servers); EXPECT_EQ(IceCandidateType::kSrflx, port()->Type()); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_EQ(2U, port()->Candidates().size()); EXPECT_EQ(port()->Candidates()[0].relay_protocol(), ""); EXPECT_EQ(port()->Candidates()[1].relay_protocol(), ""); @@ -671,11 +621,11 @@ TEST_F(StunPortTest, TestStunPortGetStunKeepaliveLifetime) { CreateStunPort(kStunServerAddr1); EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity()); // Lifetime for the cellular network is `kHighCostPortKeepaliveLifetime` - SetNetworkType(webrtc::ADAPTER_TYPE_CELLULAR); + SetNetworkType(ADAPTER_TYPE_CELLULAR); EXPECT_EQ(port()->stun_keepalive_lifetime(), kHighCostPortKeepaliveLifetime); // Lifetime for the wifi network is infinite. - SetNetworkType(webrtc::ADAPTER_TYPE_WIFI); + SetNetworkType(ADAPTER_TYPE_WIFI); CreateStunPort(kStunServerAddr2); EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity()); } @@ -688,11 +638,11 @@ TEST_F(StunPortTest, TestUdpPortGetStunKeepaliveLifetime) { CreateSharedUdpPort(kStunServerAddr1, nullptr); EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity()); // Lifetime for the cellular network is `kHighCostPortKeepaliveLifetime`. - SetNetworkType(webrtc::ADAPTER_TYPE_CELLULAR); + SetNetworkType(ADAPTER_TYPE_CELLULAR); EXPECT_EQ(port()->stun_keepalive_lifetime(), kHighCostPortKeepaliveLifetime); // Lifetime for the wifi network type is infinite. - SetNetworkType(webrtc::ADAPTER_TYPE_WIFI); + SetNetworkType(ADAPTER_TYPE_WIFI); CreateSharedUdpPort(kStunServerAddr2, nullptr); EXPECT_EQ(port()->stun_keepalive_lifetime(), TimeDelta::PlusInfinity()); } @@ -704,16 +654,11 @@ TEST_F(StunPortTest, TestStunBindingRequestShortLifetime) { SetKeepaliveLifetime(TimeDelta::Millis(100)); CreateStunPort(kStunServerAddr1); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); - EXPECT_THAT( - webrtc::WaitUntil( - [&] { return !HasPendingRequest(webrtc::STUN_BINDING_REQUEST); }, - IsTrue(), {.clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); + EXPECT_TRUE( + WaitUntil([&] { return !HasPendingRequest(STUN_BINDING_REQUEST); }, + {.timeout = kTimeout, .clock = &time_controller_})); } // Test that by default, the STUN binding requests will last for a long time. @@ -721,16 +666,10 @@ TEST_F(StunPortTest, TestStunBindingRequestLongLifetime) { SetKeepaliveDelay(TimeDelta::Millis(101)); CreateStunPort(kStunServerAddr1); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); - EXPECT_THAT( - webrtc::WaitUntil( - [&] { return HasPendingRequest(webrtc::STUN_BINDING_REQUEST); }, - IsTrue(), {.clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); + EXPECT_TRUE(WaitUntil([&] { return HasPendingRequest(STUN_BINDING_REQUEST); }, + {.timeout = kTimeout, .clock = &time_controller_})); } class StunPortIPAddressTypeMetricsTest @@ -738,51 +677,46 @@ class StunPortIPAddressTypeMetricsTest public ::testing::WithParamInterface {}; TEST_P(StunPortIPAddressTypeMetricsTest, TestIPAddressTypeMetrics) { - SetDnsResolverExpectations( - [](webrtc::MockAsyncDnsResolver* resolver, - webrtc::MockAsyncDnsResolverResult* resolver_result) { - EXPECT_CALL(*resolver, Start(SocketAddress("localhost", 5000), - /*family=*/AF_INET, _)) - .WillOnce([](const webrtc::SocketAddress& /* addr */, - int /* family */, - absl::AnyInvocable callback) { callback(); }); - - EXPECT_CALL(*resolver, result) - .WillRepeatedly(ReturnPointee(resolver_result)); - EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); - EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) - .WillOnce(DoAll(SetArgPointee<1>(SocketAddress("127.0.0.1", 5000)), - Return(true))); - }); - - webrtc::metrics::Reset(); + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(SocketAddress("localhost", 5000), + /*family=*/AF_INET, _)) + .WillOnce([](const SocketAddress& /* addr */, int /* family */, + absl::AnyInvocable callback) { callback(); }); + + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); + EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) + .WillOnce(DoAll(SetArgPointee<1>(SocketAddress("127.0.0.1", 5000)), + Return(true))); + }); + + metrics::Reset(); CreateStunPort({GetParam().address, 5000}); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); auto samples = - webrtc::metrics::Samples("WebRTC.PeerConnection.Stun.ServerAddressType"); + metrics::Samples("WebRTC.PeerConnection.Stun.ServerAddressType"); ASSERT_EQ(samples.size(), 1u); EXPECT_EQ(samples[static_cast(GetParam().address_type)], 1); } const IPAddressTypeTestConfig kAllIPAddressTypeTestConfigs[] = { - {.address = "127.0.0.1", .address_type = webrtc::IPAddressType::kLoopback}, - {.address = "localhost", .address_type = webrtc::IPAddressType::kLoopback}, - {.address = "10.0.0.3", .address_type = webrtc::IPAddressType::kPrivate}, - {.address = "1.1.1.1", .address_type = webrtc::IPAddressType::kPublic}, + {.address = "127.0.0.1", .address_type = IPAddressType::kLoopback}, + {.address = "localhost", .address_type = IPAddressType::kLoopback}, + {.address = "10.0.0.3", .address_type = IPAddressType::kPrivate}, + {.address = "1.1.1.1", .address_type = IPAddressType::kPublic}, }; INSTANTIATE_TEST_SUITE_P(All, StunPortIPAddressTypeMetricsTest, ::testing::ValuesIn(kAllIPAddressTypeTestConfigs)); -class MockAsyncPacketSocket : public webrtc::AsyncPacketSocket { +class MockAsyncPacketSocket : public AsyncPacketSocket { public: ~MockAsyncPacketSocket() override = default; @@ -792,7 +726,7 @@ class MockAsyncPacketSocket : public webrtc::AsyncPacketSocket { Send, (const void* pv, size_t cb, - const webrtc::AsyncSocketPacketOptions& options), + const AsyncSocketPacketOptions& options), (override)); MOCK_METHOD(int, @@ -800,18 +734,12 @@ class MockAsyncPacketSocket : public webrtc::AsyncPacketSocket { (const void* pv, size_t cb, const SocketAddress& addr, - const webrtc::AsyncSocketPacketOptions& options), + const AsyncSocketPacketOptions& options), (override)); MOCK_METHOD(int, Close, (), (override)); MOCK_METHOD(State, GetState, (), (const, override)); - MOCK_METHOD(int, - GetOption, - (webrtc::Socket::Option opt, int* value), - (override)); - MOCK_METHOD(int, - SetOption, - (webrtc::Socket::Option opt, int value), - (override)); + MOCK_METHOD(int, GetOption, (Socket::Option opt, int* value), (override)); + MOCK_METHOD(int, SetOption, (Socket::Option opt, int value), (override)); MOCK_METHOD(int, GetError, (), (const, override)); MOCK_METHOD(void, SetError, (int error), (override)); }; @@ -833,6 +761,7 @@ TEST_F(StunPortTest, TestStunPacketsHaveDscpPacketOption) { Eq(DSCP_NO_CHANGE)))) .WillOnce(Return(100)); PrepareAddress(); + port()->request_manager().Clear(); // Once it is set transport wide, they should inherit that value. port()->SetOption(Socket::OPT_DSCP, DSCP_AF41); @@ -841,32 +770,25 @@ TEST_F(StunPortTest, TestStunPacketsHaveDscpPacketOption) { SendTo(_, _, _, Field(&AsyncSocketPacketOptions::dscp, Eq(DSCP_AF41)))) .WillRepeatedly(Return(100)); - EXPECT_TRUE(WaitUntil( - [&] { return done(); }, - {.timeout = TimeDelta::Millis(kTimeoutMs), .clock = &fake_clock})); + PrepareAddress(); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); } -class StunIPv6PortTestBase : public StunPortTestBase { +class StunIPv6PortTest : public StunPortTest { public: - StunIPv6PortTestBase() - : StunPortTestBase(kPrivateIPv6.ipaddr(), - {kStunServerAddrIPv6Addr}, - kNatAddrIPv6) {} + StunIPv6PortTest() + : StunPortTest(kPrivateIPv6.ipaddr(), + {kStunServerAddrIPv6Addr}, + kNatAddrIPv6) {} }; -class StunIPv6PortTestWithRealClock : public StunIPv6PortTestBase {}; - -class StunIPv6PortTest : public FakeClockBase, public StunIPv6PortTestBase {}; - // Test that we can get an address from a STUN server. TEST_F(StunIPv6PortTest, TestPrepareAddress) { CreateStunPort(kStunServerAddrIPv6Addr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPublicIPv6.EqualIPs(port()->Candidates()[0].address())); std::string expected_server_url = "stun:2003:4860:4860::8844:5000"; @@ -877,19 +799,11 @@ TEST_F(StunIPv6PortTest, TestPrepareAddress) { TEST_F(StunIPv6PortTest, TestPrepareAddressFail) { CreateStunPort(kIPv6BadAddr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_TRUE(error()); EXPECT_EQ(0U, port()->Candidates().size()); - EXPECT_THAT( - webrtc::WaitUntil([&] { return error_event_.error_code; }, - Eq(webrtc::STUN_ERROR_SERVER_NOT_REACHABLE), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE); EXPECT_NE(error_event_.error_text.find('.'), std::string::npos); EXPECT_NE(error_event_.address.find(kPrivateIPv6.HostAsSensitiveURIString()), std::string::npos); @@ -902,75 +816,68 @@ TEST_F(StunIPv6PortTest, TestPrepareAddressFail) { TEST_F(StunIPv6PortTest, TestServerAddressFamilyMismatch) { CreateStunPort(kStunServerAddr1); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); - EXPECT_TRUE(error()); + EXPECT_TRUE(WaitUntil([this] { return error(); }, + {.timeout = kTimeout, .clock = &time_controller_})); EXPECT_EQ(0U, port()->Candidates().size()); EXPECT_EQ(0, error_event_.error_code); } -// Test that we handle hostname lookup failures properly with a real clock. -TEST_F(StunIPv6PortTestWithRealClock, TestPrepareAddressHostnameFail) { - CreateStunPort(kBadHostnameAddr); - PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs)}), - webrtc::IsRtcOk()); - EXPECT_TRUE(error()); - EXPECT_EQ(0U, port()->Candidates().size()); - EXPECT_THAT( - webrtc::WaitUntil([&] { return error_event_.error_code; }, - Eq(webrtc::STUN_ERROR_SERVER_NOT_REACHABLE), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs)}), - webrtc::IsRtcOk()); -} - class StunIPv6PortTestWithMockDnsResolver : public StunIPv6PortTest { public: StunIPv6PortTestWithMockDnsResolver() : StunIPv6PortTest(), socket_factory_(ss()) {} - webrtc::PacketSocketFactory* socket_factory() override { - return &socket_factory_; - } + PacketSocketFactory* socket_factory() override { return &socket_factory_; } void SetDnsResolverExpectations( - webrtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) { + MockDnsResolvingPacketSocketFactory::Expectations expectations) { socket_factory_.SetExpectations(expectations); } private: - webrtc::MockDnsResolvingPacketSocketFactory socket_factory_; + MockDnsResolvingPacketSocketFactory socket_factory_; }; +// Test that we fail to get an address from a STUN server specified by an +// invalid hostname. +TEST_F(StunIPv6PortTestWithMockDnsResolver, TestPrepareAddressHostnameFail) { + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kBadHostnameAddr, /*family=*/AF_INET6, _)) + .WillOnce([](const SocketAddress& /* addr */, int /* family */, + absl::AnyInvocable callback) { callback(); }); + + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(-2)); + }); + CreateStunPort(kBadHostnameAddr); + PrepareAddress(); + EXPECT_TRUE(WaitUntil([this] { return error(); }, + {.timeout = kTimeout, .clock = &time_controller_})); + EXPECT_EQ(0U, port()->Candidates().size()); + EXPECT_EQ(error_event_.error_code, STUN_ERROR_SERVER_NOT_REACHABLE); +} + // Test that we can get an address from a STUN server specified by a hostname. TEST_F(StunIPv6PortTestWithMockDnsResolver, TestPrepareAddressHostname) { - SetDnsResolverExpectations( - [](webrtc::MockAsyncDnsResolver* resolver, - webrtc::MockAsyncDnsResolverResult* resolver_result) { - EXPECT_CALL(*resolver, - Start(kValidHostnameAddr, /*family=*/AF_INET6, _)) - .WillOnce([](const webrtc::SocketAddress& addr, int family, - absl::AnyInvocable callback) { callback(); }); - - EXPECT_CALL(*resolver, result) - .WillRepeatedly(ReturnPointee(resolver_result)); - EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); - EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _)) - .WillOnce( - DoAll(SetArgPointee<1>(kStunServerAddrIPv6Addr), Return(true))); - }); + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET6, _)) + .WillOnce([](const SocketAddress& addr, int family, + absl::AnyInvocable callback) { callback(); }); + + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); + EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _)) + .WillOnce( + DoAll(SetArgPointee<1>(kStunServerAddrIPv6Addr), Return(true))); + }); CreateStunPort(kValidHostnameAddr); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPrivateIPv6.EqualIPs(port()->Candidates()[0].address())); EXPECT_EQ(kIPv6StunCandidatePriority, port()->Candidates()[0].priority()); @@ -981,30 +888,25 @@ TEST_F(StunIPv6PortTestWithMockDnsResolver, TestPrepareAddressHostnameWithPriorityAdjustment) { FieldTrials field_trials = CreateTestFieldTrials( "WebRTC-IncreaseIceCandidatePriorityHostSrflx/Enabled/"); - SetDnsResolverExpectations( - [](webrtc::MockAsyncDnsResolver* resolver, - webrtc::MockAsyncDnsResolverResult* resolver_result) { - EXPECT_CALL(*resolver, - Start(kValidHostnameAddr, /*family=*/AF_INET6, _)) - .WillOnce([](const webrtc::SocketAddress& addr, int family, - absl::AnyInvocable callback) { callback(); }); - EXPECT_CALL(*resolver, result) - .WillRepeatedly(ReturnPointee(resolver_result)); - EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); - EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _)) - .WillOnce( - DoAll(SetArgPointee<1>(kStunServerAddrIPv6Addr), Return(true))); - }); + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kValidHostnameAddr, /*family=*/AF_INET6, _)) + .WillOnce([](const SocketAddress& addr, int family, + absl::AnyInvocable callback) { callback(); }); + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillOnce(Return(0)); + EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _)) + .WillOnce( + DoAll(SetArgPointee<1>(kStunServerAddrIPv6Addr), Return(true))); + }); CreateStunPort(kValidHostnameAddr, &field_trials); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kPrivateIPv6.EqualIPs(port()->Candidates()[0].address())); - EXPECT_EQ(kIPv6StunCandidatePriority + (webrtc::kMaxTurnServers << 8), + EXPECT_EQ(kIPv6StunCandidatePriority + (kMaxTurnServers << 8), port()->Candidates()[0].priority()); } @@ -1013,28 +915,24 @@ class StunIPv6PortIPAddressTypeMetricsTest public ::testing::WithParamInterface {}; TEST_P(StunIPv6PortIPAddressTypeMetricsTest, TestIPAddressTypeMetrics) { - webrtc::metrics::Reset(); + metrics::Reset(); CreateStunPort({GetParam().address, 5000}); PrepareAddress(); - EXPECT_THAT( - webrtc::WaitUntil([&] { return done(); }, IsTrue(), - {.timeout = webrtc::TimeDelta::Millis(kTimeoutMs), - .clock = &fake_clock}), - webrtc::IsRtcOk()); + EXPECT_TRUE(WaitUntil([this] { return done(); }, + {.timeout = kTimeout, .clock = &time_controller_})); auto samples = - webrtc::metrics::Samples("WebRTC.PeerConnection.Stun.ServerAddressType"); + metrics::Samples("WebRTC.PeerConnection.Stun.ServerAddressType"); ASSERT_EQ(samples.size(), 1u); EXPECT_EQ(samples[static_cast(GetParam().address_type)], 1); } const IPAddressTypeTestConfig kAllIPv6AddressTypeTestConfigs[] = { - {.address = "::1", .address_type = webrtc::IPAddressType::kLoopback}, + {.address = "::1", .address_type = IPAddressType::kLoopback}, {.address = "fd00:4860:4860::8844", - .address_type = webrtc::IPAddressType::kPrivate}, - {.address = "2001:4860:4860::8888", - .address_type = webrtc::IPAddressType::kPublic}, + .address_type = IPAddressType::kPrivate}, + {.address = "2001:4860:4860::8888", .address_type = IPAddressType::kPublic}, }; INSTANTIATE_TEST_SUITE_P(All, diff --git a/p2p/base/stun_request.cc b/p2p/base/stun_request.cc index c5867be0cd3..3afabe1e42f 100644 --- a/p2p/base/stun_request.cc +++ b/p2p/base/stun_request.cc @@ -15,12 +15,12 @@ #include #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" @@ -191,7 +191,7 @@ bool StunRequestManager::empty() const { return requests_.empty(); } -bool StunRequestManager::CheckResponse(ArrayView payload) { +bool StunRequestManager::CheckResponse(std::span payload) { RTC_DCHECK_RUN_ON(thread_); // Check the appropriate bytes of the stream to see if they match the // transaction ID of a response we are expecting. diff --git a/p2p/base/stun_request.h b/p2p/base/stun_request.h index b16720d8e29..ccd284a9975 100644 --- a/p2p/base/stun_request.h +++ b/p2p/base/stun_request.h @@ -16,9 +16,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" @@ -70,7 +70,7 @@ class StunRequestManager { // Determines whether the given message is a response to one of the // outstanding requests, and if so, processes it appropriately. bool CheckResponse(StunMessage* msg); - bool CheckResponse(ArrayView payload); + bool CheckResponse(std::span payload); // Called from a StunRequest when a timeout occurs. void OnRequestTimedOut(StunRequest* request); diff --git a/p2p/base/stun_request_unittest.cc b/p2p/base/stun_request_unittest.cc index d819d939cbe..a0998a3239f 100644 --- a/p2p/base/stun_request_unittest.cc +++ b/p2p/base/stun_request_unittest.cc @@ -10,24 +10,22 @@ #include "p2p/base/stun_request.h" +#include #include -#include #include #include #include -#include #include "api/environment/environment.h" #include "api/test/rtc_error_matchers.h" #include "api/transport/stun.h" #include "api/units/time_delta.h" -#include "rtc_base/fake_clock.h" -#include "rtc_base/gunit.h" +#include "api/units/timestamp.h" #include "rtc_base/logging.h" -#include "rtc_base/thread.h" #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" namespace webrtc { @@ -43,20 +41,14 @@ std::unique_ptr CreateStunMessage( return msg; } -int TotalDelay(int sends) { - std::vector delays = {0, 250, 750, 1750, 3750, - 7750, 15750, 23750, 31750, 39750}; - return delays[sends]; -} -} // namespace - class StunRequestThunker; class StunRequestTest : public ::testing::Test { public: StunRequestTest() - : env_(CreateTestEnvironment()), - manager_(Thread::Current(), + : time_controller_(Timestamp::Seconds(12345)), + env_(CreateTestEnvironment({.time = &time_controller_})), + manager_(time_controller_.GetMainThread(), [this](const void* data, size_t size, StunRequest* request) { OnSendPacket(data, size, request); }), @@ -83,7 +75,7 @@ class StunRequestTest : public ::testing::Test { virtual void OnTimeout() { timeout_ = true; } protected: - AutoThread main_thread_; + GlobalSimulatedTimeController time_controller_; const Environment env_; StunRequestManager manager_; int request_count_; @@ -164,25 +156,30 @@ TEST_F(StunRequestTest, TestUnexpected) { EXPECT_FALSE(timeout_); } -// Test that requests are sent at the right times. TEST_F(StunRequestTest, TestBackoff) { - ScopedFakeClock fake_clock; std::unique_ptr request = CreateStunRequest(); std::unique_ptr res = request->CreateResponseMessage(STUN_BINDING_RESPONSE); + constexpr auto kTotalDelays = std::to_array( + {TimeDelta::Zero(), TimeDelta::Millis(250), TimeDelta::Millis(750), + TimeDelta::Millis(1750), TimeDelta::Millis(3750), + TimeDelta::Millis(7750), TimeDelta::Millis(15750), + TimeDelta::Millis(23750), TimeDelta::Millis(31750), + TimeDelta::Millis(39750)}); - int64_t start = env_.clock().TimeInMilliseconds(); manager_.Send(std::move(request)); - for (int i = 0; i < 9; ++i) { + Timestamp start = env_.clock().CurrentTime(); + for (size_t i = 0; i < kTotalDelays.size() - 1; ++i) { EXPECT_THAT(WaitUntil([&] { return request_count_; }, Ne(i), {.timeout = TimeDelta::Millis(STUN_TOTAL_TIMEOUT), - .clock = &fake_clock}), + .clock = &time_controller_}), IsRtcOk()); - int64_t elapsed = env_.clock().TimeInMilliseconds() - start; - RTC_DLOG(LS_INFO) << "STUN request #" << (i + 1) << " sent at " << elapsed - << " ms"; - EXPECT_EQ(TotalDelay(i), elapsed); + TimeDelta elapsed = env_.clock().CurrentTime() - start; + RTC_DLOG(LS_INFO) << "STUN request #" << (i + 1) << " sent at " << elapsed; + EXPECT_EQ(kTotalDelays[i], elapsed); } + ASSERT_EQ(request_count_, 9); + EXPECT_TRUE(manager_.CheckResponse(res.get())); EXPECT_TRUE(response_ == res.get()); @@ -193,13 +190,12 @@ TEST_F(StunRequestTest, TestBackoff) { // Test that we timeout properly if no response is received. TEST_F(StunRequestTest, TestTimeout) { - ScopedFakeClock fake_clock; std::unique_ptr request = CreateStunRequest(); std::unique_ptr res = request->CreateResponseMessage(STUN_BINDING_RESPONSE); manager_.Send(std::move(request)); - SIMULATED_WAIT(false, STUN_TOTAL_TIMEOUT, fake_clock); + time_controller_.AdvanceTime(TimeDelta::Millis(STUN_TOTAL_TIMEOUT)); EXPECT_FALSE(manager_.CheckResponse(res.get())); EXPECT_TRUE(response_ == nullptr); @@ -284,4 +280,5 @@ TEST_F(StunRequestReentranceTest, TestError) { EXPECT_FALSE(timeout_); } +} // namespace } // namespace webrtc diff --git a/p2p/base/tcp_port_unittest.cc b/p2p/base/tcp_port_unittest.cc index 6d50e41ff31..97de11c021c 100644 --- a/p2p/base/tcp_port_unittest.cc +++ b/p2p/base/tcp_port_unittest.cc @@ -34,10 +34,10 @@ #include "rtc_base/network/sent_packet.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" -#include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" using ::testing::Eq; @@ -105,7 +105,7 @@ class TCPPortTest : public ::testing::Test { int port_number = 0) { auto port = std::unique_ptr( TCPPort::Create({.env = env_, - .network_thread = &main_, + .network_thread = main_.task_queue(), .socket_factory = &socket_factory_, .network = MakeNetwork(addr), .ice_username_fragment = username_, @@ -118,7 +118,7 @@ class TCPPortTest : public ::testing::Test { std::unique_ptr CreateTCPPort(const webrtc::Network* network) { auto port = std::unique_ptr( TCPPort::Create({.env = env_, - .network_thread = &main_, + .network_thread = main_.task_queue(), .socket_factory = &socket_factory_, .network = network, .ice_username_fragment = username_, @@ -135,7 +135,7 @@ class TCPPortTest : public ::testing::Test { // vector so that when it grows, pointers aren't invalidated. std::list networks_; std::unique_ptr ss_; - webrtc::AutoSocketServerThread main_; + webrtc::test::RunLoop main_; webrtc::BasicPacketSocketFactory socket_factory_; std::string username_; std::string password_; @@ -431,7 +431,7 @@ TEST_F(TCPPortTest, SignalSentPacketAfterReconnect) { {.timeout = webrtc::TimeDelta::Millis(kTimeout)}), webrtc::IsRtcOk()); // Wait a bit for the Stun response to be received. - webrtc::Thread::Current()->ProcessMessages(100); + main_.RunFor(webrtc::TimeDelta::Millis(100)); // After the Stun Ping response has been received, packets can be sent again // and SignalSentPacket should be invoked. diff --git a/p2p/base/turn_port.cc b/p2p/base/turn_port.cc index 2c10d8cdac1..1a6f3a70787 100644 --- a/p2p/base/turn_port.cc +++ b/p2p/base/turn_port.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,6 @@ #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/local_network_access_permission.h" #include "api/packet_socket_factory.h" @@ -92,6 +92,8 @@ static int GetRelayPreference(ProtocolType proto) { return ICE_TYPE_PREFERENCE_RELAY_TCP; case PROTO_TLS: return ICE_TYPE_PREFERENCE_RELAY_TLS; + case PROTO_DTLS: + return ICE_TYPE_PREFERENCE_RELAY_DTLS; default: RTC_DCHECK(proto == PROTO_UDP); return ICE_TYPE_PREFERENCE_RELAY_UDP; @@ -438,6 +440,23 @@ bool TurnPort::CreateTurnClientSocket() { env(), SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port()); socket_ = owned_socket_.get(); + } else if (server_address_.proto == PROTO_DTLS) { + RTC_DCHECK(!SharedSocket()); + int opts = PacketSocketFactory::OPT_STUN; + if (tls_cert_policy_ == TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK) { + opts |= PacketSocketFactory::OPT_DTLS_INSECURE; + } else { + opts |= PacketSocketFactory::OPT_DTLS; + } + PacketSocketTcpOptions udp_options; + udp_options.opts = opts; + udp_options.tls_alpn_protocols = tls_alpn_protocols_; + udp_options.tls_elliptic_curves = tls_elliptic_curves_; + udp_options.tls_cert_verifier = tls_cert_verifier_; + owned_socket_ = socket_factory()->CreateClientUdpSocket( + env(), SocketAddress(Network()->GetBestIP(), 0), + server_address_.address, min_port(), max_port(), udp_options); + socket_ = owned_socket_.get(); } else if (server_address_.proto == PROTO_TCP || server_address_.proto == PROTO_TLS) { RTC_DCHECK(!SharedSocket()); @@ -498,10 +517,12 @@ bool TurnPort::CreateTurnClientSocket() { } }); - // TCP port is ready to send stun requests after the socket is connected, - // while UDP port is ready to do so once the socket is created. + // TCP and UDP with DTLS port is ready to send stun requests after the socket + // is connected, while pure UDP port is ready to do so once the socket is + // created. if (server_address_.proto == PROTO_TCP || - server_address_.proto == PROTO_TLS) { + server_address_.proto == PROTO_TLS || + server_address_.proto == PROTO_DTLS) { socket_->SubscribeConnect( this, [this](AsyncPacketSocket* socket) { OnSocketConnect(socket); }); socket_->SubscribeCloseEvent( @@ -516,7 +537,8 @@ void TurnPort::OnSocketConnect(AsyncPacketSocket* socket) { // This slot should only be invoked if we're using a connection-oriented // protocol. RTC_DCHECK(server_address_.proto == PROTO_TCP || - server_address_.proto == PROTO_TLS); + server_address_.proto == PROTO_TLS || + server_address_.proto == PROTO_DTLS); // Do not use this port if the socket bound to an address not associated with // the desired network interface. This is seen in Chrome, where TCP sockets @@ -570,7 +592,7 @@ void TurnPort::OnSocketConnect(AsyncPacketSocket* socket) { RTC_LOG(LS_INFO) << "TurnPort connected to " << socket->GetRemoteAddress().ToSensitiveString() - << " using tcp."; + << " using tcp or dtls."; SendRequest(new TurnAllocateRequest(this), 0); } @@ -767,7 +789,7 @@ bool TurnPort::HandleIncomingPacket(AsyncPacketSocket* socket, // Check the message type, to see if is a Channel Data message. // The message will either be channel data, a TURN data indication, or // a response to a previous request. - uint16_t msg_type = GetBE16(packet.payload().data()); + uint16_t msg_type = GetBE16(packet.payload()); if (IsTurnChannelData(msg_type)) { HandleChannelData(msg_type, packet); return true; @@ -865,7 +887,8 @@ void TurnPort::ResolveTurnAddress(const SocketAddress& address) { // any). auto& result = resolver_->result(); if (result.GetError() != 0 && (server_address_.proto == PROTO_TCP || - server_address_.proto == PROTO_TLS)) { + server_address_.proto == PROTO_TLS || + server_address_.proto == PROTO_DTLS)) { if (!CreateTurnClientSocket()) { OnAllocateError(STUN_ERROR_SERVER_NOT_REACHABLE, "TURN host lookup received error."); @@ -1016,10 +1039,11 @@ void TurnPort::TryAlternateServer() { // realm and nonce values. SendRequest(new TurnAllocateRequest(this), 0); } else { - // Since it's TCP, we have to delete the connected socket and reconnect + // Since it's not UDP, we have to delete the connected socket and reconnect // with the alternate server. PrepareAddress will send stun binding once // the new socket is connected. - RTC_DCHECK(server_address().proto == PROTO_TCP || + RTC_DCHECK(server_address().proto == PROTO_DTLS || + server_address().proto == PROTO_TCP || server_address().proto == PROTO_TLS); RTC_DCHECK(!SharedSocket()); owned_socket_ = nullptr; @@ -1094,8 +1118,9 @@ void TurnPort::HandleChannelData(uint16_t channel_id, // +-------------------------------+ // Extract header fields from the message. - uint16_t len = GetBE16(packet.payload().data() + 2); - if (len > packet.payload().size() - TURN_CHANNEL_HEADER_SIZE) { + std::span payload = packet.payload(); + uint16_t len = GetBE16(payload.subspan(2, 2)); + if (len > payload.size() - TURN_CHANNEL_HEADER_SIZE) { RTC_LOG(LS_WARNING) << ToString() << ": Received TURN channel data message with " "incorrect length, len: " @@ -1113,7 +1138,7 @@ void TurnPort::HandleChannelData(uint16_t channel_id, return; } ReceivedIpPacket unwrapped_packet = ReceivedIpPacket( - packet.payload().subview(TURN_CHANNEL_HEADER_SIZE, len), entry->address(), + payload.subspan(TURN_CHANNEL_HEADER_SIZE, len), entry->address(), packet.arrival_time(), packet.ecn(), packet.decryption_info()); DispatchPacket(unwrapped_packet, PROTO_UDP); } @@ -1298,6 +1323,10 @@ std::string TurnPort::ReconstructServerUrl() { case PROTO_TLS: scheme = "turns"; break; + case PROTO_DTLS: + scheme = "turns"; + transport = "udp"; + break; case PROTO_UDP: transport = "udp"; break; @@ -1838,7 +1867,7 @@ int TurnEntry::Send(const void* data, buf.WriteUInt16(channel_id_); buf.WriteUInt16(static_cast(size)); buf.Write( - ArrayView(reinterpret_cast(data), size)); + std::span(reinterpret_cast(data), size)); } AsyncSocketPacketOptions modified_options(options); modified_options.info_signaled_after_sent.turn_overhead_bytes = diff --git a/p2p/base/turn_port_unittest.cc b/p2p/base/turn_port_unittest.cc index 9a7e2f1415e..6823fbd1e2c 100644 --- a/p2p/base/turn_port_unittest.cc +++ b/p2p/base/turn_port_unittest.cc @@ -14,16 +14,15 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/packet_socket_factory.h" #include "api/test/mock_async_dns_resolver.h" #include "api/test/rtc_error_matchers.h" @@ -50,8 +49,6 @@ #include "rtc_base/buffer.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/checks.h" -#include "rtc_base/fake_clock.h" -#include "rtc_base/gunit.h" #include "rtc_base/ip_address.h" #include "rtc_base/net_helper.h" #include "rtc_base/net_helpers.h" @@ -62,14 +59,18 @@ #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" +#include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" #if defined(WEBRTC_POSIX) #include // IWYU pragma: keep #endif +namespace webrtc { + namespace { using ::testing::_; using ::testing::DoAll; @@ -79,20 +80,15 @@ using ::testing::Ne; using ::testing::Return; using ::testing::ReturnPointee; using ::testing::SetArgPointee; -using ::webrtc::CreateEnvironment; -using ::webrtc::Environment; -using ::webrtc::IceCandidateType; -using ::webrtc::SocketAddress; const SocketAddress kLocalAddr1("11.11.11.11", 0); const SocketAddress kLocalAddr2("22.22.22.22", 0); const SocketAddress kLocalIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c3", 0); const SocketAddress kLocalIPv6Addr2("2401:fa00:4:2000:be30:5bff:fee5:d4", 0); -const SocketAddress kTurnUdpIntAddr("99.99.99.3", webrtc::TURN_SERVER_PORT); -const SocketAddress kTurnTcpIntAddr("99.99.99.4", webrtc::TURN_SERVER_PORT); +const SocketAddress kTurnUdpIntAddr("99.99.99.3", TURN_SERVER_PORT); +const SocketAddress kTurnTcpIntAddr("99.99.99.4", TURN_SERVER_PORT); const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0); -const SocketAddress kTurnAlternateIntAddr("99.99.99.6", - webrtc::TURN_SERVER_PORT); +const SocketAddress kTurnAlternateIntAddr("99.99.99.6", TURN_SERVER_PORT); // Port for redirecting to a TCP Web server. Should not work. const SocketAddress kTurnDangerousAddr("99.99.99.7", 81); // Port 53 (the DNS port); should work. @@ -102,11 +98,11 @@ const SocketAddress kTurnPort80Addr("99.99.99.7", 80); // Port 443 (the HTTPS port); should work. const SocketAddress kTurnPort443Addr("99.99.99.7", 443); // The default TURN server port. -const SocketAddress kTurnIntAddr("99.99.99.7", webrtc::TURN_SERVER_PORT); +const SocketAddress kTurnIntAddr("99.99.99.7", TURN_SERVER_PORT); const SocketAddress kTurnIPv6IntAddr("2400:4030:2:2c00:be30:abcd:efab:cdef", - webrtc::TURN_SERVER_PORT); + TURN_SERVER_PORT); const SocketAddress kTurnUdpIPv6IntAddr("2400:4030:1:2c00:be30:abcd:efab:cdef", - webrtc::TURN_SERVER_PORT); + TURN_SERVER_PORT); const SocketAddress kTurnInvalidAddr("www.google.invalid.", 3478); const SocketAddress kTurnValidAddr("www.google.valid.", 3478); @@ -119,38 +115,28 @@ constexpr char kTurnUsername[] = "test"; constexpr char kTurnPassword[] = "test"; // This test configures the virtual socket server to simulate delay so that we // can verify operations take no more than the expected number of round trips. -constexpr unsigned int kSimulatedRtt = 50; +constexpr TimeDelta kSimulatedRtt = TimeDelta::Millis(50); // Connection destruction may happen asynchronously, but it should only // take one simulated clock tick. -constexpr unsigned int kConnectionDestructionDelay = 1; +constexpr TimeDelta kConnectionDestructionDelay = TimeDelta::Millis(1); // This used to be 1 second, but that's not always enough for getaddrinfo(). // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5191 -constexpr unsigned int kResolverTimeout = 10000; +constexpr TimeDelta kResolverTimeout = TimeDelta::Millis(10000); constexpr uint64_t kTiebreakerDefault = 44444; -const webrtc::ProtocolAddress kTurnUdpProtoAddr(kTurnUdpIntAddr, - webrtc::PROTO_UDP); -const webrtc::ProtocolAddress kTurnTcpProtoAddr(kTurnTcpIntAddr, - webrtc::PROTO_TCP); -const webrtc::ProtocolAddress kTurnTlsProtoAddr(kTurnTcpIntAddr, - webrtc::PROTO_TLS); -const webrtc::ProtocolAddress kTurnUdpIPv6ProtoAddr(kTurnUdpIPv6IntAddr, - webrtc::PROTO_UDP); -const webrtc::ProtocolAddress kTurnDangerousProtoAddr(kTurnDangerousAddr, - webrtc::PROTO_TCP); -const webrtc::ProtocolAddress kTurnPort53ProtoAddr(kTurnPort53Addr, - webrtc::PROTO_TCP); -const webrtc::ProtocolAddress kTurnPort80ProtoAddr(kTurnPort80Addr, - webrtc::PROTO_TCP); -const webrtc::ProtocolAddress kTurnPort443ProtoAddr(kTurnPort443Addr, - webrtc::PROTO_TCP); -const webrtc::ProtocolAddress kTurnPortInvalidHostnameProtoAddr( - kTurnInvalidAddr, - webrtc::PROTO_UDP); -const webrtc::ProtocolAddress kTurnPortValidHostnameProtoAddr( - kTurnValidAddr, - webrtc::PROTO_UDP); +const ProtocolAddress kTurnUdpProtoAddr(kTurnUdpIntAddr, PROTO_UDP); +const ProtocolAddress kTurnTcpProtoAddr(kTurnTcpIntAddr, PROTO_TCP); +const ProtocolAddress kTurnTlsProtoAddr(kTurnTcpIntAddr, PROTO_TLS); +const ProtocolAddress kTurnUdpIPv6ProtoAddr(kTurnUdpIPv6IntAddr, PROTO_UDP); +const ProtocolAddress kTurnDangerousProtoAddr(kTurnDangerousAddr, PROTO_TCP); +const ProtocolAddress kTurnPort53ProtoAddr(kTurnPort53Addr, PROTO_TCP); +const ProtocolAddress kTurnPort80ProtoAddr(kTurnPort80Addr, PROTO_TCP); +const ProtocolAddress kTurnPort443ProtoAddr(kTurnPort443Addr, PROTO_TCP); +const ProtocolAddress kTurnPortInvalidHostnameProtoAddr(kTurnInvalidAddr, + PROTO_UDP); +const ProtocolAddress kTurnPortValidHostnameProtoAddr(kTurnValidAddr, + PROTO_UDP); #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) int GetFDCount() { @@ -169,14 +155,12 @@ int GetFDCount() { } // unnamed namespace -namespace webrtc { - class TurnPortTestVirtualSocketServer : public VirtualSocketServer { public: TurnPortTestVirtualSocketServer() { // This configures the virtual socket server to always add a simulated // delay of exactly half of kSimulatedRtt. - set_delay_mean(kSimulatedRtt / 2); + set_delay_mean((kSimulatedRtt / 2).ms()); UpdateDelayDistribution(); } @@ -214,14 +198,11 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { public: TurnPortTest() : ss_(new TurnPortTestVirtualSocketServer()), - main_(ss_.get()), - turn_server_(env_, &main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), - socket_factory_(ss_.get()) { - // Some code uses "last received time == 0" to represent "nothing received - // so far", so we need to start the fake clock at a nonzero time... - // TODO(deadbeef): Fix this. - fake_clock_.AdvanceTime(TimeDelta::Seconds(1)); - } + time_controller_(Timestamp::Seconds(1), ss_.get()), + env_(CreateTestEnvironment({.time = &time_controller_})), + main_(time_controller_.GetMainThread()), + turn_server_(env_, main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), + socket_factory_(ss_.get()) {} void OnTurnPortComplete(Port* port) { turn_ready_ = true; } void OnTurnPortError(Port* port) { turn_error_ = true; } @@ -301,7 +282,7 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { RelayServerConfig config; config.credentials = RelayCredentials(username, password); CreateRelayPortArgs args = {.env = env_}; - args.network_thread = &main_; + args.network_thread = main_; args.socket_factory = socket_factory(); args.network = network; args.username = kIceUfrag1; @@ -348,7 +329,7 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { RelayServerConfig config; config.credentials = RelayCredentials(username, password); CreateRelayPortArgs args = {.env = env_}; - args.network_thread = &main_; + args.network_thread = main_; args.socket_factory = socket_factory(); args.network = MakeNetwork(kLocalAddr1); args.username = kIceUfrag1; @@ -387,7 +368,7 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { void CreateUdpPort(const SocketAddress& address) { udp_port_ = UDPPort::Create({.env = env_, - .network_thread = &main_, + .network_thread = main_, .socket_factory = socket_factory(), .network = MakeNetwork(address), .ice_username_fragment = kIceUfrag2, @@ -406,22 +387,21 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { ASSERT_TRUE(turn_port_ != nullptr); turn_port_->PrepareAddress(); ASSERT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis( - TimeToGetTurnCandidate(protocol_type)), - .clock = &fake_clock_}), + {.timeout = TimeToGetTurnCandidate(protocol_type), + .clock = &time_controller_}), IsRtcOk()); CreateUdpPort(); udp_port_->PrepareAddress(); - ASSERT_THAT(WaitUntil([&] { return udp_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return udp_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), + IsRtcOk()); } // Returns the fake clock time to establish a connection over the given // protocol. - int TimeToConnect(ProtocolType protocol_type) { + TimeDelta TimeToConnect(ProtocolType protocol_type) { switch (protocol_type) { case PROTO_TCP: // The virtual socket server will delay by a fixed half a round trip @@ -434,13 +414,13 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { case PROTO_UDP: default: // UDP requires no round trips to set up the connection. - return 0; + return TimeDelta::Zero(); } } // Returns the total fake clock time to establish a connection with a TURN // server over the given protocol and to allocate a TURN candidate. - int TimeToGetTurnCandidate(ProtocolType protocol_type) { + TimeDelta TimeToGetTurnCandidate(ProtocolType protocol_type) { // For a simple allocation, the first Allocate message will return with an // error asking for credentials and will succeed after the second Allocate // message. @@ -453,7 +433,7 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { // 3. Connect to alternate TURN server // 4. Send Allocate and receive a request for credentials // 5. Send Allocate with credentials and receive allocation - int TimeToGetAlternateTurnCandidate(ProtocolType protocol_type) { + TimeDelta TimeToGetAlternateTurnCandidate(ProtocolType protocol_type) { return 3 * kSimulatedRtt + 2 * TimeToConnect(protocol_type); } @@ -477,12 +457,11 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { return true; } - void TestTurnAllocateSucceeds(unsigned int timeout) { + void TestTurnAllocateSucceeds(TimeDelta timeout) { ASSERT_TRUE(turn_port_); turn_port_->PrepareAddress(); EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(timeout), - .clock = &fake_clock_}), + {.timeout = timeout, .clock = &time_controller_}), IsRtcOk()); ASSERT_EQ(1U, turn_port_->Candidates().size()); EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), @@ -495,9 +474,8 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { ASSERT_TRUE(turn_port_); turn_port_->PrepareAddress(); ASSERT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis( - TimeToGetTurnCandidate(protocol_type)), - .clock = &fake_clock_}), + {.timeout = TimeToGetTurnCandidate(protocol_type), + .clock = &time_controller_}), IsRtcOk()); ASSERT_EQ(1U, turn_port_->Candidates().size()); EXPECT_EQ(turn_port_->Candidates()[0].url(), expected_url); @@ -519,11 +497,11 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { const SocketAddress old_addr = turn_port_->server_address().address; turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis( - TimeToGetAlternateTurnCandidate(protocol_type)), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = TimeToGetAlternateTurnCandidate(protocol_type), + .clock = &time_controller_}), + IsRtcOk()); // Retrieve the address again, the turn port's address should be // changed. const SocketAddress new_addr = turn_port_->server_address().address; @@ -546,11 +524,11 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { turn_port_->PrepareAddress(); // Need time to connect to TURN server, send Allocate request and receive // redirect notice. - EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis( - kSimulatedRtt + TimeToConnect(protocol_type)), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_error_; }, IsTrue(), + {.timeout = kSimulatedRtt + TimeToConnect(protocol_type), + .clock = &time_controller_}), + IsRtcOk()); } void TestTurnAlternateServerPingPong(ProtocolType protocol_type) { @@ -567,11 +545,11 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { ProtocolAddress(kTurnIntAddr, protocol_type)); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis( - TimeToGetAlternateTurnCandidate(protocol_type)), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_error_; }, IsTrue(), + {.timeout = TimeToGetAlternateTurnCandidate(protocol_type), + .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(0U, turn_port_->Candidates().size()); SocketAddress address; // Verify that we have exhausted all alternate servers instead of @@ -593,11 +571,11 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { ProtocolAddress(kTurnIntAddr, protocol_type)); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis( - TimeToGetAlternateTurnCandidate(protocol_type)), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_error_; }, IsTrue(), + {.timeout = TimeToGetAlternateTurnCandidate(protocol_type), + .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(0U, turn_port_->Candidates().size()); } @@ -636,15 +614,14 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { turn_port_->PrepareAddress(); EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis( - TimeToGetTurnCandidate(protocol_type)), - .clock = &fake_clock_}), + {.timeout = TimeToGetTurnCandidate(protocol_type), + .clock = &time_controller_}), IsRtcOk()); // Wait for some extra time, and make sure no packets were received on the // loopback port we created (or in the case of TCP, no connection attempt // occurred). - SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); + time_controller_.AdvanceTime(kSimulatedRtt); if (protocol_type == PROTO_UDP) { char buf[1]; EXPECT_EQ(-1, loopback_socket->Recv(&buf, 1, nullptr)); @@ -664,7 +641,9 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { Port::ORIGIN_MESSAGE); ASSERT_TRUE(conn1 != nullptr); conn1->Ping(); - SIMULATED_WAIT(!turn_unknown_address_, kSimulatedRtt * 2, fake_clock_); + EXPECT_TRUE( + WaitUntil([&] { return !turn_unknown_address_; }, + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_})); EXPECT_FALSE(turn_unknown_address_); EXPECT_FALSE(conn1->receiving()); EXPECT_EQ(Connection::STATE_WRITE_INIT, conn1->write_state()); @@ -675,28 +654,27 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { ASSERT_TRUE(conn2 != nullptr); ASSERT_THAT( WaitUntil([&] { return turn_create_permission_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); conn2->Ping(); // Two hops from TURN port to UDP port through TURN server, thus two RTTs. - EXPECT_THAT(WaitUntil([&] { return conn2->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn2->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); EXPECT_TRUE(conn1->receiving()); EXPECT_TRUE(conn2->receiving()); EXPECT_EQ(Connection::STATE_WRITE_INIT, conn1->write_state()); // Send another ping from UDP to TURN. conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn1->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn1->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); EXPECT_TRUE(conn2->receiving()); } @@ -716,16 +694,15 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { ASSERT_TRUE(conn2 != nullptr); ASSERT_THAT( WaitUntil([&] { return turn_create_permission_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); // Make sure turn connection can receive. conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn1->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn1->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); EXPECT_FALSE(turn_unknown_address_); // Destroy the connection on the TURN port. The TurnEntry still exists, so @@ -733,16 +710,16 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { turn_port_->DestroyConnection(conn2); conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return turn_unknown_address_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_unknown_address_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), + IsRtcOk()); // Wait for TurnEntry to expire. Timeout is 5 minutes. // Expect that it still processes an incoming ping and signals the // unknown address. turn_unknown_address_ = false; - fake_clock_.AdvanceTime(TimeDelta::Seconds(5 * 60)); + time_controller_.AdvanceTime(TimeDelta::Seconds(5 * 60)); // TODO(chromium:1395625): When `TurnPort` doesn't find connection objects // for incoming packets, it forwards calls to the parent class, `Port`. This @@ -768,19 +745,19 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { conn1->set_remote_password_for_test(pwd); conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return turn_unknown_address_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_unknown_address_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), + IsRtcOk()); // If the connection is created again, it will start to receive pings. conn2 = turn_port_->CreateConnection(udp_port_->Candidates()[0], Port::ORIGIN_MESSAGE); conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn2->receiving(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn2->receiving(); }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), + IsRtcOk()); } void TestTurnSendData(ProtocolType protocol_type, bool expect_ecn_propagate) { @@ -809,17 +786,17 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { OnConnectionSignalDestroyed(connection); }); conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn1->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn1->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); conn2->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn2->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn2->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); // Send some data. size_t num_packets = 256; @@ -831,7 +808,7 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { options.ect_1 = (i % 2 == 0); conn1->Send(buf, i + 1, options); conn2->Send(buf, i + 1, options); - SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); + time_controller_.AdvanceTime(kSimulatedRtt); } // Check the data. @@ -859,8 +836,7 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { EXPECT_THAT( WaitUntil([&] { return turn_server_.server()->allocations().size(); }, Eq(0U), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); } @@ -889,17 +865,17 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { }); conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn1->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn1->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); conn2->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn2->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn2->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); // Send some data from Udp to TurnPort. unsigned char buf[256] = {0}; @@ -910,10 +886,10 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { turn_port_->Release(); // Wait for the TurnPort to signal closed. - ASSERT_THAT(WaitUntil([&] { return turn_port_closed_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), - IsRtcOk()); + ASSERT_THAT( + WaitUntil([&] { return turn_port_closed_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), + IsRtcOk()); // But the data should have arrived first. ASSERT_EQ(1ul, turn_packets_.size()); @@ -937,14 +913,11 @@ class TurnPortTest : public ::testing::Test, public TurnPort::CallbacksForTest { virtual PacketSocketFactory* socket_factory() { return &socket_factory_; } - ScopedFakeClock fake_clock_; - const Environment env_ = CreateEnvironment(); - // When a "create port" helper method is called with an IP, we create a - // Network with that IP and add it to this list. Using a list instead of a - // vector so that when it grows, pointers aren't invalidated. std::list networks_; std::unique_ptr ss_; - AutoSocketServerThread main_; + GlobalSimulatedTimeController time_controller_; + const Environment env_; + webrtc::Thread* main_; std::unique_ptr socket_; TestTurnServer turn_server_; std::unique_ptr turn_port_; @@ -1010,7 +983,7 @@ TEST_F(TurnPortTest, TestReconstructedServerUrlForHostname) { // the error will be set and contain the url. turn_port_->PrepareAddress(); EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kResolverTimeout)}), + {.timeout = kResolverTimeout}), IsRtcOk()); std::string server_url = "turn:" + kTurnInvalidAddr.ToString() + "?transport=udp"; @@ -1041,7 +1014,7 @@ class TurnLoggingIdValidator : public StunMessageObserver { } } } - void ReceivedChannelData(ArrayView packet) override {} + void ReceivedChannelData(std::span packet) override {} private: const char* expect_val_; @@ -1066,16 +1039,16 @@ TEST_F(TurnPortTest, TestTurnAllocateWithoutLoggingId) { TEST_F(TurnPortTest, TestTurnBadCredentials) { CreateTurnPort(kTurnUsername, "bad", kTurnUdpProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_error_; }, IsTrue(), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(0U, turn_port_->Candidates().size()); - EXPECT_THAT(WaitUntil([&] { return error_event_.error_code; }, - Eq(STUN_ERROR_UNAUTHORIZED), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return error_event_.error_code; }, + Eq(STUN_ERROR_UNAUTHORIZED), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); EXPECT_EQ(error_event_.error_text, "Unauthorized."); } @@ -1084,10 +1057,10 @@ TEST_F(TurnPortTest, TestTurnBadCredentials) { TEST_F(TurnPortTest, TestServerAddressFamilyMismatch) { CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpIPv6ProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_error_; }, IsTrue(), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(0U, turn_port_->Candidates().size()); EXPECT_EQ(0, error_event_.error_code); } @@ -1098,10 +1071,10 @@ TEST_F(TurnPortTest, TestServerAddressFamilyMismatch6) { CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_error_; }, IsTrue(), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(0U, turn_port_->Candidates().size()); EXPECT_EQ(0, error_event_.error_code); } @@ -1155,13 +1128,11 @@ TEST_F(TurnPortTest, // Shouldn't take more than 1 RTT to realize the bound address isn't the one // expected. EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); EXPECT_THAT(WaitUntil([&] { return error_event_.error_code; }, Eq(STUN_ERROR_SERVER_NOT_REACHABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); EXPECT_NE(error_event_.error_text.find('.'), std::string::npos); EXPECT_NE(error_event_.address.find(kLocalAddr2.HostAsSensitiveURIString()), @@ -1194,10 +1165,10 @@ TEST_F(TurnPortTest, TurnTcpAllocationNotDiscardedIfNotBoundToBestIP) { turn_port_->PrepareAddress(); // Candidate should be gathered as normally. - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(1U, turn_port_->Candidates().size()); // Verify that the socket actually used the alternate address, otherwise this @@ -1223,10 +1194,10 @@ TEST_F(TurnPortTest, TCPPortNotDiscardedIfBoundToTemporaryIP) { turn_port_->PrepareAddress(); // Candidate should be gathered as normally. - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(1U, turn_port_->Candidates().size()); } @@ -1238,7 +1209,7 @@ TEST_F(TurnPortTest, TestTurnTcpOnAddressResolveFailure) { ProtocolAddress(kTurnInvalidAddr, PROTO_TCP)); turn_port_->PrepareAddress(); EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kResolverTimeout)}), + {.timeout = kResolverTimeout}), IsRtcOk()); // As VSS doesn't provide DNS resolution, name resolve will fail. TurnPort // will proceed in creating a TCP socket which will fail as there is no @@ -1246,8 +1217,7 @@ TEST_F(TurnPortTest, TestTurnTcpOnAddressResolveFailure) { EXPECT_EQ(SOCKET_ERROR, turn_port_->error()); EXPECT_THAT(WaitUntil([&] { return error_event_.error_code; }, Eq(STUN_ERROR_SERVER_NOT_REACHABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); std::string server_url = "turn:" + kTurnInvalidAddr.ToString() + "?transport=tcp"; @@ -1262,7 +1232,7 @@ TEST_F(TurnPortTest, TestTurnTlsOnAddressResolveFailure) { ProtocolAddress(kTurnInvalidAddr, PROTO_TLS)); turn_port_->PrepareAddress(); EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kResolverTimeout)}), + {.timeout = kResolverTimeout}), IsRtcOk()); EXPECT_EQ(SOCKET_ERROR, turn_port_->error()); } @@ -1274,7 +1244,7 @@ TEST_F(TurnPortTest, TestTurnUdpOnAddressResolveFailure) { ProtocolAddress(kTurnInvalidAddr, PROTO_UDP)); turn_port_->PrepareAddress(); EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kResolverTimeout)}), + {.timeout = kResolverTimeout}), IsRtcOk()); // Error from turn port will not be socket error. EXPECT_NE(SOCKET_ERROR, turn_port_->error()); @@ -1284,10 +1254,10 @@ TEST_F(TurnPortTest, TestTurnUdpOnAddressResolveFailure) { TEST_F(TurnPortTest, TestTurnAllocateBadPassword) { CreateTurnPort(kTurnUsername, "bad", kTurnUdpProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_error_; }, IsTrue(), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(0U, turn_port_->Candidates().size()); } @@ -1297,16 +1267,16 @@ TEST_F(TurnPortTest, TestTurnAllocateNonceResetAfterAllocateMismatch) { // Do a normal allocation first. CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); // Destroy the turnport while keeping the drop probability to 1 to // suppress the release of the allocation at the server. ss_->set_drop_probability(1.0); turn_port_.reset(); - SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); + time_controller_.AdvanceTime(kSimulatedRtt); ss_->set_drop_probability(0.0); // Force the socket server to assign the same port. @@ -1326,10 +1296,10 @@ TEST_F(TurnPortTest, TestTurnAllocateNonceResetAfterAllocateMismatch) { // Four round trips; first we'll get "stale nonce", then // "allocate mismatch", then "stale nonce" again, then finally it will // succeed. - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 4), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 4, .clock = &time_controller_}), + IsRtcOk()); EXPECT_NE(first_nonce, turn_port_->nonce()); } @@ -1339,10 +1309,10 @@ TEST_F(TurnPortTest, TestTurnAllocateMismatch) { // Do a normal allocation first. CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); // Clear connected_ flag on turnport to suppress the release of @@ -1362,10 +1332,10 @@ TEST_F(TurnPortTest, TestTurnAllocateMismatch) { // Four round trips; first we'll get "stale nonce", then // "allocate mismatch", then "stale nonce" again, then finally it will // succeed. - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 4), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 4, .clock = &time_controller_}), + IsRtcOk()); // Verifies that the new port has a different address now. EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); @@ -1385,10 +1355,10 @@ TEST_F(TurnPortTest, TestSharedSocketAllocateMismatch) { // Do a normal allocation first. CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); // Clear connected_ flag on turnport to suppress the release of @@ -1404,10 +1374,10 @@ TEST_F(TurnPortTest, TestSharedSocketAllocateMismatch) { turn_port_->PrepareAddress(); // Extra 2 round trips due to allocate mismatch. - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 4), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 4, .clock = &time_controller_}), + IsRtcOk()); // Verifies that the new port has a different address now. EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); @@ -1420,10 +1390,10 @@ TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) { // Do a normal allocation first. turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); SocketAddress first_addr(turn_port_->socket()->GetLocalAddress()); // Clear connected_ flag on turnport to suppress the release of @@ -1441,10 +1411,10 @@ TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) { EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress()); // Extra 2 round trips due to allocate mismatch. - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 5), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 5, .clock = &time_controller_}), + IsRtcOk()); // Verifies that the new port has a different address now. EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress()); @@ -1464,14 +1434,12 @@ TEST_F(TurnPortTest, TestRefreshRequestGetsErrorResponse) { // credential. turn_port_->request_manager().FlushForTest(TURN_REFRESH_REQUEST); EXPECT_THAT(WaitUntil([&] { return turn_refresh_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); // Flush it again, it will receive a bad response. turn_port_->request_manager().FlushForTest(TURN_REFRESH_REQUEST); EXPECT_THAT(WaitUntil([&] { return !turn_refresh_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); EXPECT_FALSE(turn_port_->connected()); EXPECT_TRUE(CheckAllConnectionsFailedAndPruned()); @@ -1491,17 +1459,17 @@ TEST_F(TurnPortTest, TestStopProcessingPacketsAfterClosed) { ASSERT_TRUE(conn2 != nullptr); // Make sure conn2 is writable. conn2->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn2->write_state(); }, - Eq(Connection::STATE_WRITABLE), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn2->write_state(); }, + Eq(Connection::STATE_WRITABLE), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); turn_port_->CloseForTest(); - SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); + time_controller_.AdvanceTime(kSimulatedRtt); turn_unknown_address_ = false; conn2->Ping(); - SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); + time_controller_.AdvanceTime(kSimulatedRtt); // Since the turn port does not handle packets any more, it should not // SignalUnknownAddress. EXPECT_FALSE(turn_unknown_address_); @@ -1536,9 +1504,9 @@ TEST_F(TurnPortTest, TestSocketCloseWillDestroyConnection) { EXPECT_TRUE(!turn_port_->connections().empty()); turn_port_->socket()->NotifyClosedForTest(1); EXPECT_THAT( - WaitUntil([&] { return turn_port_->connections().empty(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kConnectionDestructionDelay), - .clock = &fake_clock_}), + WaitUntil( + [&] { return turn_port_->connections().empty(); }, IsTrue(), + {.timeout = kConnectionDestructionDelay, .clock = &time_controller_}), IsRtcOk()); } @@ -1683,9 +1651,8 @@ TEST_F(TurnPortTest, TestRefreshCreatePermissionRequest) { Port::ORIGIN_MESSAGE); ASSERT_TRUE(conn != nullptr); EXPECT_THAT( - WaitUntil( - [&] { return turn_create_permission_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), .clock = &fake_clock_}), + WaitUntil([&] { return turn_create_permission_success_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); turn_create_permission_success_ = false; // A create-permission-request should be pending. @@ -1695,16 +1662,14 @@ TEST_F(TurnPortTest, TestRefreshCreatePermissionRequest) { turn_port_->set_credentials(bad_credentials); turn_port_->request_manager().FlushForTest(kAllRequestsForTest); EXPECT_THAT( - WaitUntil( - [&] { return turn_create_permission_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), .clock = &fake_clock_}), + WaitUntil([&] { return turn_create_permission_success_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); // Flush the requests again; the create-permission-request will fail. turn_port_->request_manager().FlushForTest(kAllRequestsForTest); EXPECT_THAT( - WaitUntil( - [&] { return !turn_create_permission_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), .clock = &fake_clock_}), + WaitUntil([&] { return !turn_create_permission_success_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); EXPECT_TRUE(CheckConnectionFailedAndPruned(conn)); } @@ -1720,10 +1685,10 @@ TEST_F(TurnPortTest, TestChannelBindGetErrorResponse) { ASSERT_TRUE(conn2 != nullptr); conn1->Ping(); - EXPECT_THAT(WaitUntil([&] { return conn1->writable(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return conn1->writable(); }, IsTrue(), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); // Tell the TURN server to reject all bind requests from now on. turn_server_.server()->set_reject_bind_requests(true); @@ -1732,9 +1697,8 @@ TEST_F(TurnPortTest, TestChannelBindGetErrorResponse) { conn1->Send(data.data(), data.length(), options); EXPECT_THAT( - WaitUntil( - [&] { return CheckConnectionFailedAndPruned(conn1); }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), .clock = &fake_clock_}), + WaitUntil([&] { return CheckConnectionFailedAndPruned(conn1); }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); // Verify that packets are allowed to be sent after a bind request error. // They'll just use a send indication instead. @@ -1746,8 +1710,7 @@ TEST_F(TurnPortTest, TestChannelBindGetErrorResponse) { }); conn1->Send(data.data(), data.length(), options); EXPECT_THAT(WaitUntil([&] { return !udp_packets_.empty(); }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); conn2->DeregisterReceivedPacketCallback(); } @@ -1785,8 +1748,7 @@ TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv4) { kTurnUdpProtoAddr); turn_port_->PrepareAddress(); ASSERT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); EXPECT_TRUE(turn_port_->Candidates().empty()); } @@ -1811,10 +1773,10 @@ TEST_F(TurnPortTest, TestCandidateAddressFamilyMatch) { CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, kTurnUdpIPv6ProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 2), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 2, .clock = &time_controller_}), + IsRtcOk()); ASSERT_EQ(1U, turn_port_->Candidates().size()); // Create an IPv4 candidate. It will match the TURN candidate. @@ -1840,16 +1802,15 @@ TEST_F(TurnPortTest, TestConnectionFailedAndPrunedOnCreatePermissionFailure) { turn_server_.server()->set_reject_private_addresses(true); CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr); turn_port_->PrepareAddress(); - EXPECT_THAT(WaitUntil([&] { return turn_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt * 3), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return turn_ready_; }, IsTrue(), + {.timeout = kSimulatedRtt * 3, .clock = &time_controller_}), + IsRtcOk()); CreateUdpPort(SocketAddress("10.0.0.10", 0)); udp_port_->PrepareAddress(); EXPECT_THAT(WaitUntil([&] { return udp_ready_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), - .clock = &fake_clock_}), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); // Create a connection. TestConnectionWrapper conn(turn_port_->CreateConnection( @@ -1861,17 +1822,14 @@ TEST_F(TurnPortTest, TestConnectionFailedAndPrunedOnCreatePermissionFailure) { EXPECT_THAT( WaitUntil( [&] { return CheckConnectionFailedAndPruned(conn.connection()); }, - IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), .clock = &fake_clock_}), + IsTrue(), {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); EXPECT_THAT( - WaitUntil( - [&] { return !turn_create_permission_success_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kSimulatedRtt), .clock = &fake_clock_}), + WaitUntil([&] { return !turn_create_permission_success_; }, IsTrue(), + {.timeout = kSimulatedRtt, .clock = &time_controller_}), IsRtcOk()); // Check that the connection is not deleted asynchronously. - SIMULATED_WAIT(conn.connection() == nullptr, kConnectionDestructionDelay, - fake_clock_); + time_controller_.AdvanceTime(kConnectionDestructionDelay); EXPECT_NE(nullptr, conn.connection()); } @@ -1937,14 +1895,14 @@ TEST_F(TurnPortTest, TestResolverShutdown) { ProtocolAddress(kTurnInvalidAddr, PROTO_UDP)); turn_port_->PrepareAddress(); ASSERT_THAT(WaitUntil([&] { return turn_error_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kResolverTimeout)}), + {.timeout = kResolverTimeout}), IsRtcOk()); EXPECT_TRUE(turn_port_->Candidates().empty()); turn_port_.reset(); Thread::Current()->PostTask([this] { test_finish_ = true; }); // Waiting for above message to be processed. ASSERT_THAT(WaitUntil([&] { return test_finish_; }, IsTrue(), - {.clock = &fake_clock_}), + {.clock = &time_controller_}), IsRtcOk()); EXPECT_EQ(last_fd_count, GetFDCount()); } @@ -1974,7 +1932,7 @@ class MessageObserver : public StunMessageObserver { } } - void ReceivedChannelData(ArrayView payload) override { + void ReceivedChannelData(std::span payload) override { if (channel_data_counter_ != nullptr) { (*channel_data_counter_)++; } @@ -2137,11 +2095,11 @@ TEST_F(TurnPortTest, TestTurnDangerousAlternateServer) { turn_port_->PrepareAddress(); // This should result in an error event. - EXPECT_THAT(WaitUntil([&] { return error_event_.error_code; }, Ne(0), - {.timeout = TimeDelta::Millis( - TimeToGetAlternateTurnCandidate(protocol_type)), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return error_event_.error_code; }, Ne(0), + {.timeout = TimeToGetAlternateTurnCandidate(protocol_type), + .clock = &time_controller_}), + IsRtcOk()); // but should NOT result in the port turning ready, and no candidates // should be gathered. EXPECT_FALSE(turn_ready_); @@ -2167,18 +2125,17 @@ class TurnPortWithMockDnsResolverTest : public TurnPortTest { // Test an allocation from a TURN server specified by a hostname. TEST_F(TurnPortWithMockDnsResolverTest, TestHostnameResolved) { CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPortValidHostnameProtoAddr); - SetDnsResolverExpectations( - [](MockAsyncDnsResolver* resolver, - MockAsyncDnsResolverResult* resolver_result) { - EXPECT_CALL(*resolver, Start(kTurnValidAddr, /*family=*/AF_INET, _)) - .WillOnce([](const SocketAddress& addr, int family, - absl::AnyInvocable callback) { callback(); }); - EXPECT_CALL(*resolver, result) - .WillRepeatedly(ReturnPointee(resolver_result)); - EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0)); - EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) - .WillOnce(DoAll(SetArgPointee<1>(kTurnUdpIntAddr), Return(true))); - }); + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kTurnValidAddr, /*family=*/AF_INET, _)) + .WillOnce([](const SocketAddress& addr, int family, + absl::AnyInvocable callback) { callback(); }); + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0)); + EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _)) + .WillOnce(DoAll(SetArgPointee<1>(kTurnUdpIntAddr), Return(true))); + }); TestTurnAllocateSucceeds(kSimulatedRtt * 2); } @@ -2188,19 +2145,17 @@ TEST_F(TurnPortWithMockDnsResolverTest, TestHostnameResolvedIPv6Network) { turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP); CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword, kTurnPortValidHostnameProtoAddr); - SetDnsResolverExpectations( - [](MockAsyncDnsResolver* resolver, - MockAsyncDnsResolverResult* resolver_result) { - EXPECT_CALL(*resolver, Start(kTurnValidAddr, /*family=*/AF_INET6, _)) - .WillOnce([](const SocketAddress& addr, int family, - absl::AnyInvocable callback) { callback(); }); - EXPECT_CALL(*resolver, result) - .WillRepeatedly(ReturnPointee(resolver_result)); - EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0)); - EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _)) - .WillOnce( - DoAll(SetArgPointee<1>(kTurnUdpIPv6IntAddr), Return(true))); - }); + SetDnsResolverExpectations([](MockAsyncDnsResolver* resolver, + MockAsyncDnsResolverResult* resolver_result) { + EXPECT_CALL(*resolver, Start(kTurnValidAddr, /*family=*/AF_INET6, _)) + .WillOnce([](const SocketAddress& addr, int family, + absl::AnyInvocable callback) { callback(); }); + EXPECT_CALL(*resolver, result) + .WillRepeatedly(ReturnPointee(resolver_result)); + EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0)); + EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _)) + .WillOnce(DoAll(SetArgPointee<1>(kTurnUdpIPv6IntAddr), Return(true))); + }); TestTurnAllocateSucceeds(kSimulatedRtt * 2); } diff --git a/p2p/base/wrapping_active_ice_controller.cc b/p2p/base/wrapping_active_ice_controller.cc index 05d9a1f7065..a09d057afa9 100644 --- a/p2p/base/wrapping_active_ice_controller.cc +++ b/p2p/base/wrapping_active_ice_controller.cc @@ -30,11 +30,6 @@ #include "rtc_base/logging.h" #include "rtc_base/thread.h" -namespace { -using ::webrtc::SafeTask; -using ::webrtc::TimeDelta; -} // unnamed namespace - namespace webrtc { WrappingActiveIceController::WrappingActiveIceController( diff --git a/p2p/base/wrapping_active_ice_controller_unittest.cc b/p2p/base/wrapping_active_ice_controller_unittest.cc index 459af04f4f3..41c605631bd 100644 --- a/p2p/base/wrapping_active_ice_controller_unittest.cc +++ b/p2p/base/wrapping_active_ice_controller_unittest.cc @@ -26,10 +26,10 @@ #include "p2p/test/mock_ice_controller.h" #include "rtc_base/event.h" #include "rtc_base/fake_clock.h" -#include "rtc_base/thread.h" #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { namespace { @@ -55,7 +55,7 @@ const std::vector kEmptyConnsList = constexpr TimeDelta kTick = TimeDelta::Millis(1); TEST(WrappingActiveIceControllerTest, CreateLegacyIceControllerFromFactory) { - AutoThread main; + test::RunLoop main; MockIceAgent agent; IceControllerFactoryArgs args = {.env = CreateTestEnvironment()}; MockIceControllerFactory legacy_controller_factory; @@ -65,7 +65,7 @@ TEST(WrappingActiveIceControllerTest, CreateLegacyIceControllerFromFactory) { } TEST(WrappingActiveIceControllerTest, PassthroughIceControllerInterface) { - AutoThread main; + test::RunLoop main; MockIceAgent agent; auto will_move = std::make_unique(); MockIceController* wrapped = will_move.get(); @@ -100,7 +100,7 @@ TEST(WrappingActiveIceControllerTest, PassthroughIceControllerInterface) { } TEST(WrappingActiveIceControllerTest, HandlesImmediateSwitchRequest) { - AutoThread main; + test::RunLoop main; ScopedFakeClock clock; NiceMock agent; auto will_move = std::make_unique(); @@ -144,7 +144,7 @@ TEST(WrappingActiveIceControllerTest, HandlesImmediateSwitchRequest) { } TEST(WrappingActiveIceControllerTest, HandlesImmediateSortAndSwitchRequest) { - AutoThread main; + test::RunLoop main; ScopedFakeClock clock; NiceMock agent; auto will_move = std::make_unique(); @@ -195,7 +195,7 @@ TEST(WrappingActiveIceControllerTest, HandlesImmediateSortAndSwitchRequest) { } TEST(WrappingActiveIceControllerTest, HandlesSortAndSwitchRequest) { - AutoThread main; + test::RunLoop main; ScopedFakeClock clock; // Block the main task queue until ready. @@ -239,7 +239,7 @@ TEST(WrappingActiveIceControllerTest, HandlesSortAndSwitchRequest) { } TEST(WrappingActiveIceControllerTest, StartPingingAfterSortAndSwitch) { - AutoThread main; + test::RunLoop main; ScopedFakeClock clock; // Block the main task queue until ready. diff --git a/p2p/client/basic_port_allocator.cc b/p2p/client/basic_port_allocator.cc index 234314aabfd..22dc035103c 100644 --- a/p2p/client/basic_port_allocator.cc +++ b/p2p/client/basic_port_allocator.cc @@ -1739,13 +1739,23 @@ ServerAddresses PortConfiguration::StunServers() { // Every UDP TURN server should also be used as a STUN server if // use_turn_server_as_stun_server is not disabled or the stun servers are // empty. - ServerAddresses turn_servers = GetRelayServerAddresses(PROTO_UDP); + + InsertStunServersForProtocol(PROTO_UDP); + + // Every DTLS TURN server should also be used as a STUN server if + // use_turn_server_as_stun_server is not disabled or the stun servers are + // empty. + InsertStunServersForProtocol(PROTO_DTLS); + return stun_servers; +} + +void PortConfiguration::InsertStunServersForProtocol(ProtocolType type) { + ServerAddresses turn_servers = GetRelayServerAddresses(type); for (const SocketAddress& turn_server : turn_servers) { if (stun_servers.find(turn_server) == stun_servers.end()) { stun_servers.insert(turn_server); } } - return stun_servers; } void PortConfiguration::AddRelay(const RelayServerConfig& config) { diff --git a/p2p/client/basic_port_allocator.h b/p2p/client/basic_port_allocator.h index fc7506e2d9d..d274eb586cc 100644 --- a/p2p/client/basic_port_allocator.h +++ b/p2p/client/basic_port_allocator.h @@ -29,6 +29,7 @@ #include "api/task_queue/pending_task_safety_flag.h" #include "api/transport/enums.h" #include "api/turn_customizer.h" +#include "api/units/time_delta.h" #include "p2p/base/port.h" #include "p2p/base/port_allocator.h" #include "p2p/base/port_interface.h" @@ -40,6 +41,7 @@ #include "rtc_base/checks.h" #include "rtc_base/ip_address.h" #include "rtc_base/memory/always_valid_pointer.h" +#include "rtc_base/net_helper.h" #include "rtc_base/network.h" #include "rtc_base/network/received_packet.h" #include "rtc_base/socket_address.h" @@ -341,6 +343,10 @@ struct RTC_EXPORT PortConfiguration { // Helper method returns the server addresses for the matching RelayType and // Protocol type. ServerAddresses GetRelayServerAddresses(ProtocolType type) const; + + // Insert into stun_servers extra TURN servers that could be used as STUN + // servers + void InsertStunServersForProtocol(ProtocolType type); }; // Performs the allocation of ports, in a sequenced (timed) manner, for a given diff --git a/p2p/client/basic_port_allocator_unittest.cc b/p2p/client/basic_port_allocator_unittest.cc index fba34695a08..6f508eaa926 100644 --- a/p2p/client/basic_port_allocator_unittest.cc +++ b/p2p/client/basic_port_allocator_unittest.cc @@ -16,17 +16,20 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" +#include "api/async_dns_resolver.h" #include "api/candidate.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/field_trials.h" #include "api/test/rtc_error_matchers.h" #include "api/transport/enums.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "p2p/base/basic_packet_socket_factory.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" @@ -40,138 +43,194 @@ #include "p2p/test/stun_server.h" #include "p2p/test/test_stun_server.h" #include "p2p/test/test_turn_server.h" -#include "rtc_base/fake_clock.h" +#include "rtc_base/checks.h" #include "rtc_base/fake_mdns_responder.h" #include "rtc_base/fake_network.h" #include "rtc_base/firewall_socket_server.h" -#include "rtc_base/gunit.h" #include "rtc_base/ip_address.h" #include "rtc_base/logging.h" #include "rtc_base/net_helper.h" +#include "rtc_base/net_helpers.h" #include "rtc_base/net_test_helpers.h" #include "rtc_base/network.h" #include "rtc_base/network_constants.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" +#include "rtc_base/socket_factory.h" #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" +#include "test/create_test_environment.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" -using ::testing::Contains; -using ::testing::Eq; -using ::testing::IsTrue; -using ::testing::Not; -using ::webrtc::CreateEnvironment; -using ::webrtc::Environment; -using ::webrtc::IceCandidateType; -using ::webrtc::IPAddress; -using ::webrtc::SocketAddress; - #define MAYBE_SKIP_IPV4 \ if (!::webrtc::HasIPv4Enabled()) { \ RTC_LOG(LS_INFO) << "No IPv4... skipping"; \ return; \ } -static const SocketAddress kAnyAddr("0.0.0.0", 0); -static const SocketAddress kClientAddr("11.11.11.11", 0); -static const SocketAddress kClientAddr2("22.22.22.22", 0); -static const SocketAddress kLoopbackAddr("127.0.0.1", 0); -static const SocketAddress kPrivateAddr("192.168.1.11", 0); -static const SocketAddress kPrivateAddr2("192.168.1.12", 0); -static const SocketAddress kClientIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c3", - 0); -static const SocketAddress kClientIPv6Addr2( - "2401:fa00:4:2000:be30:5bff:fee5:c3", - 0); -static const SocketAddress kClientIPv6Addr3( - "2401:fa00:4:3000:be30:5bff:fee5:c3", - 0); -static const SocketAddress kClientIPv6Addr4( - "2401:fa00:4:4000:be30:5bff:fee5:c3", - 0); -static const SocketAddress kClientIPv6Addr5( - "2401:fa00:4:5000:be30:5bff:fee5:c3", - 0); -static const SocketAddress kNatUdpAddr("77.77.77.77", - webrtc::NAT_SERVER_UDP_PORT); -static const SocketAddress kNatTcpAddr("77.77.77.77", - webrtc::NAT_SERVER_TCP_PORT); -static const SocketAddress kRemoteClientAddr("22.22.22.22", 0); -static const SocketAddress kStunAddr("99.99.99.1", webrtc::STUN_SERVER_PORT); -static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478); -static const SocketAddress kTurnUdpIntIPv6Addr( - "2402:fb00:4:1000:be30:5bff:fee5:c3", - 3479); -static const SocketAddress kTurnTcpIntAddr("99.99.99.5", 3478); -static const SocketAddress kTurnTcpIntIPv6Addr( - "2402:fb00:4:2000:be30:5bff:fee5:c3", - 3479); -static const SocketAddress kTurnUdpExtAddr("99.99.99.6", 0); +namespace webrtc { +namespace { + +using ::testing::Contains; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::Not; + +const SocketAddress kAnyAddr("0.0.0.0", 0); +const SocketAddress kClientAddr("11.11.11.11", 0); +const SocketAddress kClientAddr2("22.22.22.22", 0); +const SocketAddress kLoopbackAddr("127.0.0.1", 0); +const SocketAddress kPrivateAddr("192.168.1.11", 0); +const SocketAddress kPrivateAddr2("192.168.1.12", 0); +const SocketAddress kClientIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c3", 0); +const SocketAddress kClientIPv6Addr2("2401:fa00:4:2000:be30:5bff:fee5:c3", 0); +const SocketAddress kClientIPv6Addr3("2401:fa00:4:3000:be30:5bff:fee5:c3", 0); +const SocketAddress kClientIPv6Addr4("2401:fa00:4:4000:be30:5bff:fee5:c3", 0); +const SocketAddress kClientIPv6Addr5("2401:fa00:4:5000:be30:5bff:fee5:c3", 0); +const SocketAddress kNatUdpAddr("77.77.77.77", webrtc::NAT_SERVER_UDP_PORT); +const SocketAddress kNatTcpAddr("77.77.77.77", webrtc::NAT_SERVER_TCP_PORT); +const SocketAddress kRemoteClientAddr("22.22.22.22", 0); +const SocketAddress kStunAddr("99.99.99.1", webrtc::STUN_SERVER_PORT); +const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478); +const SocketAddress kTurnUdpIntIPv6Addr("2402:fb00:4:1000:be30:5bff:fee5:c3", + 3479); +const SocketAddress kTurnTcpIntAddr("99.99.99.5", 3478); +const SocketAddress kTurnTcpIntIPv6Addr("2402:fb00:4:2000:be30:5bff:fee5:c3", + 3479); +const SocketAddress kTurnUdpExtAddr("99.99.99.6", 0); // Minimum and maximum port for port range tests. -static const int kMinPort = 10000; -static const int kMaxPort = 10099; +const int kMinPort = 10000; +const int kMaxPort = 10099; // Based on ICE_UFRAG_LENGTH -static const char kIceUfrag0[] = "UF00"; +const char kIceUfrag0[] = "UF00"; // Based on ICE_PWD_LENGTH -static const char kIcePwd0[] = "TESTICEPWD00000000000000"; +const char kIcePwd0[] = "TESTICEPWD00000000000000"; -static const char kContentName[] = "test content"; +const char kContentName[] = "test content"; -static const int kDefaultAllocationTimeout = 3000; -static const char kTurnUsername[] = "test"; -static const char kTurnPassword[] = "test"; +constexpr TimeDelta kDefaultAllocationTimeout = TimeDelta::Millis(3000); +const char kTurnUsername[] = "test"; +const char kTurnPassword[] = "test"; // STUN timeout (with all retries) is webrtc::STUN_TOTAL_TIMEOUT. // Add some margin of error for slow bots. -static const int kStunTimeoutMs = webrtc::STUN_TOTAL_TIMEOUT; - -namespace { +constexpr TimeDelta kStunTimeout = TimeDelta::Millis(STUN_TOTAL_TIMEOUT); void CheckStunKeepaliveIntervalOfAllReadyPorts( - const webrtc::PortAllocatorSession* allocator_session, + const PortAllocatorSession* allocator_session, int expected) { auto ready_ports = allocator_session->ReadyPorts(); for (const auto* port : ready_ports) { if (port->Type() == IceCandidateType::kSrflx || (port->Type() == IceCandidateType::kHost && - port->GetProtocol() == webrtc::PROTO_UDP)) { - EXPECT_EQ( - static_cast(port)->stun_keepalive_delay(), - webrtc::TimeDelta::Millis(expected)); + port->GetProtocol() == PROTO_UDP)) { + EXPECT_EQ(static_cast(port)->stun_keepalive_delay(), + TimeDelta::Millis(expected)); } } } -} // namespace +class FakeAsyncDnsResolverResult : public AsyncDnsResolverResult { + public: + FakeAsyncDnsResolverResult(const SocketAddress& addr, int error) + : addr_(addr), error_(error) {} -namespace webrtc { + bool GetResolvedAddress(int family, SocketAddress* addr) const override { + if (error_ != 0) + return false; + if (family != AF_UNSPEC && addr_.family() != family) + return false; + *addr = addr_; + return true; + } + + int GetError() const override { return error_; } + + private: + SocketAddress addr_; + int error_; +}; + +class FakeAsyncDnsResolver : public AsyncDnsResolverInterface { + public: + void Start(const SocketAddress& addr, + absl::AnyInvocable callback) override { + StartInternal(addr, AF_UNSPEC, std::move(callback)); + } + + void Start(const SocketAddress& addr, + int family, + absl::AnyInvocable callback) override { + StartInternal(addr, family, std::move(callback)); + } + + const AsyncDnsResolverResult& result() const override { + RTC_DCHECK(result_); + return *result_; + } + + private: + void StartInternal(const SocketAddress& addr, + int family, + absl::AnyInvocable callback) { + SocketAddress resolved = addr; + int error = 0; + if (addr.hostname() == "localhost") { + if (family == AF_INET6) { + resolved.SetIP("::1"); + } else { + resolved.SetIP("127.0.0.1"); + } + } else { + error = -1; + } + result_ = std::make_unique(resolved, error); + Thread::Current()->PostTask(std::move(callback)); + } + + std::unique_ptr result_; +}; + +class FakePacketSocketFactory : public BasicPacketSocketFactory { + public: + explicit FakePacketSocketFactory(SocketFactory* socket_factory) + : BasicPacketSocketFactory(socket_factory) {} -class BasicPortAllocatorTestBase : public ::testing::Test { + std::unique_ptr CreateAsyncDnsResolver() override { + return std::make_unique(); + } +}; + +class BasicPortAllocatorTest : public ::testing::Test { public: - BasicPortAllocatorTestBase() + BasicPortAllocatorTest() : vss_(new VirtualSocketServer()), fss_(new FirewallSocketServer(vss_.get())), + time_controller_(Timestamp::Zero(), fss_.get()), + env_(CreateTestEnvironment({.time = &time_controller_})), socket_factory_(fss_.get()), - thread_(fss_.get()), + thread_(time_controller_.GetMainThread()), // Note that the NAT is not used by default. ResetWithStunServerAndNat // must be called. nat_factory_(vss_.get(), kNatUdpAddr, kNatTcpAddr), nat_socket_factory_(new BasicPacketSocketFactory(&nat_factory_)), - stun_server_(TestStunServer::Create(env_, kStunAddr, *fss_, thread_)), + stun_server_(TestStunServer::Create(env_, kStunAddr, *fss_, *thread_)), turn_server_(env_, - Thread::Current(), + thread_, fss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr), - network_manager_(&thread_), - candidate_allocation_done_(false) { + network_manager_(thread_), + candidate_allocation_done_(false), + waiter_({.timeout = kDefaultAllocationTimeout, + .clock = &time_controller_}) { allocator_.emplace(env_, &network_manager_, &socket_factory_); allocator_->SetConfiguration({kStunAddr}, {}, 0, NO_PRUNE, nullptr); @@ -384,12 +443,11 @@ class BasicPortAllocatorTestBase : public ::testing::Test { static bool HasNetwork(const std::vector& networks, const Network& to_be_found) { - auto it = - absl::c_find_if(networks, [&to_be_found](const Network* network) { - return network->description() == to_be_found.description() && - network->name() == to_be_found.name() && - network->prefix() == to_be_found.prefix(); - }); + auto it = absl::c_find_if(networks, [&to_be_found](const Network* network) { + return network->description() == to_be_found.description() && + network->name() == to_be_found.name() && + network->prefix() == to_be_found.prefix(); + }); return it != networks.end(); } @@ -486,8 +544,8 @@ class BasicPortAllocatorTestBase : public ::testing::Test { void ResetWithStunServer(const SocketAddress& stun_server, bool with_nat) { if (with_nat) { nat_server_ = std::make_unique( - env_, NAT_OPEN_CONE, thread_, vss_.get(), kNatUdpAddr, kNatTcpAddr, - thread_, vss_.get(), SocketAddress(kNatUdpAddr.ipaddr(), 0)); + env_, NAT_OPEN_CONE, *thread_, vss_.get(), kNatUdpAddr, kNatTcpAddr, + *thread_, vss_.get(), SocketAddress(kNatUdpAddr.ipaddr(), 0)); } else { nat_socket_factory_ = std::make_unique(fss_.get()); @@ -504,11 +562,12 @@ class BasicPortAllocatorTestBase : public ::testing::Test { allocator_->set_step_delay(kMinimumStepDelay); } - Environment env_ = CreateEnvironment(); std::unique_ptr vss_; std::unique_ptr fss_; + GlobalSimulatedTimeController time_controller_; + const Environment env_; BasicPacketSocketFactory socket_factory_; - AutoSocketServerThread thread_; + webrtc::Thread* const thread_; std::unique_ptr nat_server_; NATSocketFactory nat_factory_; std::unique_ptr nat_socket_factory_; @@ -520,18 +579,8 @@ class BasicPortAllocatorTestBase : public ::testing::Test { std::vector ports_; std::vector candidates_; bool candidate_allocation_done_; -}; + Waiter waiter_; -class BasicPortAllocatorTestWithRealClock : public BasicPortAllocatorTestBase { -}; - -class FakeClockBase { - public: - ScopedFakeClock fake_clock; -}; - -class BasicPortAllocatorTest : public FakeClockBase, - public BasicPortAllocatorTestBase { public: // This function starts the port/address gathering and check the existence of // candidates as specified. When `expect_stun_candidate` is true, @@ -554,11 +603,7 @@ class BasicPortAllocatorTest : public FakeClockBase, PORTALLOCATOR_ENABLE_SHARED_SOCKET); allocator().set_allow_tcp_listen(false); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); uint32_t total_candidates = 0; if (!host_candidate_addr.IsNil()) { @@ -615,11 +660,7 @@ class BasicPortAllocatorTest : public FakeClockBase, ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Three ports (one IPv4 STUN, one IPv6 STUN and one TURN) will be ready. EXPECT_EQ(3U, session_->ReadyPorts().size()); EXPECT_EQ(3U, ports_.size()); @@ -660,11 +701,7 @@ class BasicPortAllocatorTest : public FakeClockBase, ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Only 2 ports (one STUN and one TURN) are actually being used. EXPECT_EQ(2U, session_->ReadyPorts().size()); // We have verified that each port, when it is added to `ports_`, it is @@ -720,11 +757,7 @@ class BasicPortAllocatorTest : public FakeClockBase, PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_ENABLE_IPV6_ON_WIFI); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // 10 ports (4 STUN and 1 TURN ports on each interface) will be ready to // use. EXPECT_EQ(10U, session_->ReadyPorts().size()); @@ -803,11 +836,7 @@ TEST_F(BasicPortAllocatorTest, TestIgnoreOnlyLoopbackNetworkByDefault) { session_->set_flags(PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_RELAY | PORTALLOCATOR_DISABLE_TCP); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(4U, candidates_.size()); for (const Candidate& candidate : candidates_) { EXPECT_LT(candidate.address().ip(), 0x12345604U); @@ -827,11 +856,7 @@ TEST_F(BasicPortAllocatorTest, TestIgnoreNetworksAccordingToIgnoreMask) { session_->set_flags(PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_RELAY | PORTALLOCATOR_DISABLE_TCP); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(1U, candidates_.size()); EXPECT_EQ(0x12345602U, candidates_[0].address().ip()); } @@ -850,11 +875,7 @@ TEST_F(BasicPortAllocatorTest, PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Should only get one Wi-Fi candidate. EXPECT_EQ(1U, candidates_.size()); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kHost, "udp", wifi)); @@ -878,11 +899,7 @@ TEST_F(BasicPortAllocatorTest, PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Should only get two candidates, none of which is cell. EXPECT_EQ(2U, candidates_.size()); EXPECT_TRUE( @@ -910,11 +927,7 @@ TEST_F(BasicPortAllocatorTest, PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Should only get one Wi-Fi candidate. EXPECT_EQ(1U, candidates_.size()); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kHost, "udp", wifi)); @@ -933,11 +946,7 @@ TEST_F(BasicPortAllocatorTest, PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Make sure we got the cell candidate. EXPECT_EQ(1U, candidates_.size()); EXPECT_TRUE( @@ -959,11 +968,7 @@ TEST_F(BasicPortAllocatorTest, PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Make sure we got both wifi and cell candidates. EXPECT_EQ(2U, candidates_.size()); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kHost, "udp", @@ -990,11 +995,7 @@ TEST_F(BasicPortAllocatorTest, PORTALLOCATOR_DISABLE_TCP | PORTALLOCATOR_DISABLE_COSTLY_NETWORKS); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Make sure we got only wifi candidates. EXPECT_EQ(2U, candidates_.size()); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kHost, "udp", wifi)); @@ -1015,11 +1016,7 @@ TEST_F(BasicPortAllocatorTest, PORTALLOCATOR_DISABLE_TCP); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // The VPN tap0 network should be filtered out as a costly network, and we // should have a UDP port and a STUN port from the Ethernet eth0. ASSERT_EQ(2U, ports_.size()); @@ -1043,11 +1040,7 @@ TEST_F(BasicPortAllocatorTest, MaxIpv6NetworksLimitEnforced) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(2U, candidates_.size()); // Ensure the expected two interfaces (eth0 and eth1) were used. EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kHost, "udp", @@ -1073,11 +1066,7 @@ TEST_F(BasicPortAllocatorTest, MaxIpv6NetworksLimitDoesNotImpactIpv4Networks) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); // Ensure that only one IPv6 interface was used, but both IPv4 interfaces // were used. @@ -1097,11 +1086,7 @@ TEST_F(BasicPortAllocatorTest, TestLoopbackNetworkInterface) { session_->set_flags(PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_RELAY | PORTALLOCATOR_DISABLE_TCP); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(1U, candidates_.size()); } @@ -1110,11 +1095,7 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsWithMinimumStepDelay) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_EQ(3U, ports_.size()); EXPECT_TRUE( @@ -1133,11 +1114,7 @@ TEST_F(BasicPortAllocatorTest, TestSameNetworkDownAndUpWhenSessionNotStopped) { AddInterface(kClientAddr, if_name); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_EQ(3U, ports_.size()); candidate_allocation_done_ = false; @@ -1150,7 +1127,7 @@ TEST_F(BasicPortAllocatorTest, TestSameNetworkDownAndUpWhenSessionNotStopped) { fss_->set_tcp_sockets_enabled(false); fss_->set_udp_sockets_enabled(false); RemoveInterface(kClientAddr); - SIMULATED_WAIT(false, 1000, fake_clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1000)); EXPECT_EQ(0U, candidates_.size()); ports_.clear(); candidate_allocation_done_ = false; @@ -1160,11 +1137,7 @@ TEST_F(BasicPortAllocatorTest, TestSameNetworkDownAndUpWhenSessionNotStopped) { fss_->set_tcp_sockets_enabled(true); fss_->set_udp_sockets_enabled(true); AddInterface(kClientAddr, if_name); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_EQ(3U, ports_.size()); } @@ -1177,11 +1150,7 @@ TEST_F(BasicPortAllocatorTest, TestSameNetworkDownAndUpWhenSessionStopped) { AddInterface(kClientAddr, if_name); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_EQ(3U, ports_.size()); session_->StopGettingPorts(); @@ -1191,14 +1160,14 @@ TEST_F(BasicPortAllocatorTest, TestSameNetworkDownAndUpWhenSessionStopped) { RemoveInterface(kClientAddr); // Wait one (simulated) second and then verify no new candidates have // appeared. - SIMULATED_WAIT(false, 1000, fake_clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1000)); EXPECT_EQ(0U, candidates_.size()); EXPECT_EQ(0U, ports_.size()); // When the same interfaces are added again, new candidates/ports should not // be generated because the session has stopped. AddInterface(kClientAddr, if_name); - SIMULATED_WAIT(false, 1000, fake_clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1000)); EXPECT_EQ(0U, candidates_.size()); EXPECT_EQ(0U, ports_.size()); } @@ -1218,11 +1187,7 @@ TEST_F(BasicPortAllocatorTest, CandidatesRegatheredAfterBindingFails) { fss_->set_udp_sockets_enabled(false); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Make sure we actually prevented candidates from being gathered (other than // a single TCP active candidate, since that doesn't require creating a // socket). @@ -1236,11 +1201,7 @@ TEST_F(BasicPortAllocatorTest, CandidatesRegatheredAfterBindingFails) { fss_->set_tcp_sockets_enabled(true); fss_->set_udp_sockets_enabled(true); AddInterface(kClientAddr, if_name); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Should get UDP and TCP candidate. ASSERT_EQ(2U, candidates_.size()); EXPECT_TRUE( @@ -1258,17 +1219,14 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsWithOneSecondStepDelay) { allocator_->set_step_delay(kDefaultStepDelay); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT(WaitUntil([&] { return candidates_.size(); }, Eq(2U), - {.clock = &fake_clock}), + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2U)), IsRtcOk()); EXPECT_EQ(2U, ports_.size()); - ASSERT_THAT(WaitUntil([&] { return candidates_.size(); }, Eq(3U), - {.clock = &fake_clock}), + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(3U)), IsRtcOk()); EXPECT_EQ(3U, ports_.size()); - ASSERT_THAT(WaitUntil([&] { return candidates_.size(); }, Eq(3U), - {.clock = &fake_clock}), + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(3U)), IsRtcOk()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "tcp", kClientAddr)); @@ -1282,11 +1240,7 @@ TEST_F(BasicPortAllocatorTest, TestSetupVideoRtpPortsWithNormalSendBuffers) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP, "video")); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); // If we Stop gathering now, we shouldn't get a second "done" callback. session_->StopGettingPorts(); @@ -1301,18 +1255,11 @@ TEST_F(BasicPortAllocatorTest, TestStopGetAllPorts) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(2U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2U)), + IsRtcOk()); EXPECT_EQ(2U, ports_.size()); session_->StopGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); } // Test that we restrict client ports appropriately when a port range is set. @@ -1328,11 +1275,7 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsPortRange) { EXPECT_TRUE(SetPortRange(kMinPort, kMaxPort)); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_EQ(3U, ports_.size()); @@ -1358,11 +1301,7 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoAdapters) { AddTurnServers(kTurnUdpIntIPv6Addr, kTurnTcpIntIPv6Addr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(4U, ports_.size()); EXPECT_EQ(1, CountPorts(ports_, IceCandidateType::kSrflx, PROTO_UDP, kAnyAddr)); @@ -1504,11 +1443,7 @@ TEST_F(BasicPortAllocatorTest, TestDisableUdpTurn) { PORTALLOCATOR_ENABLE_SHARED_SOCKET); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Expect to see 2 ports and 2 candidates - TURN/TCP and TCP ports, TCP and // TURN/TCP candidates. @@ -1531,9 +1466,7 @@ TEST_F(BasicPortAllocatorTest, TestDisableAllPorts) { session_->set_flags(PORTALLOCATOR_DISABLE_UDP | PORTALLOCATOR_DISABLE_STUN | PORTALLOCATOR_DISABLE_RELAY | PORTALLOCATOR_DISABLE_TCP); session_->StartGettingPorts(); - EXPECT_THAT(WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(0U, candidates_.size()); } @@ -1543,11 +1476,7 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoUdpSockets) { fss_->set_udp_sockets_enabled(false); ASSERT_TRUE(CreateSession(1)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(1U, candidates_.size()); EXPECT_EQ(1U, ports_.size()); EXPECT_TRUE( @@ -1563,11 +1492,7 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoUdpSocketsNoTcpListen) { fss_->set_tcp_listen_enabled(false); ASSERT_TRUE(CreateSession(1)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(1U, candidates_.size()); EXPECT_EQ(1U, ports_.size()); EXPECT_TRUE( @@ -1582,7 +1507,9 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoSockets) { fss_->set_udp_sockets_enabled(false); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - SIMULATED_WAIT(!candidates_.empty(), 2000, fake_clock); + EXPECT_TRUE( + Waiter({.timeout = TimeDelta::Millis(2000), .clock = &time_controller_}) + .Until([&] { return !candidates_.empty(); })); // TODO(deadbeef): Check candidate_allocation_done signal. // In case of Relay, ports creation will succeed but sockets will fail. // There is no error reporting from RelayEntry to handle this failure. @@ -1594,11 +1521,8 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoUdpAllowed) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(2U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2U)), + IsRtcOk()); EXPECT_EQ(2U, ports_.size()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kClientAddr)); @@ -1606,10 +1530,9 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsNoUdpAllowed) { HasCandidate(candidates_, IceCandidateType::kHost, "tcp", kClientAddr)); // We wait at least for a full STUN timeout, which // STUN_TOTAL_TIMEOUT seconds. - EXPECT_THAT(WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(STUN_TOTAL_TIMEOUT), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(Waiter({.timeout = TimeDelta::Millis(STUN_TOTAL_TIMEOUT), + .clock = &time_controller_}) + .Until([&] { return candidate_allocation_done_; })); // No additional (STUN) candidates. EXPECT_EQ(2U, candidates_.size()); } @@ -1623,11 +1546,7 @@ TEST_F(BasicPortAllocatorTest, TestCandidatePriorityOfMultipleInterfaces) { PORTALLOCATOR_DISABLE_RELAY); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); ASSERT_EQ(2U, candidates_.size()); EXPECT_EQ(2U, ports_.size()); // Candidates priorities should be different. @@ -1639,11 +1558,7 @@ TEST_F(BasicPortAllocatorTest, TestGetAllPortsRestarts) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_EQ(3U, ports_.size()); // TODO(deadbeef): Extend this to verify ICE restart. @@ -1661,11 +1576,7 @@ TEST_F(BasicPortAllocatorTest, TestSessionUsesOwnCandidateFilter) { session_->StartGettingPorts(); // 7 candidates and 4 ports is what we would normally get (see the // TestGetAllPorts* tests). - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_EQ(3U, ports_.size()); } @@ -1682,11 +1593,7 @@ TEST_F(BasicPortAllocatorTest, TestCandidateFilterWithRelayOnly) { allocator().SetCandidateFilter(CF_RELAY); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kRelay, "udp", SocketAddress(kTurnUdpExtAddr.ipaddr(), 0))); @@ -1703,11 +1610,7 @@ TEST_F(BasicPortAllocatorTest, TestCandidateFilterWithHostOnly) { allocator().SetCandidateFilter(CF_HOST); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(2U, candidates_.size()); // Host UDP/TCP candidates only. EXPECT_EQ(2U, ports_.size()); // UDP/TCP ports only. for (const Candidate& candidate : candidates_) { @@ -1724,11 +1627,7 @@ TEST_F(BasicPortAllocatorTest, TestCandidateFilterWithReflexiveOnly) { allocator().SetCandidateFilter(CF_REFLEXIVE); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Host is behind NAT, no private address will be exposed. Hence only UDP // port with STUN candidate will be sent outside. EXPECT_EQ(1U, candidates_.size()); // Only STUN candidate. @@ -1745,11 +1644,7 @@ TEST_F(BasicPortAllocatorTest, TestCandidateFilterWithReflexiveOnlyAndNoNAT) { allocator().SetCandidateFilter(CF_REFLEXIVE); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Host has a public address, both UDP and TCP candidates will be exposed. EXPECT_EQ(2U, candidates_.size()); // Local UDP + TCP candidate. EXPECT_EQ(2U, ports_.size()); // UDP and TCP ports will be in ready state. @@ -1763,11 +1658,7 @@ TEST_F(BasicPortAllocatorTest, TestEnableSharedUfrag) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kClientAddr)); @@ -1792,19 +1683,12 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithoutNat) { PORTALLOCATOR_ENABLE_SHARED_SOCKET); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(2U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2U)), + IsRtcOk()); EXPECT_EQ(2U, ports_.size()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kClientAddr)); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); } // Test that when PORTALLOCATOR_ENABLE_SHARED_SOCKET is enabled only one port @@ -1818,21 +1702,14 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithNat) { PORTALLOCATOR_ENABLE_SHARED_SOCKET); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(3U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(3U)), + IsRtcOk()); ASSERT_EQ(2U, ports_.size()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kClientAddr)); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kSrflx, "udp", SocketAddress(kNatUdpAddr.ipaddr(), 0))); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); } @@ -1853,11 +1730,7 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithoutNatUsingTurn) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); ASSERT_EQ(3U, candidates_.size()); ASSERT_EQ(3U, ports_.size()); EXPECT_TRUE( @@ -1967,17 +1840,16 @@ TEST_F(BasicPortAllocatorTest, // Testing DNS resolve for the TURN server, this will test AllocationSequence // handling the unresolved address signal from TurnPort. -// TODO(pthatcher): Make this test work with SIMULATED_WAIT. It -// appears that it doesn't currently because of the DNS look up not -// using the fake clock. -TEST_F(BasicPortAllocatorTestWithRealClock, - TestSharedSocketWithServerAddressResolve) { +TEST_F(BasicPortAllocatorTest, TestSharedSocketWithServerAddressResolve) { // This test relies on a real query for "localhost", so it won't work on an // IPv6-only machine. MAYBE_SKIP_IPV4; turn_server_.AddInternalSocket(SocketAddress("127.0.0.1", 3478), PROTO_UDP); AddInterface(kClientAddr); - allocator_.emplace(env_, &network_manager_, &socket_factory_); + // Use a fake socket factory to avoid real DNS lookups which may not used a + // fake clock. + FakePacketSocketFactory fake_socket_factory(fss_.get()); + allocator_.emplace(env_, &network_manager_, &fake_socket_factory); allocator_->Initialize(); RelayServerConfig turn_server; RelayCredentials credentials(kTurnUsername, kTurnPassword); @@ -1994,10 +1866,9 @@ TEST_F(BasicPortAllocatorTestWithRealClock, ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return ports_.size(); }, Eq(2U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout)}), - IsRtcOk()); + EXPECT_THAT(waiter_.Until([&] { return ports_.size(); }, Eq(2U)), IsRtcOk()); + session_.reset(); + allocator_.reset(); } // Test that when PORTALLOCATOR_ENABLE_SHARED_SOCKET is enabled only one port @@ -2016,11 +1887,7 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithNatUsingTurn) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); ASSERT_EQ(2U, ports_.size()); EXPECT_TRUE( @@ -2029,11 +1896,7 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithNatUsingTurn) { SocketAddress(kNatUdpAddr.ipaddr(), 0))); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kRelay, "udp", SocketAddress(kTurnUdpExtAddr.ipaddr(), 0))); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // Local port will be created first and then TURN port. // TODO(deadbeef): This isn't something the BasicPortAllocator API contract // guarantees... @@ -2062,11 +1925,7 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithNatUsingTurnAsStun) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kClientAddr)); @@ -2101,11 +1960,7 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithNatUsingTurnTcpOnly) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(2U, candidates_.size()); ASSERT_EQ(2U, ports_.size()); EXPECT_TRUE( @@ -2132,11 +1987,7 @@ TEST_F(BasicPortAllocatorTest, TestNonSharedSocketWithNatUsingTurnAsStun) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); ASSERT_EQ(3U, ports_.size()); EXPECT_TRUE( @@ -2175,11 +2026,8 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithNatUsingTurnAndStun) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(3U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(3U)), + IsRtcOk()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kClientAddr)); Candidate stun_candidate; @@ -2205,19 +2053,15 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketNoUdpAllowed) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return ports_.size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return ports_.size(); }, Eq(1U)), IsRtcOk()); EXPECT_EQ(1U, candidates_.size()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kClientAddr)); // STUN timeout is 9.5sec. We need to wait to get candidate done signal. - EXPECT_THAT(WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kStunTimeoutMs), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE( + Waiter({.timeout = kStunTimeout, .clock = &time_controller_}).Until([&] { + return candidate_allocation_done_; + })); EXPECT_EQ(1U, candidates_.size()); } @@ -2237,11 +2081,7 @@ TEST_F(BasicPortAllocatorTest, TestNetworkPermissionBlocked) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); EXPECT_EQ(0U, session_->flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return ports_.size(); }, Eq(1U), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_THAT(waiter_.Until([&] { return ports_.size(); }, Eq(1U)), IsRtcOk()); EXPECT_EQ(1U, candidates_.size()); EXPECT_TRUE( HasCandidate(candidates_, IceCandidateType::kHost, "udp", kPrivateAddr)); @@ -2258,11 +2098,7 @@ TEST_F(BasicPortAllocatorTest, TestEnableIPv6Addresses) { allocator_->set_step_delay(kMinimumStepDelay); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(4U, ports_.size()); EXPECT_EQ(4U, candidates_.size()); EXPECT_TRUE(HasCandidate(candidates_, IceCandidateType::kHost, "udp", @@ -2280,14 +2116,11 @@ TEST_F(BasicPortAllocatorTest, TestStopGettingPorts) { allocator_->set_step_delay(kDefaultStepDelay); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT(WaitUntil([&] { return candidates_.size(); }, Eq(2U), - {.clock = &fake_clock}), + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2U)), IsRtcOk()); EXPECT_EQ(2U, ports_.size()); session_->StopGettingPorts(); - EXPECT_THAT(WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // After stopping getting ports, adding a new interface will not start // getting ports again. @@ -2296,7 +2129,7 @@ TEST_F(BasicPortAllocatorTest, TestStopGettingPorts) { ports_.clear(); candidate_allocation_done_ = false; network_manager_.AddInterface(kClientAddr2); - SIMULATED_WAIT(false, 1000, fake_clock); + time_controller_.AdvanceTime(TimeDelta::Millis(1000)); EXPECT_EQ(0U, candidates_.size()); EXPECT_EQ(0U, ports_.size()); } @@ -2306,14 +2139,11 @@ TEST_F(BasicPortAllocatorTest, TestClearGettingPorts) { allocator_->set_step_delay(kDefaultStepDelay); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT(WaitUntil([&] { return candidates_.size(); }, Eq(2U), - {.clock = &fake_clock}), + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2U)), IsRtcOk()); EXPECT_EQ(2U, ports_.size()); session_->ClearGettingPorts(); - EXPECT_THAT(WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); // After clearing getting ports, adding a new interface will start getting // ports again. @@ -2322,15 +2152,10 @@ TEST_F(BasicPortAllocatorTest, TestClearGettingPorts) { ports_.clear(); candidate_allocation_done_ = false; network_manager_.AddInterface(kClientAddr2); - ASSERT_THAT(WaitUntil([&] { return candidates_.size(); }, Eq(2U), - {.clock = &fake_clock}), + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2U)), IsRtcOk()); EXPECT_EQ(2U, ports_.size()); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); } // Test that the ports and candidates are updated with new ufrag/pwd/etc. when @@ -2342,12 +2167,8 @@ TEST_F(BasicPortAllocatorTest, TestTransportInformationUpdated) { allocator_->turn_servers(), pool_size, NO_PRUNE); const PortAllocatorSession* peeked_session = allocator_->GetPooledSession(); ASSERT_NE(nullptr, peeked_session); - EXPECT_THAT( - WaitUntil([&] { return peeked_session->CandidatesAllocationDone(); }, - IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until( + [&] { return peeked_session->CandidatesAllocationDone(); })); // Expect that when TakePooledSession is called, // UpdateTransportInformationInternal will be called and the // BasicPortAllocatorSession will update the ufrag/pwd of ports and @@ -2382,12 +2203,8 @@ TEST_F(BasicPortAllocatorTest, TestSetCandidateFilterAfterCandidatesGathered) { allocator_->turn_servers(), pool_size, NO_PRUNE); const PortAllocatorSession* peeked_session = allocator_->GetPooledSession(); ASSERT_NE(nullptr, peeked_session); - EXPECT_THAT( - WaitUntil([&] { return peeked_session->CandidatesAllocationDone(); }, - IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until( + [&] { return peeked_session->CandidatesAllocationDone(); })); size_t initial_candidates_size = peeked_session->ReadyCandidates().size(); size_t initial_ports_size = peeked_session->ReadyPorts().size(); allocator_->SetCandidateFilter(CF_RELAY); @@ -2434,41 +2251,28 @@ TEST_F(BasicPortAllocatorTest, allocator_->SetCandidateFilter(CF_NONE); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_TRUE(candidates_.empty()); EXPECT_TRUE(ports_.empty()); // Surface the relay candidate previously gathered but not signaled. session_->SetCandidateFilter(CF_RELAY); - ASSERT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(1u), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(1u)), + IsRtcOk()); EXPECT_TRUE(candidates_.back().is_relay()); EXPECT_EQ(1u, ports_.size()); // Surface the srflx candidate previously gathered but not signaled. session_->SetCandidateFilter(CF_RELAY | CF_REFLEXIVE); - ASSERT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(2u), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2u)), + IsRtcOk()); EXPECT_TRUE(candidates_.back().is_stun()); EXPECT_EQ(2u, ports_.size()); // Surface the srflx candidate previously gathered but not signaled. session_->SetCandidateFilter(CF_ALL); - ASSERT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(3u), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(3u)), + IsRtcOk()); EXPECT_TRUE(candidates_.back().is_local()); EXPECT_EQ(2u, ports_.size()); } @@ -2496,41 +2300,28 @@ TEST_F( allocator_->SetCandidateFilter(CF_NONE); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_TRUE(candidates_.empty()); EXPECT_TRUE(ports_.empty()); // Surface the relay candidate previously gathered but not signaled. session_->SetCandidateFilter(CF_RELAY); - EXPECT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(1u), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(1u)), + IsRtcOk()); EXPECT_TRUE(candidates_.back().is_relay()); EXPECT_EQ(1u, ports_.size()); // Surface the srflx candidate previously gathered but not signaled. session_->SetCandidateFilter(CF_REFLEXIVE); - EXPECT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(2u), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(2u)), + IsRtcOk()); EXPECT_TRUE(candidates_.back().is_stun()); EXPECT_EQ(2u, ports_.size()); // Surface the host candidate previously gathered but not signaled. session_->SetCandidateFilter(CF_HOST); - EXPECT_THAT( - WaitUntil([&] { return candidates_.size(); }, Eq(3u), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_THAT(waiter_.Until([&] { return candidates_.size(); }, Eq(3u)), + IsRtcOk()); EXPECT_TRUE(candidates_.back().is_local()); // We use a shared socket and UDPPort handles the srflx candidate. EXPECT_EQ(2u, ports_.size()); @@ -2553,11 +2344,7 @@ TEST_F(BasicPortAllocatorTest, allocator_->SetCandidateFilter(CF_NONE); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); auto test_invariants = [this]() { EXPECT_TRUE(candidates_.empty()); EXPECT_TRUE(ports_.empty()); @@ -2568,15 +2355,15 @@ TEST_F(BasicPortAllocatorTest, session_->StopGettingPorts(); session_->SetCandidateFilter(CF_RELAY); - SIMULATED_WAIT(false, kDefaultAllocationTimeout, fake_clock); + time_controller_.AdvanceTime(kDefaultAllocationTimeout); test_invariants(); session_->SetCandidateFilter(CF_RELAY | CF_REFLEXIVE); - SIMULATED_WAIT(false, kDefaultAllocationTimeout, fake_clock); + time_controller_.AdvanceTime(kDefaultAllocationTimeout); test_invariants(); session_->SetCandidateFilter(CF_ALL); - SIMULATED_WAIT(false, kDefaultAllocationTimeout, fake_clock); + time_controller_.AdvanceTime(kDefaultAllocationTimeout); test_invariants(); } @@ -2589,12 +2376,8 @@ TEST_F(BasicPortAllocatorTest, SetStunKeepaliveIntervalForPorts) { nullptr, expected_stun_keepalive_interval); auto* pooled_session = allocator_->GetPooledSession(); ASSERT_NE(nullptr, pooled_session); - EXPECT_THAT( - WaitUntil([&] { return pooled_session->CandidatesAllocationDone(); }, - IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until( + [&] { return pooled_session->CandidatesAllocationDone(); })); CheckStunKeepaliveIntervalOfAllReadyPorts(pooled_session, expected_stun_keepalive_interval); } @@ -2608,12 +2391,8 @@ TEST_F(BasicPortAllocatorTest, nullptr, 123 /* stun keepalive interval */); auto* pooled_session = allocator_->GetPooledSession(); ASSERT_NE(nullptr, pooled_session); - EXPECT_THAT( - WaitUntil([&] { return pooled_session->CandidatesAllocationDone(); }, - IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until( + [&] { return pooled_session->CandidatesAllocationDone(); })); const int expected_stun_keepalive_interval = 321; allocator_->SetConfiguration(allocator_->stun_servers(), allocator_->turn_servers(), pool_size, NO_PRUNE, @@ -2634,11 +2413,7 @@ TEST_F(BasicPortAllocatorTest, nullptr, expected_stun_keepalive_interval); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); CheckStunKeepaliveIntervalOfAllReadyPorts(session_.get(), expected_stun_keepalive_interval); } @@ -2655,11 +2430,7 @@ TEST_F(BasicPortAllocatorTest, nullptr, expected_stun_keepalive_interval); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); CheckStunKeepaliveIntervalOfAllReadyPorts(session_.get(), expected_stun_keepalive_interval); } @@ -2681,11 +2452,7 @@ TEST_F(BasicPortAllocatorTest, HostCandidateAddressIsReplacedByHostname) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(5u, candidates_.size()); int num_host_udp_candidates = 0; int num_host_tcp_candidates = 0; @@ -2762,11 +2529,7 @@ TEST_F(BasicPortAllocatorTest, AssignsUniqueLocalPreferencetoRelayCandidates) { AddInterface(kClientAddr); ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - ASSERT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + ASSERT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3u, candidates_.size()); EXPECT_GT((candidates_[0].priority() >> 8) & 0xFFFF, (candidates_[1].priority() >> 8) & 0xFFFF); @@ -2913,11 +2676,7 @@ TEST_F(BasicPortAllocatorTest, Select2DifferentIntefaces) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(2U, candidates_.size()); // ethe1 and wifi1 were selected. @@ -2943,11 +2702,7 @@ TEST_F(BasicPortAllocatorTest, Select3DifferentIntefaces) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(3U, candidates_.size()); // ethe1, wifi1, and cell1 were selected. @@ -2975,11 +2730,7 @@ TEST_F(BasicPortAllocatorTest, Select4DifferentIntefaces) { ASSERT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); session_->StartGettingPorts(); - EXPECT_THAT( - WaitUntil([&] { return candidate_allocation_done_; }, IsTrue(), - {.timeout = TimeDelta::Millis(kDefaultAllocationTimeout), - .clock = &fake_clock}), - IsRtcOk()); + EXPECT_TRUE(waiter_.Until([&] { return candidate_allocation_done_; })); EXPECT_EQ(4U, candidates_.size()); // ethe1, ethe2, wifi1, and cell1 were selected. @@ -2993,4 +2744,5 @@ TEST_F(BasicPortAllocatorTest, Select4DifferentIntefaces) { kClientIPv6Addr5)); } +} // namespace } // namespace webrtc diff --git a/p2p/dtls/dtls_ice_integration_bench.cc b/p2p/dtls/dtls_ice_integration_bench.cc index 0171e2fac75..73bc6f33307 100644 --- a/p2p/dtls/dtls_ice_integration_bench.cc +++ b/p2p/dtls/dtls_ice_integration_bench.cc @@ -114,11 +114,13 @@ TEST_P(DtlsIceIntegrationBenchmark, Benchmark) { RTC_LOG(LS_INFO) << GetParam() << " START"; SamplesStatsCounter stats = RunBenchmark(iter); - RTC_LOG(LS_INFO) << GetParam() << " RESULT:" - << " p10: " << stats.GetPercentile(0.10) - << " p50: " << stats.GetPercentile(0.50) - << " avg: " << stats.GetAverage() - << " p95: " << stats.GetPercentile(0.95); + RTC_LOG(LS_INFO) + << GetParam() << " RESULT:" + << " p10: " << stats.GetPercentile(0.10) + << " p50: " << stats.GetPercentile(0.50) + << " avg: " << stats.GetAverage() // Keep the format. + << " p95: " << stats.GetPercentile(0.95) << " " + << ::testing::UnitTest::GetInstance()->current_test_info()->name(); } } // namespace diff --git a/p2p/dtls/dtls_ice_integration_fixture.h b/p2p/dtls/dtls_ice_integration_fixture.h index a45134341e2..0b5374eb8dd 100644 --- a/p2p/dtls/dtls_ice_integration_fixture.h +++ b/p2p/dtls/dtls_ice_integration_fixture.h @@ -366,8 +366,9 @@ class Base { BuiltInNetworkBehaviorConfig networkBehavior; networkBehavior.link_capacity = DataRate::KilobitsPerSec(220); - networkBehavior.queue_delay_ms = - DtlsTransportInternalImpl::kDefaultHandshakeEstimateRttMs; + networkBehavior + .queue_delay_ms = /* this is one way delay, i.e. divide the rtt by 2 */ + DtlsTransportInternalImpl::kDefaultHandshakeEstimateRttMs / 2; networkBehavior.queue_length_packets = 30; networkBehavior.loss_percent = pct_loss; @@ -565,6 +566,10 @@ class Base { ep.env, ep.ice_transport, crypto_options, ep.config.max_protocol_version); + if (ice_lite_agent) { + ep.dtls->SetFakeIceLite(); + } + // Enable(or disable) the dtls_in_stun parameter before // DTLS is negotiated. IceConfig config; diff --git a/p2p/dtls/dtls_ice_integration_test.cc b/p2p/dtls/dtls_ice_integration_test.cc index 577204d25c5..7f8d51a97c9 100644 --- a/p2p/dtls/dtls_ice_integration_test.cc +++ b/p2p/dtls/dtls_ice_integration_test.cc @@ -86,17 +86,68 @@ class DtlsIceIntegrationTest : public dtls_ice_integration_fixture::Base, [](auto con) { return con.writable; }); } - // TODO(webrtc:404763475): In this specific config, - // the server (acting as SSL_CLIENT) gets a retransmission. - // This should be fixed. - bool server_retransmissions_bug() { - return client_.config.ssl_role == SSL_SERVER && - (client_.config.dtls_in_stun && !server_.config.dtls_in_stun) && - (client_.config.pqc && server_.config.pqc); + void CheckRetransmissions() { + if (!server_.config.ice_lite) { + EXPECT_EQ(client_.dtls->GetRetransmissionCount(), 0); + EXPECT_EQ(server_.dtls->GetRetransmissionCount(), 0); + return; + } + if (client_.config.ssl_role == SSL_CLIENT) { + EXPECT_EQ(client_.dtls->GetRetransmissionCount(), 0); + EXPECT_EQ(server_.dtls->GetRetransmissionCount(), 0); + return; + } + + // TODO: bugs.webrtc.org/367395350 - Investigate retransmissions + // from in fake ice lite scenarios. Very few remaining! + // - server is (fake) ICE lite but SSL_CLIENT + if (client_.config.dtls_in_stun == server_.config.dtls_in_stun) { + const bool pqc = client_.config.pqc && server_.config.pqc; + EXPECT_EQ(client_.dtls->GetRetransmissionCount(), 0); + EXPECT_LE(server_.dtls->GetRetransmissionCount(), !pqc ? 0 : 1); + return; + } + + EXPECT_LE(client_.dtls->GetRetransmissionCount(), 1); + EXPECT_LE(server_.dtls->GetRetransmissionCount(), 1); } }; TEST_P(DtlsIceIntegrationTest, SmokeTest) { + ConfigureEmulatedNetwork(/* pct_loss= */ 0); + Prepare(); + client_thread()->PostTask([&]() { client_.ice()->MaybeStartGathering(); }); + server_thread()->PostTask([&]() { server_.ice()->MaybeStartGathering(); }); + + // Note: this only reaches the pending piggybacking state. + EXPECT_THAT( + WaitUntil( + [&] { return client_.dtls->writable() && server_.dtls->writable(); }, + IsTrue(), wait_until_settings()), + IsRtcOk()); + + client_thread()->BlockingCall([&]() { + EXPECT_EQ(client_.dtls->IsDtlsPiggybackSupportedByPeer(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + EXPECT_EQ(client_.dtls->WasDtlsCompletedByPiggybacking(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + }); + server_thread()->BlockingCall([&]() { + EXPECT_EQ(server_.dtls->IsDtlsPiggybackSupportedByPeer(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + EXPECT_EQ(server_.dtls->WasDtlsCompletedByPiggybacking(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + }); + + if (client_.config.dtls_in_stun && server_.config.dtls_in_stun) { + EXPECT_GE(dtls_client().dtls->GetStunDataCount(), 0); + EXPECT_GE(dtls_server().dtls->GetStunDataCount(), 0); + } + + CheckRetransmissions(); +} + +TEST_P(DtlsIceIntegrationTest, AddCandidates) { Prepare(); client_.ice()->MaybeStartGathering(); server_.ice()->MaybeStartGathering(); @@ -116,21 +167,9 @@ TEST_P(DtlsIceIntegrationTest, SmokeTest) { EXPECT_EQ(server_.dtls->WasDtlsCompletedByPiggybacking(), client_.config.dtls_in_stun && server_.config.dtls_in_stun); - if (GetParam().ice_lite && - (client_.config.dtls_in_stun && server_.config.dtls_in_stun)) { - // TODO(webrtc:404763475) - verify what counters we expect here for ICE Lite - // and dtls-in-stun. - } else if (!(client_.config.pqc || server_.config.pqc) && - client_.config.dtls_in_stun && server_.config.dtls_in_stun) { - EXPECT_EQ(dtls_client().dtls->GetStunDataCount(), 1); - EXPECT_EQ(dtls_server().dtls->GetStunDataCount(), 2); - } else { - // TODO(webrtc:404763475) - } - - EXPECT_EQ(client_.dtls->GetRetransmissionCount(), 0); - if (!server_retransmissions_bug()) { - EXPECT_EQ(server_.dtls->GetRetransmissionCount(), 0); + if (client_.config.dtls_in_stun && server_.config.dtls_in_stun) { + EXPECT_GE(dtls_client().dtls->GetStunDataCount(), 0); + EXPECT_GE(dtls_server().dtls->GetStunDataCount(), 0); } // Validate that we can add new Connections (that become writable). @@ -149,15 +188,21 @@ TEST_P(DtlsIceIntegrationTest, SmokeTest) { // connected. Before this patch, this would disable stun-piggy-backing. TEST_P(DtlsIceIntegrationTest, ClientLateCertificate) { client_.store_but_dont_set_remote_fingerprint = true; + ConfigureEmulatedNetwork(/* pct_loss= */ 0); Prepare(); - client_.ice()->MaybeStartGathering(); - server_.ice()->MaybeStartGathering(); + client_thread()->PostTask([&]() { client_.ice()->MaybeStartGathering(); }); + server_thread()->PostTask([&]() { server_.ice()->MaybeStartGathering(); }); - ASSERT_THAT( - WaitUntil([&] { return CountWritableConnections(client_.ice()) > 0; }, - IsTrue(), wait_until_settings()), - IsRtcOk()); - SetRemoteFingerprint(client_); + ASSERT_THAT(WaitUntil( + [&] { + return client_thread()->BlockingCall([&]() { + return CountWritableConnections(client_.ice()); + }) > 0; + }, + IsTrue(), wait_until_settings()), + IsRtcOk()); + + client_thread()->BlockingCall([&]() { SetRemoteFingerprint(client_); }); ASSERT_THAT( WaitUntil( @@ -165,18 +210,18 @@ TEST_P(DtlsIceIntegrationTest, ClientLateCertificate) { IsTrue(), wait_until_settings()), IsRtcOk()); - EXPECT_EQ(client_.dtls->IsDtlsPiggybackSupportedByPeer(), - client_.config.dtls_in_stun && server_.config.dtls_in_stun); - - EXPECT_EQ(client_.dtls->WasDtlsCompletedByPiggybacking(), - client_.config.dtls_in_stun && server_.config.dtls_in_stun); - EXPECT_EQ(server_.dtls->WasDtlsCompletedByPiggybacking(), - client_.config.dtls_in_stun && server_.config.dtls_in_stun); + client_thread()->BlockingCall([&]() { + EXPECT_EQ(client_.dtls->IsDtlsPiggybackSupportedByPeer(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + EXPECT_EQ(client_.dtls->WasDtlsCompletedByPiggybacking(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + }); + server_thread()->BlockingCall([&]() { + EXPECT_EQ(server_.dtls->WasDtlsCompletedByPiggybacking(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + }); - EXPECT_EQ(client_.dtls->GetRetransmissionCount(), 0); - if (!server_retransmissions_bug()) { - EXPECT_EQ(server_.dtls->GetRetransmissionCount(), 0); - } + CheckRetransmissions(); } TEST_P(DtlsIceIntegrationTest, TestWithPacketLoss) { @@ -188,7 +233,6 @@ TEST_P(DtlsIceIntegrationTest, TestWithPacketLoss) { Prepare(); client_thread()->PostTask([&]() { client_.ice()->MaybeStartGathering(); }); - server_thread()->PostTask([&]() { server_.ice()->MaybeStartGathering(); }); EXPECT_THAT(WaitUntil( @@ -227,7 +271,6 @@ TEST_P(DtlsIceIntegrationTest, LongRunningTestWithPacketLoss) { Prepare(); client_thread()->PostTask([&]() { client_.ice()->MaybeStartGathering(); }); - server_thread()->PostTask([&]() { server_.ice()->MaybeStartGathering(); }); ASSERT_THAT(WaitUntil( @@ -318,6 +361,7 @@ TEST_P(DtlsIceIntegrationTest, LongRunningTestWithPacketLoss) { // Verify that DtlsStunPiggybacking works even if one (or several) // of the STUN_BINDING_REQUESTs are so full that dtls does not fit. TEST_P(DtlsIceIntegrationTest, AlmostFullSTUN_BINDING) { + ConfigureEmulatedNetwork(/* pct_loss= */ 0); Prepare(); std::string a_long_string(500, 'a'); @@ -326,8 +370,8 @@ TEST_P(DtlsIceIntegrationTest, AlmostFullSTUN_BINDING) { server_.ice()->GetDictionaryWriter()->get().SetByteString(78)->CopyBytes( a_long_string); - client_.ice()->MaybeStartGathering(); - server_.ice()->MaybeStartGathering(); + client_thread()->PostTask([&]() { client_.ice()->MaybeStartGathering(); }); + server_thread()->PostTask([&]() { server_.ice()->MaybeStartGathering(); }); // Note: this only reaches the pending piggybacking state. EXPECT_THAT( @@ -335,30 +379,30 @@ TEST_P(DtlsIceIntegrationTest, AlmostFullSTUN_BINDING) { [&] { return client_.dtls->writable() && server_.dtls->writable(); }, IsTrue(), wait_until_settings()), IsRtcOk()); - EXPECT_EQ(client_.dtls->IsDtlsPiggybackSupportedByPeer(), - client_.config.dtls_in_stun && server_.config.dtls_in_stun); - EXPECT_EQ(server_.dtls->IsDtlsPiggybackSupportedByPeer(), - client_.config.dtls_in_stun && server_.config.dtls_in_stun); - EXPECT_EQ(client_.dtls->WasDtlsCompletedByPiggybacking(), - client_.config.dtls_in_stun && server_.config.dtls_in_stun); - EXPECT_EQ(server_.dtls->WasDtlsCompletedByPiggybacking(), - client_.config.dtls_in_stun && server_.config.dtls_in_stun); - if (GetParam().ice_lite && - (client_.config.dtls_in_stun && server_.config.dtls_in_stun)) { - // TODO(webrtc:404763475) - verify what counters we expect here for ICE Lite - // and dtls-in-stun. - } else if (!(client_.config.pqc || server_.config.pqc) && - client_.config.dtls_in_stun && server_.config.dtls_in_stun) { - EXPECT_EQ(dtls_client().dtls->GetStunDataCount(), 1); - EXPECT_EQ(dtls_server().dtls->GetStunDataCount(), 2); - } else { - // TODO(webrtc:404763475) - figure out what value we want/expect here. - } + client_thread()->BlockingCall([&]() { + EXPECT_EQ(client_.dtls->IsDtlsPiggybackSupportedByPeer(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + EXPECT_EQ(client_.dtls->WasDtlsCompletedByPiggybacking(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + }); - EXPECT_EQ(client_.dtls->GetRetransmissionCount(), 0); - if (!server_retransmissions_bug()) { - EXPECT_EQ(server_.dtls->GetRetransmissionCount(), 0); + server_thread()->BlockingCall([&]() { + EXPECT_EQ(server_.dtls->IsDtlsPiggybackSupportedByPeer(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + EXPECT_EQ(server_.dtls->WasDtlsCompletedByPiggybacking(), + client_.config.dtls_in_stun && server_.config.dtls_in_stun); + }); + + if (client_.config.dtls_in_stun && server_.config.dtls_in_stun && + (client_.config.pqc || server_.config.pqc)) { + // TODO: bugs.webrtc.org/367395350 - Investigate why there is + // retransmissions in the scenario where the STUN packet is almost full, + // e.g. will the issue be solved by our effort to smooth BoringSSL packets ? + EXPECT_LE(client_.dtls->GetRetransmissionCount(), 1); + EXPECT_LE(server_.dtls->GetRetransmissionCount(), 1); + } else { + CheckRetransmissions(); } } diff --git a/p2p/dtls/dtls_stun_piggyback_callbacks.h b/p2p/dtls/dtls_stun_piggyback_callbacks.h index 241d51e964b..e3cb33afbcc 100644 --- a/p2p/dtls/dtls_stun_piggyback_callbacks.h +++ b/p2p/dtls/dtls_stun_piggyback_callbacks.h @@ -13,12 +13,12 @@ #include #include +#include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/transport/stun.h" #include "rtc_base/checks.h" @@ -38,10 +38,10 @@ class DtlsStunPiggybackCallbacks { /* request-type */ StunMessageType)>&& send_data, // Function invoked when receiving a STUN_BINDING { REQUEST / RESPONSE } - // contains the optional ArrayView of the DTLS_IN_STUN attribute and the + // contains the optional std::span of the DTLS_IN_STUN attribute and the // optional uint32_t vector DTLS_IN_STUN_ACK attribute. absl::AnyInvocable> /* recv_data */, + std::optional> /* recv_data */, std::optional> /* recv_acks */)>&& recv_data) : send_data_(std::move(send_data)), recv_data_(std::move(recv_data)) { RTC_DCHECK( @@ -58,7 +58,7 @@ class DtlsStunPiggybackCallbacks { return send_data_(request_type); } - void recv_data(std::optional> data, + void recv_data(std::optional> data, std::optional> acks) { RTC_DCHECK(recv_data_); return recv_data_(data, acks); @@ -75,7 +75,7 @@ class DtlsStunPiggybackCallbacks { std::optional>>( /* request-type */ StunMessageType)> send_data_; - absl::AnyInvocable> /* recv_data */, + absl::AnyInvocable> /* recv_data */, std::optional> /* recv_acks */)> recv_data_; }; diff --git a/p2p/dtls/dtls_stun_piggyback_controller.cc b/p2p/dtls/dtls_stun_piggyback_controller.cc index 2ff48d3c939..cb7d0c4cfe5 100644 --- a/p2p/dtls/dtls_stun_piggyback_controller.cc +++ b/p2p/dtls/dtls_stun_piggyback_controller.cc @@ -13,42 +13,39 @@ #include #include #include +#include #include #include #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/sequence_checker.h" #include "api/transport/stun.h" #include "p2p/dtls/dtls_utils.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/network/received_packet.h" #include "rtc_base/strings/str_join.h" namespace webrtc { DtlsStunPiggybackController::DtlsStunPiggybackController( - absl::AnyInvocable)> dtls_data_callback) - : dtls_data_callback_(std::move(dtls_data_callback)) {} - -DtlsStunPiggybackController::~DtlsStunPiggybackController() {} + absl::AnyInvocable)> dtls_data_callback, + // NOLINTNEXTLINE(readability/casting) - not a cast; false positive! + absl::AnyInvocable piggyback_complete_callback) + : dtls_data_callback_(std::move(dtls_data_callback)), + piggyback_complete_callback_(std::move(piggyback_complete_callback)) {} + +DtlsStunPiggybackController::~DtlsStunPiggybackController() { + RTC_DCHECK(dtls_data_callback_); + RTC_DCHECK(piggyback_complete_callback_); +} void DtlsStunPiggybackController::SetDtlsHandshakeComplete(bool is_dtls_client, bool is_dtls13) { RTC_DCHECK_RUN_ON(&sequence_checker_); - // As DTLS 1.2 server we need to keep the last flight around until - // we receive the post-handshake acknowledgment. - // As DTLS 1.2 client we have nothing more to send at this point - // but will continue to send ACK attributes until receiving - // the last flight from the server. - // For DTLS 1.3 this is reversed since the handshake has one round trip less. - if ((is_dtls_client && !is_dtls13) || (!is_dtls_client && is_dtls13)) { - pending_packets_.clear(); - } - // Peer does not support this so fallback to a normal DTLS handshake // happened. if (state_ == State::OFF) { @@ -57,6 +54,32 @@ void DtlsStunPiggybackController::SetDtlsHandshakeComplete(bool is_dtls_client, state_ = State::PENDING; } +void DtlsStunPiggybackController::ApplicationPacketReceived( + const ReceivedIpPacket& packet) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + + if (state_ == State::OFF) { + return; + } + + RTC_DCHECK(packet.decryption_info() == ReceivedIpPacket::kDtlsDecrypted || + packet.decryption_info() == ReceivedIpPacket::kSrtpEncrypted); + + if (packet.decryption_info() == ReceivedIpPacket::kDtlsDecrypted) { + // We should be writable before this to happen. + RTC_DCHECK(state_ == State::PENDING); + } else if (packet.decryption_info() == ReceivedIpPacket::kSrtpEncrypted) { + // Peer sending encrypted srtp mean that it must be writable, + // but we don't necessarily know that it's decodable. However, if + // we are also dtls-writable (PENDING) this means that we are complete. + if (state_ != State::PENDING) { + return; + } + } + state_ = State::COMPLETE; + CallCompleteCallback(/*success=*/true); +} + void DtlsStunPiggybackController::SetDtlsFailed() { RTC_DCHECK_RUN_ON(&sequence_checker_); @@ -66,9 +89,10 @@ void DtlsStunPiggybackController::SetDtlsFailed() { << "DTLS-STUN piggybacking DTLS failed during negotiation."; } state_ = State::OFF; + CallCompleteCallback(/*success=*/false); } -void DtlsStunPiggybackController::CapturePacket(ArrayView data) { +void DtlsStunPiggybackController::CapturePacket(std::span data) { RTC_DCHECK_RUN_ON(&sequence_checker_); if (!IsDtlsPacket(data)) { return; @@ -76,7 +100,7 @@ void DtlsStunPiggybackController::CapturePacket(ArrayView data) { // BoringSSL writes burst of packets...but the interface // is made for 1-packet at a time. Use the writing_packets_ variable to keep - // track of a full batch. The writing_packets_ is reset in Flush. + // track of a full flight. The writing_packets_ is reset in Flush. if (!writing_packets_) { pending_packets_.clear(); writing_packets_ = true; @@ -91,6 +115,8 @@ void DtlsStunPiggybackController::ClearCachedPacketForTesting() { } void DtlsStunPiggybackController::Flush() { + // Flush is called by the StreamInterface (and the underlying SSL BIO) + // after a flight of packets has been sent. RTC_DCHECK_RUN_ON(&sequence_checker_); writing_packets_ = false; } @@ -102,9 +128,6 @@ DtlsStunPiggybackController::GetDataToPiggyback( RTC_DCHECK(stun_message_type == STUN_BINDING_REQUEST || stun_message_type == STUN_BINDING_RESPONSE); - // No longer writing packets...since we're now about to send them. - RTC_DCHECK(!writing_packets_); - if (state_ == State::COMPLETE) { return std::nullopt; } @@ -113,6 +136,9 @@ DtlsStunPiggybackController::GetDataToPiggyback( return std::nullopt; } + // No longer writing packets...since we're now about to send them. + RTC_DCHECK(!writing_packets_); + if (pending_packets_.empty()) { return std::nullopt; } @@ -133,14 +159,14 @@ DtlsStunPiggybackController::GetAckToPiggyback( return handshake_messages_received_; } -std::vector> +std::vector> DtlsStunPiggybackController::GetPending() { RTC_DCHECK_RUN_ON(&sequence_checker_); return pending_packets_.GetAll(); } void DtlsStunPiggybackController::ReportDataPiggybacked( - std::optional> data, + std::optional> data, std::optional> acks) { RTC_DCHECK_RUN_ON(&sequence_checker_); @@ -150,27 +176,20 @@ void DtlsStunPiggybackController::ReportDataPiggybacked( return; } - // We sent dtls piggybacked but got nothing in return or - // we received a stun request with neither attribute set - // => peer does not support. - if (state_ == State::TENTATIVE && !data.has_value() && !acks.has_value()) { - RTC_LOG(LS_INFO) << "DTLS-STUN piggybacking not supported by peer."; - state_ = State::OFF; - return; - } - - // In PENDING state the peer may have stopped sending the ack - // when it moved to the COMPLETE state. Move to the same state. - if (state_ == State::PENDING && !data.has_value() && !acks.has_value()) { - RTC_LOG(LS_INFO) << "DTLS-STUN piggybacking complete."; - state_ = State::COMPLETE; - pending_packets_.clear(); - handshake_messages_received_.clear(); - return; - } - - // We sent dtls piggybacked and got something in return => peer does support. if (state_ == State::TENTATIVE) { + if (!data.has_value() && !acks.has_value()) { + // We sent dtls piggybacked but got nothing in return or + // we received a stun request with neither attribute set + // => peer does not support. + RTC_LOG(LS_INFO) << "DTLS-STUN piggybacking not supported by peer."; + state_ = State::OFF; + // TODO: bugs.webrtc.org/367395350 - We should call CallCompleteCallback + // here but this causes a slew of failed tests. Investigate why! + // CallCompleteCallback(/*success=*/false); + return; + } + // We sent dtls piggybacked and got something in return => peer does + // support. state_ = State::CONFIRMED; } @@ -189,43 +208,33 @@ void DtlsStunPiggybackController::ReportDataPiggybacked( } } - if (!data.has_value()) { - // If we receive msg w/o any data, that means that the peer - // is not retransmitting, so we don't need to ACK anything. - handshake_messages_received_.clear(); + if (data.has_value() && !data->empty()) { + // Drop non-DTLS packets. + if (!IsDtlsPacket(*data)) { + RTC_LOG(LS_WARNING) << "Dropping non-DTLS data."; + return; + } + ++data_recv_count_; + ReportDtlsPacket(*data); + + // Forwards the data to the DTLS layer. Note that this will call + // ProcessDtlsPacket() again which does not change the state. + dtls_data_callback_(*data); } - // The response to the final flight of the handshake will not contain - // the DTLS data but will contain an ack. - // Must not happen on the initial server to client packet which - // has no DTLS data yet. - if (!data.has_value() && acks.has_value() && state_ == State::PENDING) { + if (state_ == State::PENDING && pending_packets_.empty()) { + // We are writeable(PENDING) and have no pending packets, i.e. + // peer has acked everything we sent, this means that we + // are complete. RTC_LOG(LS_INFO) << "DTLS-STUN piggybacking complete."; state_ = State::COMPLETE; - pending_packets_.clear(); - handshake_messages_received_.clear(); + CallCompleteCallback(/*success=*/true); return; } - - if (!data.has_value() || data->empty()) { - return; - } - // Drop non-DTLS packets. - if (!IsDtlsPacket(*data)) { - RTC_LOG(LS_WARNING) << "Dropping non-DTLS data."; - return; - } - data_recv_count_++; - - ReportDtlsPacket(*data); - - // Forwards the data to the DTLS layer. Note that this will call - // ProcessDtlsPacket() again which does not change the state. - dtls_data_callback_(*data); } void DtlsStunPiggybackController::ReportDtlsPacket( - ArrayView data) { + std::span data) { RTC_DCHECK_RUN_ON(&sequence_checker_); if (state_ == State::OFF || state_ == State::COMPLETE) { @@ -248,4 +257,15 @@ void DtlsStunPiggybackController::ReportDtlsPacket( } } +void DtlsStunPiggybackController::CallCompleteCallback(bool success) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + pending_packets_.clear(); + handshake_messages_received_.clear(); + if (!piggyback_complete_callback_) { + RTC_DCHECK_NOTREACHED() << "CompleteCallback called twice!"; + return; + } + std::move(piggyback_complete_callback_)(success); +} + } // namespace webrtc diff --git a/p2p/dtls/dtls_stun_piggyback_controller.h b/p2p/dtls/dtls_stun_piggyback_controller.h index 6c11a377cfc..18af1bdef30 100644 --- a/p2p/dtls/dtls_stun_piggyback_controller.h +++ b/p2p/dtls/dtls_stun_piggyback_controller.h @@ -13,14 +13,15 @@ #include #include +#include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/sequence_checker.h" #include "api/transport/stun.h" #include "p2p/dtls/dtls_utils.h" +#include "rtc_base/network/received_packet.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread_annotations.h" @@ -36,7 +37,10 @@ class DtlsStunPiggybackController { // dtls_data_callback will be called with any DTLS packets received // piggybacked. DtlsStunPiggybackController( - absl::AnyInvocable)> dtls_data_callback); + absl::AnyInvocable)> dtls_data_callback, + // NOLINTNEXTLINE(readability/casting) - not a cast; false positive! + absl::AnyInvocable piggyback_complete_callback); + ~DtlsStunPiggybackController(); enum class State { @@ -60,14 +64,22 @@ class DtlsStunPiggybackController { return state_; } - // Called by DtlsTransport when the handshake is complete. + // Called by DtlsTransport when the handshake is complete "locally", + // i.e. we can send encrypted packets to peer (but we don't strictly know + // that peer can decode them). void SetDtlsHandshakeComplete(bool is_dtls_client, bool is_dtls13); + + // Called by DtlsTransport when a packet has been received and passed + // to layers above us. This means that dtls is writable for the peer, + // and maybe we are complete. + void ApplicationPacketReceived(const ReceivedIpPacket& packet); + // Called by DtlsTransport when DTLS failed. void SetDtlsFailed(); // Intercepts DTLS packets which should go into the STUN packets during the // handshake. - void CapturePacket(ArrayView data); + void CapturePacket(std::span data); void ClearCachedPacketForTesting(); // Inform piggybackcontroller that a flight is complete. @@ -79,17 +91,17 @@ class DtlsStunPiggybackController { StunMessageType stun_message_type); std::optional> GetAckToPiggyback( StunMessageType stun_message_type); - std::vector> GetPending(); + std::vector> GetPending(); // Called by Connection when receiving a STUN BINDING { REQUEST / RESPONSE }. - void ReportDataPiggybacked(std::optional> data, + void ReportDataPiggybacked(std::optional> data, std::optional> acks); // Called by // * DTLSTransport when receiving a DTLS packet (possibly after the packet // was emitted by this class). // * This class when processing a DTLS packet. - void ReportDtlsPacket(ArrayView data); + void ReportDtlsPacket(std::span data); int GetCountOfReceivedData() const { return data_recv_count_; } @@ -97,8 +109,11 @@ class DtlsStunPiggybackController { State state_ RTC_GUARDED_BY(sequence_checker_) = State::TENTATIVE; bool writing_packets_ RTC_GUARDED_BY(sequence_checker_) = false; PacketStash pending_packets_ RTC_GUARDED_BY(sequence_checker_); - absl::AnyInvocable)> dtls_data_callback_; - absl::AnyInvocable disable_piggybacking_callback_; + absl::AnyInvocable)> dtls_data_callback_ + RTC_GUARDED_BY(sequence_checker_); + // NOLINTNEXTLINE(readability/casting) - not a cast; false positive! + absl::AnyInvocable piggyback_complete_callback_ + RTC_GUARDED_BY(sequence_checker_); std::vector handshake_messages_received_ RTC_GUARDED_BY(sequence_checker_); @@ -106,6 +121,8 @@ class DtlsStunPiggybackController { // Count of embedded data attributes received. int data_recv_count_ = 0; + void CallCompleteCallback(bool success); + // In practice this will be the network thread. RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; }; diff --git a/p2p/dtls/dtls_stun_piggyback_controller_unittest.cc b/p2p/dtls/dtls_stun_piggyback_controller_unittest.cc index 49894be85c0..7ff86af6d99 100644 --- a/p2p/dtls/dtls_stun_piggyback_controller_unittest.cc +++ b/p2p/dtls/dtls_stun_piggyback_controller_unittest.cc @@ -13,18 +13,22 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/transport/stun.h" #include "p2p/dtls/dtls_utils.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/checks.h" +#include "rtc_base/network/received_packet.h" +#include "rtc_base/socket_address.h" #include "test/gmock.h" #include "test/gtest.h" +namespace webrtc { + namespace { // Extracted from a stock DTLS call using Wireshark. // Each packet (apart from the last) is truncated to @@ -57,8 +61,10 @@ const std::vector dtls_flight4 = { const std::vector empty = {}; -std::vector FromAckAttribute(webrtc::ArrayView attr) { - webrtc::ByteBufferReader ack_reader(attr); +const std::vector kPayload = {0x1, 0x2, 0x3}; + +std::vector FromAckAttribute(std::span attr) { + ByteBufferReader ack_reader(attr); std::vector values; uint32_t value; while (ack_reader.ReadUInt32(&value)) { @@ -75,29 +81,26 @@ std::vector FakeDtlsPacket(uint16_t packet_number) { return packet; } -std::unique_ptr WrapInStun( - webrtc::IceAttributeType type, - absl::string_view data) { - return std::make_unique(type, data); +std::unique_ptr WrapInStun(IceAttributeType type, + absl::string_view data) { + return std::make_unique(type, data); } -std::unique_ptr WrapInStun( - webrtc::IceAttributeType type, +std::unique_ptr WrapInStun( + IceAttributeType type, const std::vector& data) { - return std::make_unique(type, data.data(), - data.size()); + return std::make_unique(type, data.data(), + data.size()); } -std::unique_ptr WrapInStun( - webrtc::IceAttributeType type, +std::unique_ptr WrapInStun( + IceAttributeType type, const std::vector& data) { - return std::make_unique(type, data); + return std::make_unique(type, data); } } // namespace -namespace webrtc { - using ::testing::ElementsAreArray; using ::testing::MockFunction; using ::testing::NotNull; @@ -107,10 +110,12 @@ class DtlsStunPiggybackControllerTest : public ::testing::Test { protected: DtlsStunPiggybackControllerTest() : client_( - [this](ArrayView data) { ClientPacketSink(data); }), - server_([this](ArrayView data) { - ServerPacketSink(data); - }) {} + [this](std::span data) { ClientPacketSink(data); }, + [this](bool success) { ClientCompleteCallback(success); }), + server_( + [this](std::span data) { ServerPacketSink(data); }, + [this](bool success) { ServerCompleteCallback(success); }), + packet_(kPayload, SocketAddress(), std::nullopt) {} // Send from client to server embedded in STUN. void SendClientToServerEmbedded(const std::vector& packet, @@ -122,7 +127,7 @@ class DtlsStunPiggybackControllerTest : public ::testing::Test { client_.ClearCachedPacketForTesting(); } std::unique_ptr attr_data; - std::optional> view_data; + std::optional> view_data; if (auto data = client_.GetDataToPiggyback(type)) { attr_data = WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, *data); view_data = attr_data->array_view(); @@ -155,7 +160,7 @@ class DtlsStunPiggybackControllerTest : public ::testing::Test { server_.ClearCachedPacketForTesting(); } std::unique_ptr attr_data; - std::optional> view_data; + std::optional> view_data; if (auto data = server_.GetDataToPiggyback(type)) { attr_data = WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, *data); view_data = attr_data->array_view(); @@ -190,8 +195,13 @@ class DtlsStunPiggybackControllerTest : public ::testing::Test { DtlsStunPiggybackController client_; DtlsStunPiggybackController server_; - MOCK_METHOD(void, ClientPacketSink, (ArrayView)); - MOCK_METHOD(void, ServerPacketSink, (ArrayView)); + MOCK_METHOD(void, ClientPacketSink, (std::span)); + MOCK_METHOD(void, ServerPacketSink, (std::span)); + + MOCK_METHOD(void, ClientCompleteCallback, (bool)); + MOCK_METHOD(void, ServerCompleteCallback, (bool)); + + ReceivedIpPacket packet_; private: void MaybeSetHandshakeComplete(std::vector packet) { @@ -220,12 +230,74 @@ TEST_F(DtlsStunPiggybackControllerTest, BasicHandshake) { EXPECT_EQ(client_.state(), State::PENDING); // Post-handshake ACK + EXPECT_CALL(*this, ClientCompleteCallback(true)); SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST); + EXPECT_CALL(*this, ServerCompleteCallback(true)); SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE); EXPECT_EQ(server_.state(), State::COMPLETE); EXPECT_EQ(client_.state(), State::COMPLETE); } +TEST_F(DtlsStunPiggybackControllerTest, + BasicHandshakeCompleteWithDecryptedPacket) { + // Flight 1+2 + SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST); + EXPECT_EQ(server_.state(), State::CONFIRMED); + SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE); + EXPECT_EQ(client_.state(), State::CONFIRMED); + + // Flight 3+4 + SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST); + SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE); + EXPECT_EQ(server_.state(), State::PENDING); + EXPECT_EQ(client_.state(), State::PENDING); + + // Post-handshake ACK + EXPECT_CALL(*this, ClientCompleteCallback); + client_.ApplicationPacketReceived( + packet_.CopyAndSet(ReceivedIpPacket::kDtlsDecrypted)); + EXPECT_EQ(client_.state(), State::COMPLETE); + + EXPECT_CALL(*this, ServerCompleteCallback); + server_.ApplicationPacketReceived( + packet_.CopyAndSet(ReceivedIpPacket::kSrtpEncrypted)); + EXPECT_EQ(server_.state(), State::COMPLETE); +} + +TEST_F(DtlsStunPiggybackControllerTest, + BasicHandshakeEarlySrtpDoesNotComplete) { + // Flight 1+2 + SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST); + EXPECT_EQ(server_.state(), State::CONFIRMED); + SendServerToClientEmbedded(dtls_flight2, STUN_BINDING_RESPONSE); + EXPECT_EQ(client_.state(), State::CONFIRMED); + + // Flight 3 + SendClientToServerEmbedded(dtls_flight3, STUN_BINDING_REQUEST); + EXPECT_EQ(server_.state(), State::CONFIRMED); + + // An srtp packet arriving before reaching PENDING state. + server_.ApplicationPacketReceived( + packet_.CopyAndSet(ReceivedIpPacket::kSrtpEncrypted)); + EXPECT_EQ(server_.state(), State::CONFIRMED); + + // Flight 4 + SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_RESPONSE); + EXPECT_EQ(server_.state(), State::PENDING); + EXPECT_EQ(client_.state(), State::PENDING); + + // Post-handshake ACK + EXPECT_CALL(*this, ClientCompleteCallback); + client_.ApplicationPacketReceived( + packet_.CopyAndSet(ReceivedIpPacket::kDtlsDecrypted)); + EXPECT_EQ(client_.state(), State::COMPLETE); + + EXPECT_CALL(*this, ServerCompleteCallback); + server_.ApplicationPacketReceived( + packet_.CopyAndSet(ReceivedIpPacket::kSrtpEncrypted)); + EXPECT_EQ(server_.state(), State::COMPLETE); +} + TEST_F(DtlsStunPiggybackControllerTest, FirstClientPacketLost) { // Client to server got lost (or arrives late) // Flight 1 @@ -242,11 +314,13 @@ TEST_F(DtlsStunPiggybackControllerTest, FirstClientPacketLost) { // Flight 4 SendServerToClientEmbedded(dtls_flight4, STUN_BINDING_REQUEST); + EXPECT_CALL(*this, ServerCompleteCallback(true)); SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE); EXPECT_EQ(server_.state(), State::COMPLETE); EXPECT_EQ(client_.state(), State::PENDING); // Post-handshake ACK + EXPECT_CALL(*this, ClientCompleteCallback(true)); SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST); EXPECT_EQ(client_.state(), State::COMPLETE); } @@ -256,6 +330,9 @@ TEST_F(DtlsStunPiggybackControllerTest, NotSupportedByServer) { // Flight 1 SendClientToServerEmbedded(dtls_flight1, STUN_BINDING_REQUEST); + // TODO: bugs.webrtc.org/367395350 - assert when calling the complete + // callback in this case which currently causes a sleuth of test failures. + // EXPECT_CALL(*this, ClientCompleteCallback()); SendServerToClientEmbedded(empty, STUN_BINDING_RESPONSE); EXPECT_EQ(client_.state(), State::OFF); } @@ -297,7 +374,9 @@ TEST_F(DtlsStunPiggybackControllerTest, SomeRequestsDoNotGoThrough) { EXPECT_EQ(client_.state(), State::PENDING); // Post-handshake ACK + EXPECT_CALL(*this, ServerCompleteCallback(true)); SendClientToServerEmbedded(empty, STUN_BINDING_REQUEST); + EXPECT_CALL(*this, ClientCompleteCallback(true)); SendServerToClientEmbedded(empty, STUN_BINDING_RESPONSE); EXPECT_EQ(server_.state(), State::COMPLETE); EXPECT_EQ(client_.state(), State::COMPLETE); @@ -317,7 +396,9 @@ TEST_F(DtlsStunPiggybackControllerTest, LossOnPostHandshakeAck) { EXPECT_EQ(client_.state(), State::PENDING); // Post-handshake ACK. Client to server gets lost + EXPECT_CALL(*this, ClientCompleteCallback(true)); SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST); + EXPECT_CALL(*this, ServerCompleteCallback(true)); SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE); EXPECT_EQ(server_.state(), State::COMPLETE); EXPECT_EQ(client_.state(), State::COMPLETE); @@ -364,7 +445,9 @@ TEST_F(DtlsStunPiggybackControllerTest, BasicHandshakeAckData) { })); // Post-handshake ACK + EXPECT_CALL(*this, ClientCompleteCallback); SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST); + EXPECT_CALL(*this, ServerCompleteCallback); SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE); EXPECT_EQ(server_.state(), State::COMPLETE); EXPECT_EQ(client_.state(), State::COMPLETE); @@ -401,7 +484,9 @@ TEST_F(DtlsStunPiggybackControllerTest, UnwrappedHandshakeAckData) { })); // Post-handshake ACK + EXPECT_CALL(*this, ClientCompleteCallback); SendServerToClientEmbedded(empty, STUN_BINDING_REQUEST); + EXPECT_CALL(*this, ServerCompleteCallback); SendClientToServerEmbedded(empty, STUN_BINDING_RESPONSE); EXPECT_EQ(server_.state(), State::COMPLETE); EXPECT_EQ(client_.state(), State::COMPLETE); @@ -501,6 +586,26 @@ TEST_F(DtlsStunPiggybackControllerTest, LimitAckSize) { })); } +TEST_F(DtlsStunPiggybackControllerTest, EmptyDataDoesNotClearAck) { + std::vector dtls_flight5 = FakeDtlsPacket(0x5487); + + server_.ReportDataPiggybacked( + WrapInStun(STUN_ATTR_META_DTLS_IN_STUN, dtls_flight1)->array_view(), + std::nullopt); + EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 1u); + + // The fact that we don't get any data does not mean that + // we can clear the ack list. + // a) packets can be arbitrary reordered. + // b) the peer might be needing 2 packets (ie. pqc) to produce + // a return packet and only one of them has arrived. + server_.ReportDataPiggybacked( + std::nullopt, + std::vector({ComputeDtlsPacketHash(dtls_flight1)})); + + EXPECT_EQ(server_.GetAckToPiggyback(STUN_BINDING_REQUEST)->size(), 1u); +} + TEST_F(DtlsStunPiggybackControllerTest, MultiPacketRoundRobin) { // Let's pretend that a flight is 3 packets... server_.CapturePacket(dtls_flight1); diff --git a/p2p/dtls/dtls_transport.cc b/p2p/dtls/dtls_transport.cc index 19950017924..1796ddfe770 100644 --- a/p2p/dtls/dtls_transport.cc +++ b/p2p/dtls/dtls_transport.cc @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/crypto/crypto_options.h" #include "api/dtls_transport_interface.h" #include "api/environment/environment.h" @@ -128,11 +128,16 @@ constexpr int kDisabledHandshakeTimeoutMs = 3600 * 1000 * 24; constexpr uint32_t kMaxCachedClientHello = 4; -static bool IsRtpPacket(ArrayView payload) { +static bool IsRtpPacket(std::span payload) { const uint8_t* u = payload.data(); return (payload.size() >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80); } +int ComputeRetransmissionTimeout(int rtt_ms) { + return std::max(kMinDtlsHandshakeTimeoutMs, + std::min(kMaxDtlsHandshakeTimeoutMs, 2 * (rtt_ms))); +} + StreamInterfaceChannel::StreamInterfaceChannel( IceTransportInternal* ice_transport) : ice_transport_(ice_transport), @@ -144,7 +149,7 @@ void StreamInterfaceChannel::SetDtlsStunPiggybackController( dtls_stun_piggyback_controller_ = dtls_stun_piggyback_controller; } -StreamResult StreamInterfaceChannel::Read(ArrayView buffer, +StreamResult StreamInterfaceChannel::Read(std::span buffer, size_t& read, int& /* error */) { RTC_DCHECK_RUN_ON(&callback_sequence_); @@ -172,22 +177,23 @@ void StreamInterfaceChannel::ClearNextPacketOptions() { next_packet_options_.reset(); } -StreamResult StreamInterfaceChannel::Write(ArrayView data, +StreamResult StreamInterfaceChannel::Write(std::span data, size_t& written, int& /* error */) { RTC_DCHECK_RUN_ON(&callback_sequence_); - // If we use DTLS-in-STUN, DTLS packets will be sent as part of STUN - // packets, they are captured by the controller. - if (dtls_stun_piggyback_controller_) { - dtls_stun_piggyback_controller_->CapturePacket(data); - } - AsyncSocketPacketOptions packet_options; if (next_packet_options_) { packet_options = std::move(*next_packet_options_); next_packet_options_.reset(); + } else if (dtls_stun_piggyback_controller_) { + // Note: packets with `next_packet_options_` are user packets. + // Packets without `next_packet_options_` are generated by BoringSSL. + // The DtlsStunPiggybackController captures BoringSSL packets. + RTC_DCHECK(IsDtlsPacket(data)); + dtls_stun_piggyback_controller_->CapturePacket(data); } + ice_transport_->SendPacket(reinterpret_cast(data.data()), data.size(), packet_options); written = data.size(); @@ -203,7 +209,7 @@ bool StreamInterfaceChannel::Flush() { return false; } -bool StreamInterfaceChannel::OnPacketReceived(ArrayView data) { +bool StreamInterfaceChannel::OnPacketReceived(std::span data) { RTC_DCHECK_RUN_ON(&callback_sequence_); if (packets_.size() > 0) { RTC_LOG(LS_WARNING) << "Packet already in queue."; @@ -254,14 +260,15 @@ DtlsTransportInternalImpl::DtlsTransportInternalImpl( crypto_options.ephemeral_key_exchange_cipher_groups.GetEnabled()), ssl_max_version_(max_version), dtls_stun_piggyback_controller_( - [this](ArrayView piggybacked_dtls_packet) { + [this](std::span piggybacked_dtls_packet) { if (piggybacked_dtls_callback_ == nullptr) { return; } piggybacked_dtls_callback_( this, ReceivedIpPacket(piggybacked_dtls_packet, SocketAddress())); - }) { + }, + [this](bool success) { CompleteDtlsInStun(success); }) { RTC_DCHECK(ice_transport_); ConnectToIceTransport(); dtls_in_stun_ = env_.field_trials().IsEnabled("WebRTC-IceHandshakeDtls"); @@ -281,8 +288,21 @@ DtlsTransportInternalImpl::DtlsTransportInternalImpl( ssl_stream_factory) {} DtlsTransportInternalImpl::~DtlsTransportInternalImpl() { + if (dtls_in_stun_) { + CompleteDtlsInStun(/*success=*/false); + } +} + +void DtlsTransportInternalImpl::CompleteDtlsInStun(bool success) { + RTC_LOG(LS_INFO) << "DTLS-STUN piggyback complete with success=" << success; + + dtls_in_stun_complete_ = true; + if (downward_) { + downward_->SetDtlsStunPiggybackController(nullptr); + } ice_transport()->ResetDtlsStunPiggybackCallbacks(); - ice_transport()->DeregisterReceivedPacketCallback(this); + + DeregisterReceivedPacketCallback(&dtls_stun_piggyback_controller_); } DtlsTransportState DtlsTransportInternalImpl::dtls_state() const { @@ -488,17 +508,30 @@ bool DtlsTransportInternalImpl::ExportSrtpKeyingMaterial( return dtls_ ? dtls_->ExportSrtpKeyingMaterial(keying_material) : false; } +bool DtlsTransportInternalImpl::AppendSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) { + return dtls_ ? dtls_->AppendSrtpKeyingMaterial(keying_material) : false; +} + bool DtlsTransportInternalImpl::SetupDtls() { RTC_DCHECK(dtls_role_); dtls_in_stun_ = ice_transport()->config().dtls_handshake_in_stun; + { auto downward = std::make_unique(ice_transport()); StreamInterfaceChannel* downward_ptr = downward.get(); - if (dtls_in_stun_) { + if (dtls_in_stun_ && !dtls_in_stun_complete_) { downward_ptr->SetDtlsStunPiggybackController( &dtls_stun_piggyback_controller_); + + RegisterReceivedPacketCallback( + &dtls_stun_piggyback_controller_, + [this](webrtc::PacketTransportInternal* transport, + const ReceivedIpPacket& packet) { + dtls_stun_piggyback_controller_.ApplicationPacketReceived(packet); + }); } if (ssl_stream_factory_) { dtls_ = ssl_stream_factory_( @@ -528,6 +561,12 @@ bool DtlsTransportInternalImpl::SetupDtls() { dtls_->SetMTU(kDtlsMtu); } + if (fake_ice_lite_) { + int rtt_ms = kDefaultHandshakeEstimateRttMs; + int initial_timeout_ms = ComputeRetransmissionTimeout(rtt_ms); + dtls_->SetInitialRetransmissionTimeout(initial_timeout_ms); + } + dtls_->SetIdentity(local_certificate_->identity()->Clone()); dtls_->SetMaxProtocolVersion(ssl_max_version_); dtls_->SetServerRole(*dtls_role_); @@ -616,7 +655,7 @@ int DtlsTransportInternalImpl::SendPacket( if (flags & PF_SRTP_BYPASS) { RTC_DCHECK(!srtp_ciphers_.empty()); if (!IsRtpPacket( - MakeArrayView(reinterpret_cast(data), size))) { + std::span(reinterpret_cast(data), size))) { return -1; } @@ -630,8 +669,8 @@ int DtlsTransportInternalImpl::SendPacket( // StreamInterfaceChannel::Write function. Such change would remove the // need of the next_packet_options_. StreamResult result = dtls_->Write( - MakeArrayView(reinterpret_cast(data), size), - written, error); + std::span(reinterpret_cast(data), size), written, + error); if (result != SR_SUCCESS) { // Explicitly clear the next packet options, in case no packet was // sent. @@ -736,7 +775,7 @@ void DtlsTransportInternalImpl::ConnectToIceTransport() { } return std::make_pair(data, ack); }, - [&](std::optional> data, + [&](std::optional> data, std::optional> acks) { if (!dtls_in_stun_) { return; @@ -960,7 +999,6 @@ void DtlsTransportInternalImpl::OnDtlsEvent(int sig, int err) { RTC_DCHECK(ret); dtls_stun_piggyback_controller_.SetDtlsHandshakeComplete( dtls_role_ == SSL_CLIENT, ssl_version_bytes == kDtls13VersionBytes); - downward_->SetDtlsStunPiggybackController(nullptr); set_dtls_state(DtlsTransportState::kConnected); set_writable(true); } @@ -978,16 +1016,16 @@ void DtlsTransportInternalImpl::OnDtlsEvent(int sig, int err) { // TODO(bugs.webrtc.org/15368): It should be possible to use information // from the original packet here to populate socket address and // timestamp. - NotifyPacketReceived( - ReceivedIpPacket(MakeArrayView(buf, read), SocketAddress(), - env_.clock().CurrentTime(), EcnMarking::kNotEct, - ReceivedIpPacket::kDtlsDecrypted)); + NotifyPacketReceived(ReceivedIpPacket( + std::span(buf, read), SocketAddress(), env_.clock().CurrentTime(), + EcnMarking::kNotEct, ReceivedIpPacket::kDtlsDecrypted)); } else if (ret == SR_EOS) { // Remote peer shut down the association with no error. RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote"; set_writable(false); set_dtls_state(DtlsTransportState::kClosed); NotifyOnClose(); + CompleteDtlsInStun(/*success=*/false); } else if (ret == SR_ERROR) { // Remote peer shut down the association with an error. RTC_LOG(LS_INFO) @@ -997,6 +1035,7 @@ void DtlsTransportInternalImpl::OnDtlsEvent(int sig, int err) { set_writable(false); set_dtls_state(DtlsTransportState::kFailed); NotifyOnClose(); + CompleteDtlsInStun(/*success=*/false); } } while (ret == SR_SUCCESS); } @@ -1069,7 +1108,7 @@ void DtlsTransportInternalImpl::MaybeStartDtls() { // Called from OnReadPacket when a DTLS packet is received. bool DtlsTransportInternalImpl::HandleDtlsPacket( - ArrayView payload) { + std::span payload) { // Pass to the StreamInterfaceChannel which ends up being passed to the DTLS // stack. return downward_->OnPacketReceived(payload); @@ -1168,11 +1207,6 @@ void DtlsTransportInternalImpl::OnDtlsHandshakeError(SSLHandshakeError error) { SendDtlsHandshakeError(error); } -int ComputeRetransmissionTimeout(int rtt_ms) { - return std::max(kMinDtlsHandshakeTimeoutMs, - std::min(kMaxDtlsHandshakeTimeoutMs, 2 * (rtt_ms))); -} - void DtlsTransportInternalImpl::ConfigureHandshakeTimeout() { RTC_DCHECK(dtls_); std::optional rtt_ms = ice_transport()->GetRttEstimate(); @@ -1200,8 +1234,19 @@ void DtlsTransportInternalImpl::ConfigureHandshakeTimeout() { void DtlsTransportInternalImpl::UpdateHandshakeTimeout() { RTC_DCHECK(dtls_); const auto rtt_ms = ice_transport()->GetRttEstimate(); - const int delay_ms = ComputeRetransmissionTimeout( + int delay_ms = ComputeRetransmissionTimeout( rtt_ms.value_or(kDefaultHandshakeEstimateRttMs)); + if (dtls_stun_piggyback_controller_.state() == + DtlsStunPiggybackController::State::OFF && + dtls_role_ == SSL_CLIENT) { + // We sent one STUN BINDING request with an embedded DTLS packet and + // discovered that peer does not support DtlsInStun. The DTLS packet will be + // sent by PeriodicRetransmitDtlsPacketUntilDtlsConnected and that will + // incur one more RTT. Increase slightly timeout to avoid unneeded DTLS + // retranmission. + delay_ms = (delay_ms * 133) / 100; + } + RTC_LOG(LS_INFO) << ToString() << ": Update DTLS handshake timeout to " << delay_ms << "ms based on ICE RTT " << (rtt_ms ? std::to_string(*rtt_ms) : ""); @@ -1261,6 +1306,13 @@ void DtlsTransportInternalImpl:: } } + if (dtls_stun_piggyback_controller_.state() == + DtlsStunPiggybackController::State::OFF) { + // Peer does not support DTLS in STUN. We have now retransmitted the packet + // once, and let DTLS handle further retransmits. + return; + } + const auto rtt_ms = ice_transport()->GetRttEstimate().value_or( kDefaultHandshakeEstimateRttMs); const int delay_ms = ComputeRetransmissionTimeout(rtt_ms); diff --git a/p2p/dtls/dtls_transport.h b/p2p/dtls/dtls_transport.h index 42447425489..9eed5169965 100644 --- a/p2p/dtls/dtls_transport.h +++ b/p2p/dtls/dtls_transport.h @@ -16,12 +16,12 @@ #include #include #include +#include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/crypto/crypto_options.h" #include "api/dtls_transport_interface.h" #include "api/environment/environment.h" @@ -76,7 +76,7 @@ class StreamInterfaceChannel : public StreamInterface { StreamInterfaceChannel& operator=(const StreamInterfaceChannel&) = delete; // Push in a packet; this gets pulled out from Read(). - bool OnPacketReceived(ArrayView data); + bool OnPacketReceived(std::span data); // Sets the options for the next packet to be written to ice_transport, // corresponding to the next Write() call. Safe since BoringSSL guarantees @@ -89,10 +89,10 @@ class StreamInterfaceChannel : public StreamInterface { // Implementations of StreamInterface StreamState GetState() const override; void Close() override; - StreamResult Read(ArrayView buffer, + StreamResult Read(std::span buffer, size_t& read, int& error) override; - StreamResult Write(ArrayView data, + StreamResult Write(std::span data, size_t& written, int& error) override; bool Flush() override; @@ -236,7 +236,9 @@ class DtlsTransportInternalImpl : public DtlsTransportInternal { // Once DTLS has established (i.e., this ice_transport is writable), this // method extracts the keys negotiated during the DTLS handshake, for use in // external encryption. DTLS-SRTP uses this to extract the needed SRTP keys. - bool ExportSrtpKeyingMaterial( + [[deprecated]] bool ExportSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) override; + bool AppendSrtpKeyingMaterial( ZeroOnFreeBuffer& keying_material) override; IceTransportInternal* ice_transport() override; @@ -274,6 +276,7 @@ class DtlsTransportInternalImpl : public DtlsTransportInternal { // Two methods for testing. bool IsDtlsPiggybackSupportedByPeer(); bool WasDtlsCompletedByPiggybacking(); + void SetFakeIceLite() { fake_ice_lite_ = true; } private: void ConnectToIceTransport(); @@ -290,7 +293,7 @@ class DtlsTransportInternalImpl : public DtlsTransportInternal { void OnNetworkRouteChanged(std::optional network_route); bool SetupDtls(); void MaybeStartDtls(); - bool HandleDtlsPacket(ArrayView payload); + bool HandleDtlsPacket(std::span payload); void OnDtlsHandshakeError(SSLHandshakeError error); void ConfigureHandshakeTimeout(); void UpdateHandshakeTimeout(); @@ -299,6 +302,8 @@ class DtlsTransportInternalImpl : public DtlsTransportInternal { void set_writable(bool writable); // Sets the DTLS state, signaling if necessary. void set_dtls_state(DtlsTransportState state); + + void CompleteDtlsInStun(bool success); void SetPiggybackDtlsDataCallback( absl::AnyInvocable callback); @@ -352,6 +357,9 @@ class DtlsTransportInternalImpl : public DtlsTransportInternal { // (so that we return PIGGYBACK_ACK to client if we get STUN_BINDING_REQUEST // directly). Maybe disabled in SetupDtls has been called. bool dtls_in_stun_ = false; + // Has DtlsInStun Complete been run? + // This variable is used to prevent reinitializing after dtls-restart. + bool dtls_in_stun_complete_ = false; // A controller for piggybacking DTLS in STUN. DtlsStunPiggybackController dtls_stun_piggyback_controller_; @@ -365,6 +373,9 @@ class DtlsTransportInternalImpl : public DtlsTransportInternal { // DtlsTransportInternalImpl has a "hack" to periodically retransmit. bool pending_periodic_retransmit_dtls_packet_ = false; ScopedTaskSafetyDetached safety_flag_; + + // We reuse this class also in tests that pretend to be ice-lite. + bool fake_ice_lite_ = false; }; } // namespace webrtc diff --git a/p2p/dtls/dtls_transport_internal.h b/p2p/dtls/dtls_transport_internal.h index bb5c862e34c..a6fa9f7119a 100644 --- a/p2p/dtls/dtls_transport_internal.h +++ b/p2p/dtls/dtls_transport_internal.h @@ -25,6 +25,7 @@ #include "p2p/base/packet_transport_internal.h" #include "rtc_base/buffer.h" #include "rtc_base/callback_list.h" +#include "rtc_base/checks.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" @@ -87,9 +88,16 @@ class DtlsTransportInternal : public PacketTransportInternal { virtual std::unique_ptr GetRemoteSSLCertChain() const = 0; // Allows key material to be extracted for external encryption. - virtual bool ExportSrtpKeyingMaterial( + virtual bool AppendSrtpKeyingMaterial( ZeroOnFreeBuffer& keying_material) = 0; + // Older API. If not overridden in subclasses, will fail. + [[deprecated]] virtual bool ExportSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) { + RTC_DCHECK_NOTREACHED() << "Superseded by AppendSrtpKeyingMaterial"; + return false; + } + // Set DTLS remote fingerprint and role. Must be after local identity set. virtual RTCError SetRemoteParameters(absl::string_view digest_alg, const uint8_t* digest, diff --git a/p2p/dtls/dtls_transport_unittest.cc b/p2p/dtls/dtls_transport_unittest.cc index 4c7c6e10df7..3d3dd6a9ab1 100644 --- a/p2p/dtls/dtls_transport_unittest.cc +++ b/p2p/dtls/dtls_transport_unittest.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/crypto/crypto_options.h" #include "api/dtls_transport_interface.h" #include "api/field_trials.h" @@ -57,11 +57,11 @@ #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/stream.h" -#include "rtc_base/thread.h" #include "system_wrappers/include/metrics.h" #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -218,6 +218,10 @@ class DtlsTestClient { return received_dtls_server_hellos_; } + int received_dtls_ciphertext_packets() const { + return received_dtls_ciphertext_packets_; + } + std::optional GetVersionBytes() { int value; if (dtls_transport_->GetSslVersionBytes(&value)) { @@ -273,7 +277,10 @@ class DtlsTestClient { // against, and make sure that it doesn't look like DTLS. memset(packet.get(), sent & 0xff, size); packet[0] = (srtp) ? kRtpLeadByte : 0x00; - SetBE32(packet.get() + kPacketNumOffset, static_cast(sent)); + SetBE32( + std::span( + reinterpret_cast(packet.get() + kPacketNumOffset), 4), + static_cast(sent)); // Only set the bypass flag if we've activated DTLS. int flags = (certificate_ && srtp) ? PF_SRTP_BYPASS : 0; @@ -305,14 +312,15 @@ class DtlsTestClient { size_t NumPacketsReceived() { return received_.size(); } // Inverse of SendPackets. - bool VerifyPacket(ArrayView payload, uint32_t* out_num) { + bool VerifyPacket(std::span payload, uint32_t* out_num) { const uint8_t* data = payload.data(); size_t size = payload.size(); if (size != packet_size_ || (data[0] != 0 && (data[0]) != 0x80)) { return false; } - uint32_t packet_num = GetBE32(data + kPacketNumOffset); + uint32_t packet_num = + GetBE32(std::span(data + kPacketNumOffset, 4)); for (size_t i = kPacketHeaderLen; i < size; ++i) { if (data[i] != (packet_num & 0xff)) { return false; @@ -329,7 +337,8 @@ class DtlsTestClient { if (size <= packet_size_) { return false; } - uint32_t packet_num = GetBE32(data + kPacketNumOffset); + uint32_t packet_num = + GetBE32(std::span(data + kPacketNumOffset, 4)); int num_matches = 0; for (size_t i = kPacketNumOffset; i < size; ++i) { if (data[i] == (packet_num & 0xff)) { @@ -376,6 +385,11 @@ class DtlsTestClient { SentPacketInfo sent_packet() const { return sent_packet_; } + bool IsDtlsCiphertextPacket(std::span payload) { + return IsDtlsPacket(payload) && + (payload.data()[0] > 31 && payload.data()[0] < 64); + } + // Hook into the raw packet stream to make sure DTLS packets are encrypted. void OnFakeIceTransportReadPacket(PacketTransportInternal* /* transport */, const ReceivedIpPacket& packet) { @@ -391,6 +405,8 @@ class DtlsTestClient { } else if (data[13] == 2) { ++received_dtls_server_hellos_; } + } else if (IsDtlsCiphertextPacket(packet.payload())) { + ++received_dtls_ciphertext_packets_; } else if (data[0] == 26) { RTC_LOG(LS_INFO) << "Found DTLS ACK"; } else if (dtls_transport_->IsDtlsActive()) { @@ -414,6 +430,7 @@ class DtlsTestClient { SSLProtocolVersion ssl_max_version_ = SSL_PROTOCOL_DTLS_12; int received_dtls_client_hellos_ = 0; int received_dtls_server_hellos_ = 0; + int received_dtls_ciphertext_packets_ = 0; SentPacketInfo sent_packet_; absl::AnyInvocable writable_func_; int async_delay_ms_ = 100; @@ -460,7 +477,7 @@ class FakeSSLStreamAdapter : public SSLStreamAdapter { int StartSSL() override { return impl_->StartSSL(); } SSLPeerCertificateDigestError SetPeerCertificateDigest( absl::string_view digest_alg, - ArrayView digest_val) override { + std::span digest_val) override { return impl_->SetPeerCertificateDigest(digest_alg, digest_val); } std::unique_ptr GetPeerSSLCertChain() const override { @@ -475,10 +492,14 @@ class FakeSSLStreamAdapter : public SSLStreamAdapter { bool GetSslVersionBytes(int* version) const override { return impl_->GetSslVersionBytes(version); } - bool ExportSrtpKeyingMaterial( + [[deprecated]] bool ExportSrtpKeyingMaterial( ZeroOnFreeBuffer& keying_material) override { return impl_->ExportSrtpKeyingMaterial(keying_material); } + bool AppendSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) override { + return impl_->AppendSrtpKeyingMaterial(keying_material); + } uint16_t GetPeerSignatureAlgorithm() const override { return impl_->GetPeerSignatureAlgorithm(); } @@ -500,12 +521,12 @@ class FakeSSLStreamAdapter : public SSLStreamAdapter { // StreamInterface overrides. StreamState GetState() const override { return impl_->GetState(); } void Close() override { impl_->Close(); } - StreamResult Read(ArrayView buffer, + StreamResult Read(std::span buffer, size_t& read, int& error) override { return impl_->Read(buffer, read, error); } - StreamResult Write(ArrayView data, + StreamResult Write(std::span data, size_t& written, int& error) override { if (write_error_) { @@ -623,13 +644,17 @@ class DtlsTransportInternalImplTestBase { IsRtcOk()); } + int client1_recv_packets = 0; + int client2_recv_packets = 0; void AddPacketLogging() { client1_.fake_ice_transport()->set_packet_recv_filter( [&](auto packet, auto timestamp_us) { + client1_recv_packets++; return LogRecv(client1_.name(), packet); }); client2_.fake_ice_transport()->set_packet_recv_filter( [&](auto packet, auto timestamp_us) { + client2_recv_packets++; return LogRecv(client2_.name(), packet); }); client1_.set_writable_callback([&]() {}); @@ -677,6 +702,23 @@ class DtlsTransportInternalImplTestBase { return drop; } + bool LogBlock(absl::string_view name, + bool block, + const char* data, + size_t len) { + auto timestamp_ms = (fake_clock_.TimeNanos() - start_time_ns_) / 1000000; + if (block) { + RTC_LOG(LS_INFO) << "time=" << timestamp_ms << " : " << name + << ": blocking packet len=" << len + << ", data[0]: " << static_cast(data[0]); + } else { + RTC_LOG(LS_INFO) << "time=" << timestamp_ms << " : " << name + << ": SendPacket, len=" << len + << ", data[0]: " << static_cast(data[0]); + } + return block; + } + template bool WaitUntil(Fn func) { return webrtc::WaitUntil( @@ -686,7 +728,7 @@ class DtlsTransportInternalImplTestBase { } protected: - AutoThread main_thread_; + test::RunLoop main_thread_; ScopedFakeClock fake_clock_; DtlsTestClient client1_; DtlsTestClient client2_; @@ -726,7 +768,9 @@ TEST_F(DtlsTransportInternalImplTest, TestSendPacketWithOptions) { std::unique_ptr packet(new char[size]); memset(packet.get(), 0, size); packet[0] = 0x00; - SetBE32(packet.get() + kPacketNumOffset, 0); + SetBE32(std::span( + reinterpret_cast(packet.get() + kPacketNumOffset), 4), + 0); AsyncSocketPacketOptions packet_options; packet_options.packet_id = kFakePacketId; @@ -756,7 +800,9 @@ TEST_F(DtlsTransportInternalImplTest, TestSendSrtpBypassPacketWithOptions) { std::unique_ptr packet(new char[size]); memset(packet.get(), 0, size); packet[0] = kRtpLeadByte; // Make it look like an SRTP packet. - SetBE32(packet.get() + kPacketNumOffset, 0); + SetBE32(std::span( + reinterpret_cast(packet.get() + kPacketNumOffset), 4), + 0); AsyncSocketPacketOptions packet_options; packet_options.packet_id = kFakePacketId; @@ -866,19 +912,10 @@ TEST_F(DtlsTransportInternalImplTest, KeyingMaterialExporter) { PrepareDtls(KT_DEFAULT); ASSERT_TRUE(Connect()); - int crypto_suite; - EXPECT_TRUE(client1_.dtls_transport()->GetSrtpCryptoSuite(&crypto_suite)); - int key_len; - int salt_len; - EXPECT_TRUE(GetSrtpKeyAndSaltLengths(crypto_suite, &key_len, &salt_len)); - ZeroOnFreeBuffer client1_out = - ZeroOnFreeBuffer::CreateUninitializedWithSize( - 2 * (key_len + salt_len)); - ZeroOnFreeBuffer client2_out = - ZeroOnFreeBuffer::CreateUninitializedWithSize( - 2 * (key_len + salt_len)); - EXPECT_TRUE(client1_.dtls_transport()->ExportSrtpKeyingMaterial(client1_out)); - EXPECT_TRUE(client2_.dtls_transport()->ExportSrtpKeyingMaterial(client2_out)); + ZeroOnFreeBuffer client1_out; + ZeroOnFreeBuffer client2_out; + EXPECT_TRUE(client1_.dtls_transport()->AppendSrtpKeyingMaterial(client1_out)); + EXPECT_TRUE(client2_.dtls_transport()->AppendSrtpKeyingMaterial(client2_out)); EXPECT_EQ(client1_out, client2_out); } @@ -1731,7 +1768,8 @@ class DtlsEventOrderingTest int count = pqc ? 2 : 1; // Check that no hello needed to be retransmitted. EXPECT_EQ(count, client1_.received_dtls_client_hellos()); - EXPECT_EQ(1, client2_.received_dtls_server_hellos()); + EXPECT_LE(count, client2_.received_dtls_server_hellos() + + client2_.received_dtls_ciphertext_packets()); if (valid_fingerprint) { TestTransfer(1000, 100, false); @@ -1781,6 +1819,26 @@ class DtlsTransportInternalImplDtlsInStunTest : public DtlsTransportInternalImplVersionTest { public: DtlsTransportInternalImplDtlsInStunTest() {} + + void CheckRetransmissions() { + EXPECT_EQ(client1_.dtls_transport()->GetRetransmissionCount(), 0); + if (std::get<0>(GetParam()).dtls_in_stun == + std::get<1>(GetParam()).dtls_in_stun) { + EXPECT_EQ(client2_.dtls_transport()->GetRetransmissionCount(), 0); + return; + } + if (std::get<0>(GetParam()).ssl_role == SSL_CLIENT) { + EXPECT_EQ(client2_.dtls_transport()->GetRetransmissionCount(), 0); + return; + } + + // If peers does not have same setting for DTLS in STUN + // current implementation does sometimes introduce a DTLS + // retransmit on client2 (activing as SSL_CLIENT). + // + // TODO: bugs.webrtc.org/367395350 - It would be nice to remove these! + EXPECT_LE(client2_.dtls_transport()->GetRetransmissionCount(), 1); + } }; std::vector> AllEndpointVariants() { @@ -1866,9 +1924,7 @@ TEST_P(DtlsTransportInternalImplDtlsInStunTest, Handshake1) { EXPECT_TRUE(client1_.dtls_transport()->writable()); EXPECT_TRUE(client2_.dtls_transport()->writable()); - EXPECT_EQ(client1_.dtls_transport()->GetRetransmissionCount(), 0); - EXPECT_EQ(client2_.dtls_transport()->GetRetransmissionCount(), 0); - + CheckRetransmissions(); ClearPacketFilters(); } @@ -1917,9 +1973,7 @@ TEST_P(DtlsTransportInternalImplDtlsInStunTest, Handshake2) { EXPECT_TRUE(client1_.dtls_transport()->writable()); EXPECT_TRUE(client2_.dtls_transport()->writable()); - EXPECT_EQ(client1_.dtls_transport()->GetRetransmissionCount(), 0); - EXPECT_EQ(client2_.dtls_transport()->GetRetransmissionCount(), 0); - + CheckRetransmissions(); ClearPacketFilters(); } @@ -1974,9 +2028,7 @@ TEST_P(DtlsTransportInternalImplDtlsInStunTest, PartiallyPiggybacked) { EXPECT_TRUE(client1_.dtls_transport()->writable()); EXPECT_TRUE(client2_.dtls_transport()->writable()); - EXPECT_EQ(client1_.dtls_transport()->GetRetransmissionCount(), 0); - EXPECT_EQ(client2_.dtls_transport()->GetRetransmissionCount(), 0); - + CheckRetransmissions(); ClearPacketFilters(); } @@ -2176,10 +2228,237 @@ TEST_P(DtlsInStunTest, OptimalDtls13Handshake) { })); EXPECT_TRUE(client2_.dtls_transport()->writable()); - EXPECT_EQ(client1_.dtls_transport()->GetRetransmissionCount(), 0); - EXPECT_EQ(client2_.dtls_transport()->GetRetransmissionCount(), 0); + CheckRetransmissions(); + ClearPacketFilters(); +} + +EndpointConfig Vanilla12(bool client1) { + return { + .max_protocol_version = SSL_PROTOCOL_DTLS_12, + .dtls_in_stun = false, + .ice_role = client1 ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED, + .ssl_role = client1 ? SSL_CLIENT : SSL_SERVER, + .pqc = false, + }; +} + +EndpointConfig Vanilla13(bool client1) { + return { + .max_protocol_version = SSL_PROTOCOL_DTLS_13, + .dtls_in_stun = false, + .ice_role = client1 ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED, + .ssl_role = client1 ? SSL_CLIENT : SSL_SERVER, + .pqc = false, + }; +} + +struct DtlsTransportLastHandshakePacketTest + : public DtlsTransportInternalImplVersionTest { + int client1_packet_num = 0; + int client2_packet_num = 0; + + int client1_packet_to_block = -1; + int client1_packet_size = 0; + std::unique_ptr client1_packet = nullptr; + + int client2_packet_to_block = -1; + int client2_packet_size = 0; + std::unique_ptr client2_packet = nullptr; + + void InstallBlock() { + AddPacketLogging(); + client1_.fake_ice_transport()->set_packet_send_filter( + [&](auto data, auto len, auto options, auto flags) { + // Always allow STUN. + bool stun = data[0] == 0 || data[0] == 1; + + if (stun) { + return false; + } + if (client1_packet_num++ != client1_packet_to_block) { + return LogBlock(client1_.name(), /* block=*/false, data, len); + } + client1_packet_size = len; + client1_packet = std::unique_ptr(new char[len]); + memcpy(client1_packet.get(), data, len); + return LogBlock(client1_.name(), /* block=*/true, data, len); + }); + + client2_.fake_ice_transport()->set_packet_send_filter( + [&](auto data, auto len, auto options, auto flags) { + // Always allow STUN. + bool stun = data[0] == 0 || data[0] == 1; + + if (stun) { + return false; + } + if (client2_packet_num++ != client2_packet_to_block) { + return LogBlock(client2_.name(), /* block=*/false, data, len); + return false; + } + client2_packet_size = len; + client2_packet = std::unique_ptr(new char[len]); + memcpy(client2_packet.get(), data, len); + return LogBlock(client2_.name(), /* block=*/true, data, len); + }); + } + + void resend_blocked() { + int flags = 0; + AsyncSocketPacketOptions packet_options; + if (client1_packet != nullptr) { + int size = client1_packet_size; + RTC_LOG(LS_INFO) << "client1 => client2: RESEND " + << client1_packet.get()[0] << " len= " << size; + int rv = client1_.fake_ice_transport()->SendPacket( + client1_packet.get(), size, packet_options, flags); + ASSERT_EQ(size, rv); + client1_packet = nullptr; + } + if (client2_packet != nullptr) { + int size = client2_packet_size; + RTC_LOG(LS_INFO) << "client2 => client1: RESEND " + << client2_packet.get()[0] << " len= " << size; + int rv = client2_.fake_ice_transport()->SendPacket( + client2_packet.get(), size, packet_options, flags); + ASSERT_EQ(size, rv); + client2_packet = nullptr; + } + } +}; + +TEST_P(DtlsTransportLastHandshakePacketTest, + VerifyThatDtls12ClientCanNotDecodeBeforeBecomingWritable) { + if (!SSLStreamAdapter::IsBoringSsl()) { + GTEST_SKIP() << "Needs boringssl."; + } + + if (std::get<0>(GetParam()).max_protocol_version != SSL_PROTOCOL_DTLS_12) { + GTEST_SKIP() << "This is a DTLS 1.2 test."; + } + + Prepare(false); + EXPECT_TRUE(client1_.ConnectIceTransport(&client2_)); + + // Block the last handshake message server to client. + client2_packet_to_block = 1; + InstallBlock(); + + client1_.SendIcePing(); + client2_.SendIcePing(); + ASSERT_TRUE(WaitUntil([&] { + return client1_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_REQUEST) == 1 && + client2_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_REQUEST) == 1; + })); + client1_.SendIcePingConf(); + client2_.SendIcePingConf(); + ASSERT_TRUE(WaitUntil([&] { + return client1_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_RESPONSE) == 1 && + client2_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_RESPONSE) == 1; + })); + + ASSERT_TRUE(WaitUntil([&] { return client2_packet != nullptr; })); + + // Now everything except the last message from server to client is exchanged. + ASSERT_FALSE(client1_.dtls_transport()->writable()); + ASSERT_TRUE(client2_.dtls_transport()->writable()); + + // Verify that messages sent from client2 can NOT be decoded at client1. + int size = 100; + int count = 1; + client1_recv_packets = 0; + client1_.ExpectPackets(size); + client2_.SendPackets(size, count, /* srtp= */ false); + ASSERT_TRUE(WaitUntil([&] { return client1_recv_packets == 1; })); + // Last received packet was not decodable! + ASSERT_EQ(client1_.NumPacketsReceived(), 0u); + + // Resend the blocked packet. + client1_recv_packets = 0; + resend_blocked(); + ASSERT_TRUE(WaitUntil([&] { return client1_recv_packets == 1; })); + ASSERT_TRUE(client1_.dtls_transport()->writable()); ClearPacketFilters(); } + +INSTANTIATE_TEST_SUITE_P(DtlsTransportLastHandshakePacket12Test, + DtlsTransportLastHandshakePacketTest, + ::testing::Values(std::make_tuple(Vanilla12(true), + Vanilla12(false)))); + +TEST_P(DtlsTransportLastHandshakePacketTest, + VerifyThatDtls13ServerCanNotDecodeBeforeBecomingWritable) { + if (!SSLStreamAdapter::IsBoringSsl()) { + GTEST_SKIP() << "Needs boringssl."; + } + + if (std::get<0>(GetParam()).max_protocol_version != SSL_PROTOCOL_DTLS_13) { + GTEST_SKIP() << "This is a DTLS 1.3 test."; + } + + Prepare(false); + EXPECT_TRUE(client1_.ConnectIceTransport(&client2_)); + + // Block the ACK from client to server. + client1_packet_to_block = 1; + InstallBlock(); + + client1_.SendIcePing(); + client2_.SendIcePing(); + ASSERT_TRUE(WaitUntil([&] { + return client1_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_REQUEST) == 1 && + client2_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_REQUEST) == 1; + })); + client1_.SendIcePingConf(); + client2_.SendIcePingConf(); + ASSERT_TRUE(WaitUntil([&] { + return client1_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_RESPONSE) == 1 && + client2_.fake_ice_transport()->GetCountOfReceivedStunMessages( + STUN_BINDING_RESPONSE) == 1; + })); + + ASSERT_TRUE(WaitUntil([&] { return client1_packet != nullptr; })); + + // Now everything except the last message from client to server is exchanged. + ASSERT_TRUE(client1_.dtls_transport()->writable()); + ASSERT_FALSE(client2_.dtls_transport()->writable()); + + // Verify that messages sent from client1 can NOT be decoded at server. + int size = 100; + int count = 1; + client2_recv_packets = 0; + client2_.ExpectPackets(size); + client1_.SendPackets(size, count, /* srtp= */ false); + ASSERT_TRUE(WaitUntil([&] { return client2_recv_packets == 1; })); + + // Last received packet was NOT decodable! + ASSERT_EQ(client2_.NumPacketsReceived(), 0u); + + // Resend the blocked packet. + client2_recv_packets = 0; + resend_blocked(); + ASSERT_TRUE(WaitUntil([&] { return client2_recv_packets == 1; })); + ASSERT_TRUE(client2_.dtls_transport()->writable()); + + client2_recv_packets = 0; + client1_.SendPackets(size, count, /* srtp= */ false); + ASSERT_TRUE(WaitUntil([&] { return client2_recv_packets == 1; })); + ASSERT_EQ(client2_.NumPacketsReceived(), 1u); + ClearPacketFilters(); +} + +INSTANTIATE_TEST_SUITE_P(DtlsTransportLastHandshakePacket13Test, + DtlsTransportLastHandshakePacketTest, + ::testing::Values(std::make_tuple(Vanilla13(true), + Vanilla13(false)))); + } // namespace } // namespace webrtc diff --git a/p2p/dtls/dtls_utils.cc b/p2p/dtls/dtls_utils.cc index ff07f57a376..87d188fc1d7 100644 --- a/p2p/dtls/dtls_utils.cc +++ b/p2p/dtls/dtls_utils.cc @@ -10,16 +10,19 @@ #include "p2p/dtls/dtls_utils.h" +#include #include #include +#include #include #include "absl/container/flat_hash_set.h" -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" #include "rtc_base/crc32.h" +namespace webrtc { + namespace { // https://datatracker.ietf.org/doc/html/rfc5246#appendix-A.1 const uint8_t kDtlsChangeCipherSpecRecord = 20; @@ -27,14 +30,12 @@ const uint8_t kDtlsHandshakeRecord = 22; } // namespace -namespace webrtc { - -bool IsDtlsPacket(ArrayView payload) { +bool IsDtlsPacket(std::span payload) { const uint8_t* u = payload.data(); return (payload.size() >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64)); } -bool IsDtlsClientHelloPacket(ArrayView payload) { +bool IsDtlsClientHelloPacket(std::span payload) { if (!IsDtlsPacket(payload)) { return false; } @@ -42,7 +43,7 @@ bool IsDtlsClientHelloPacket(ArrayView payload) { return payload.size() > 17 && u[0] == kDtlsHandshakeRecord && u[13] == 1; } -bool IsDtlsHandshakePacket(ArrayView payload) { +bool IsDtlsHandshakePacket(std::span payload) { if (!IsDtlsPacket(payload)) { return false; } @@ -54,11 +55,11 @@ bool IsDtlsHandshakePacket(ArrayView payload) { payload[0] == kDtlsChangeCipherSpecRecord); } -uint32_t ComputeDtlsPacketHash(ArrayView dtls_packet) { +uint32_t ComputeDtlsPacketHash(std::span dtls_packet) { return ComputeCrc32(dtls_packet.data(), dtls_packet.size()); } -bool PacketStash::AddIfUnique(ArrayView packet) { +bool PacketStash::AddIfUnique(std::span packet) { uint32_t h = ComputeDtlsPacketHash(packet); for (const auto& [hash, p] : packets_) { if (h == hash) { @@ -71,15 +72,15 @@ bool PacketStash::AddIfUnique(ArrayView packet) { return true; } -void PacketStash::Add(ArrayView packet) { +void PacketStash::Add(std::span packet) { packets_.push_back( {.hash = ComputeDtlsPacketHash(packet), .buffer = std::make_unique(packet.data(), packet.size())}); } -void PacketStash::Prune(const absl::flat_hash_set& hashes) { +size_t PacketStash::Prune(const absl::flat_hash_set& hashes) { if (hashes.empty()) { - return; + return 0; } uint32_t before = packets_.size(); std::erase_if(packets_, @@ -89,6 +90,10 @@ void PacketStash::Prune(const absl::flat_hash_set& hashes) { if (pos_ >= removed) { pos_ -= removed; } + if (pos_ >= packets_.size()) { + pos_ = packets_.empty() ? 0 : packets_.size() - 1; + } + return removed; } void PacketStash::Prune(uint32_t max_size) { @@ -105,20 +110,20 @@ void PacketStash::Prune(uint32_t max_size) { } } -ArrayView PacketStash::GetNext() { +std::span PacketStash::GetNext() { RTC_DCHECK(!packets_.empty()); auto pos = pos_; pos_ = (pos + 1) % packets_.size(); const auto& buffer = packets_[pos].buffer; - return ArrayView(buffer->data(), buffer->size()); + return std::span(buffer->data(), buffer->size()); } -std::vector> PacketStash::GetAll() const { - std::vector> ret; +std::vector> PacketStash::GetAll() const { + std::vector> ret; ret.reserve(packets_.size()); for (const auto& buffer : packets_) { const uint8_t* ptr = buffer.buffer->data(); - ret.push_back(ArrayView(ptr, buffer.buffer->size())); + ret.push_back(std::span(ptr, buffer.buffer->size())); } return ret; } diff --git a/p2p/dtls/dtls_utils.h b/p2p/dtls/dtls_utils.h index c433d28f1cf..e2ed5dc33b1 100644 --- a/p2p/dtls/dtls_utils.h +++ b/p2p/dtls/dtls_utils.h @@ -14,10 +14,10 @@ #include #include #include +#include #include #include "absl/container/flat_hash_set.h" -#include "api/array_view.h" #include "rtc_base/buffer.h" namespace webrtc { @@ -25,22 +25,23 @@ namespace webrtc { const size_t kDtlsRecordHeaderLen = 13; const size_t kMaxDtlsPacketLen = 2048; -bool IsDtlsPacket(ArrayView payload); -bool IsDtlsClientHelloPacket(ArrayView payload); -bool IsDtlsHandshakePacket(ArrayView payload); +bool IsDtlsPacket(std::span payload); +bool IsDtlsClientHelloPacket(std::span payload); +bool IsDtlsHandshakePacket(std::span payload); -uint32_t ComputeDtlsPacketHash(ArrayView dtls_packet); +uint32_t ComputeDtlsPacketHash(std::span dtls_packet); class PacketStash { public: PacketStash() {} - void Add(ArrayView packet); - bool AddIfUnique(ArrayView packet); - void Prune(const absl::flat_hash_set& packet_hashes); + void Add(std::span packet); + bool AddIfUnique(std::span packet); + // Returns number of elements that were removed. + size_t Prune(const absl::flat_hash_set& packet_hashes); void Prune(uint32_t max_size); - ArrayView GetNext(); - std::vector> GetAll() const; + std::span GetNext(); + std::vector> GetAll() const; void clear() { packets_.clear(); @@ -49,7 +50,7 @@ class PacketStash { bool empty() const { return packets_.empty(); } int size() const { return packets_.size(); } - static uint32_t Hash(ArrayView packet) { + static uint32_t Hash(std::span packet) { return ComputeDtlsPacketHash(packet); } diff --git a/p2p/dtls/dtls_utils_unittest.cc b/p2p/dtls/dtls_utils_unittest.cc index 15d4739d4e2..8c3c771315f 100644 --- a/p2p/dtls/dtls_utils_unittest.cc +++ b/p2p/dtls/dtls_utils_unittest.cc @@ -11,15 +11,15 @@ #include "p2p/dtls/dtls_utils.h" #include +#include #include #include "absl/container/flat_hash_set.h" -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { -std::vector ToVector(ArrayView array) { +std::vector ToVector(std::span array) { return std::vector(array.begin(), array.end()); } @@ -156,4 +156,29 @@ TEST(PacketStash, PruneSize) { EXPECT_EQ(ToVector(stash.GetNext()), packet6); } +TEST(PacketStash, PruneSome) { + PacketStash stash; + std::vector packet1 = {1}; + std::vector packet2 = {2}; + std::vector packet3 = {3}; + + stash.AddIfUnique(packet1); + stash.AddIfUnique(packet2); + stash.AddIfUnique(packet3); + EXPECT_EQ(stash.size(), 3); + + EXPECT_EQ(ToVector(stash.GetNext()), packet1); + EXPECT_EQ(ToVector(stash.GetNext()), packet2); + EXPECT_EQ(ToVector(stash.GetNext()), packet3); + EXPECT_EQ(ToVector(stash.GetNext()), packet1); + + absl::flat_hash_set remove; + remove.insert(PacketStash::Hash(packet1)); + remove.insert(PacketStash::Hash(packet2)); + EXPECT_EQ(stash.Prune(remove), 2u); + EXPECT_EQ(stash.Prune(remove), 0u); + + EXPECT_EQ(ToVector(stash.GetNext()), packet3); +} + } // namespace webrtc diff --git a/p2p/dtls/fake_dtls_transport.h b/p2p/dtls/fake_dtls_transport.h index f8de40dd9d3..0beb398cc3c 100644 --- a/p2p/dtls/fake_dtls_transport.h +++ b/p2p/dtls/fake_dtls_transport.h @@ -15,11 +15,12 @@ #include #include #include +#include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/dtls_transport_interface.h" #include "api/ice_transport_interface.h" #include "api/rtc_error.h" @@ -55,7 +56,7 @@ class FakeDtlsTransport : public DtlsTransportInternal { ice_transport_ref_->internal())), transport_name_(ice_transport_->transport_name()), component_(ice_transport_->component()), - dtls_fingerprint_("", nullptr) { + dtls_fingerprint_("", std::span()) { RTC_DCHECK(ice_transport_); ice_transport_->RegisterReceivedPacketCallback( this, [&](PacketTransportInternal* transport, @@ -71,7 +72,7 @@ class FakeDtlsTransport : public DtlsTransportInternal { : owned_ice_transport_(std::move(ice)), transport_name_(owned_ice_transport_->transport_name()), component_(owned_ice_transport_->component()), - dtls_fingerprint_("", ArrayView()) { + dtls_fingerprint_("", std::span()) { ice_transport_ = owned_ice_transport_.get(); ice_transport_->RegisterReceivedPacketCallback( this, [&](PacketTransportInternal* transport, @@ -186,7 +187,7 @@ class FakeDtlsTransport : public DtlsTransportInternal { bool SetRemoteFingerprint(absl::string_view alg, const uint8_t* digest, size_t digest_len) { - dtls_fingerprint_ = SSLFingerprint(alg, MakeArrayView(digest, digest_len)); + dtls_fingerprint_ = SSLFingerprint(alg, std::span(digest, digest_len)); return true; } bool SetDtlsRole(SSLRole role) override { @@ -255,6 +256,24 @@ class FakeDtlsTransport : public DtlsTransportInternal { } return do_dtls_; } + bool AppendSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) override { + if (do_dtls_) { + int crypto_suite; + if (!GetSrtpCryptoSuite(&crypto_suite)) { + return false; + } + int key_len; + int salt_len; + if (!GetSrtpKeyAndSaltLengths(crypto_suite, &key_len, &salt_len)) { + return false; + } + size_t data_size = 2 * key_len + 2 * salt_len; + std::vector data(data_size, 0xff); + keying_material.AppendData(data); + } + return do_dtls_; + } void set_ssl_max_protocol_version(SSLProtocolVersion version) { ssl_max_version_ = version; } diff --git a/p2p/test/fake_ice_transport.h b/p2p/test/fake_ice_transport.h index f6cd168a595..864bd401313 100644 --- a/p2p/test/fake_ice_transport.h +++ b/p2p/test/fake_ice_transport.h @@ -16,13 +16,13 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/field_trials.h" #include "api/ice_transport_interface.h" @@ -574,7 +574,7 @@ class FakeIceTransportInternal : public IceTransportInternal { << " attr: " << (dtls_piggyback_attr != nullptr) << " ack: " << (dtls_piggyback_ack != nullptr); if (!dtls_stun_piggyback_callbacks_.empty()) { - std::optional> piggyback_attr; + std::optional> piggyback_attr; if (dtls_piggyback_attr) { piggyback_attr = dtls_piggyback_attr->array_view(); } @@ -614,7 +614,7 @@ class FakeIceTransportInternal : public IceTransportInternal { } std::unique_ptr stun_msg(new IceMessage()); - ByteBufferReader buf(MakeArrayView(packet.data(), packet.size())); + ByteBufferReader buf(std::span(packet.data(), packet.size())); RTC_CHECK(stun_msg->Read(&buf)); return stun_msg; } diff --git a/p2p/test/mock_ice_agent.h b/p2p/test/mock_ice_agent.h index 7613a76e837..c555bf0aee2 100644 --- a/p2p/test/mock_ice_agent.h +++ b/p2p/test/mock_ice_agent.h @@ -11,7 +11,8 @@ #ifndef P2P_TEST_MOCK_ICE_AGENT_H_ #define P2P_TEST_MOCK_ICE_AGENT_H_ -#include "api/array_view.h" +#include + #include "api/units/timestamp.h" #include "p2p/base/connection.h" #include "p2p/base/ice_agent_interface.h" @@ -32,7 +33,7 @@ class MockIceAgent : public IceAgentInterface { MOCK_METHOD(void, UpdateState, (), (override)); MOCK_METHOD(void, ForgetLearnedStateForConnections, - (ArrayView), + (std::span), (override)); MOCK_METHOD(void, SendPingRequest, (const Connection*), (override)); MOCK_METHOD(void, @@ -41,7 +42,7 @@ class MockIceAgent : public IceAgentInterface { (override)); MOCK_METHOD(bool, PruneConnections, - (ArrayView), + (std::span), (override)); }; diff --git a/p2p/test/mock_ice_controller.h b/p2p/test/mock_ice_controller.h index 5eb3bf5cfa5..70adf88024e 100644 --- a/p2p/test/mock_ice_controller.h +++ b/p2p/test/mock_ice_controller.h @@ -12,9 +12,9 @@ #define P2P_TEST_MOCK_ICE_CONTROLLER_H_ #include +#include #include -#include "api/array_view.h" #include "api/units/timestamp.h" #include "p2p/base/connection.h" #include "p2p/base/ice_controller_factory_interface.h" @@ -36,11 +36,10 @@ class MockIceController : public IceControllerInterface { MOCK_METHOD(void, SetSelectedConnection, (const Connection*), (override)); MOCK_METHOD(void, AddConnection, (const Connection*), (override)); MOCK_METHOD(void, OnConnectionDestroyed, (const Connection*), (override)); - MOCK_METHOD(ArrayView, + MOCK_METHOD(std::span, GetConnections, (), (const, override)); - MOCK_METHOD(ArrayView, connections, (), (const, override)); MOCK_METHOD(bool, HasPingableConnection, (), (const, override)); MOCK_METHOD(PingResult, GetConnectionToPing, (Timestamp), (override)); MOCK_METHOD(bool, diff --git a/p2p/test/nat_server.cc b/p2p/test/nat_server.cc index 46ade7e0ea4..6a897b17181 100644 --- a/p2p/test/nat_server.cc +++ b/p2p/test/nat_server.cc @@ -14,9 +14,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/environment/environment.h" #include "p2p/test/nat_socket_factory.h" #include "p2p/test/nat_types.h" @@ -116,8 +116,7 @@ class NATProxyServerSocket : public AsyncProxyServerSocket { SocketAddress dest_addr; size_t address_length = UnpackAddressFromNAT( - MakeArrayView(reinterpret_cast(data), *len), - &dest_addr); + std::span(reinterpret_cast(data), *len), &dest_addr); *len -= address_length; if (*len > 0) { memmove(data, data + address_length, *len); diff --git a/p2p/test/nat_socket_factory.cc b/p2p/test/nat_socket_factory.cc index c0ba6d9e434..02abc96aa1e 100644 --- a/p2p/test/nat_socket_factory.cc +++ b/p2p/test/nat_socket_factory.cc @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -64,7 +64,7 @@ void PackAddressForNAT(const SocketAddress& remote_addr, Buffer& buf) { // Decodes the remote address from a packet that has been encoded with the nat's // quasi-STUN format. Returns the length of the address (i.e., the offset into // data where the original packet starts). -size_t UnpackAddressFromNAT(ArrayView buf, +size_t UnpackAddressFromNAT(std::span buf, SocketAddress* remote_addr) { RTC_CHECK(buf.size() >= 8); RTC_DCHECK(buf.data()[0] == 0); diff --git a/p2p/test/nat_socket_factory.h b/p2p/test/nat_socket_factory.h index b22d6eb64d5..f1b388e9892 100644 --- a/p2p/test/nat_socket_factory.h +++ b/p2p/test/nat_socket_factory.h @@ -16,8 +16,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/units/time_delta.h" #include "p2p/test/nat_server.h" @@ -177,7 +177,7 @@ class NATSocketServer : public SocketServer, public NATInternalSocketFactory { // Free-standing NAT helper functions. void PackAddressForNAT(const SocketAddress& remote_addr, Buffer& buf); -size_t UnpackAddressFromNAT(ArrayView buf, +size_t UnpackAddressFromNAT(std::span buf, SocketAddress* remote_addr); } // namespace webrtc diff --git a/p2p/test/nat_unittest.cc b/p2p/test/nat_unittest.cc index f50ea7328c1..502e0f2653f 100644 --- a/p2p/test/nat_unittest.cc +++ b/p2p/test/nat_unittest.cc @@ -36,6 +36,7 @@ #include "rtc_base/virtual_socket_server.h" #include "test/create_test_environment.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -231,13 +232,13 @@ bool TestConnectivity(const SocketAddress& src, const IPAddress& dst) { } void TestPhysicalInternal(const SocketAddress& int_addr) { - AutoThread main_thread; + test::RunLoop main_thread; PhysicalSocketServer socket_server; const Environment env = CreateTestEnvironment(); BasicNetworkManager network_manager(env, &socket_server); network_manager.StartUpdating(); // Process pending messages so the network list is updated. - Thread::Current()->ProcessMessages(0); + main_thread.Flush(); std::vector networks = network_manager.GetNetworks(); std::erase_if(networks, [](const Network* network) { @@ -301,7 +302,7 @@ class TestVirtualSocketServer : public VirtualSocketServer { } // namespace void TestVirtualInternal(int family) { - AutoThread main_thread; + test::RunLoop main_thread; const Environment env = CreateTestEnvironment(); std::unique_ptr int_vss( new TestVirtualSocketServer()); diff --git a/p2p/test/stun_server_unittest.cc b/p2p/test/stun_server_unittest.cc index 95f5298b4d7..8a63a149d9d 100644 --- a/p2p/test/stun_server_unittest.cc +++ b/p2p/test/stun_server_unittest.cc @@ -20,10 +20,10 @@ #include "rtc_base/byte_buffer.h" #include "rtc_base/socket_address.h" #include "rtc_base/test_client.h" -#include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/create_test_environment.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { @@ -57,7 +57,7 @@ class StunServerTest : public ::testing::Test { private: const Environment env_ = CreateTestEnvironment(); VirtualSocketServer ss_; - AutoSocketServerThread main_thread_{&ss_}; + test::RunLoop run_loop_{&ss_}; StunServer server_{AsyncUDPSocket::Create(env_, server_addr, ss_)}; TestClient client_{AsyncUDPSocket::Create(env_, client_addr, ss_)}; }; diff --git a/p2p/test/test_port.cc b/p2p/test/test_port.cc index fb0c9984dc3..eb4161cf1d3 100644 --- a/p2p/test/test_port.cc +++ b/p2p/test/test_port.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/transport/stun.h" #include "p2p/base/connection.h" @@ -39,9 +39,9 @@ TestPort::TestPort(const PortParametersRef& args, : Port(args, IceCandidateType::kHost, min_port, max_port) {} TestPort::~TestPort() = default; -ArrayView TestPort::last_stun_buf() { +std::span TestPort::last_stun_buf() { if (!last_stun_buf_) - return ArrayView(); + return std::span(); return *last_stun_buf_; } IceMessage* TestPort::last_stun_msg() { diff --git a/p2p/test/test_port.h b/p2p/test/test_port.h index b1ddcc96f51..8d107975275 100644 --- a/p2p/test/test_port.h +++ b/p2p/test/test_port.h @@ -14,9 +14,9 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/transport/stun.h" #include "p2p/base/connection.h" @@ -41,7 +41,7 @@ class TestPort : public Port { using Port::GetStunMessage; // The last StunMessage that was sent on this Port. - ArrayView last_stun_buf(); + std::span last_stun_buf(); IceMessage* last_stun_msg(); int last_stun_error_code(); diff --git a/p2p/test/turn_server.cc b/p2p/test/turn_server.cc index edd0f6b249b..5b59ebd83f6 100644 --- a/p2p/test/turn_server.cc +++ b/p2p/test/turn_server.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include // for std::tie #include @@ -21,7 +22,6 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/packet_socket_factory.h" #include "api/sequence_checker.h" @@ -173,30 +173,31 @@ void TurnServer::OnInternalPacket(AsyncPacketSocket* socket, const ReceivedIpPacket& packet) { RTC_DCHECK_RUN_ON(thread_); // Fail if the packet is too small to even contain a channel header. - if (packet.payload().size() < TURN_CHANNEL_HEADER_SIZE) { + std::span payload = packet.payload(); + if (payload.size() < TURN_CHANNEL_HEADER_SIZE) { return; } auto iter = server_sockets_.find(socket); RTC_DCHECK(iter != server_sockets_.end()); TurnServerConnection conn(packet.source_address(), iter->second, socket); - uint16_t msg_type = GetBE16(packet.payload().data()); + uint16_t msg_type = GetBE16(payload); if (!IsTurnChannelData(msg_type)) { // This is a STUN message. - HandleStunMessage(&conn, packet.payload(), packet.ecn()); + HandleStunMessage(&conn, payload, packet.ecn()); } else { // This is a channel message; let the allocation handle it. TurnServerAllocation* allocation = FindAllocation(&conn); if (allocation) { - allocation->HandleChannelData(packet.payload(), packet.ecn()); + allocation->HandleChannelData(payload, packet.ecn()); } if (stun_message_observer_ != nullptr) { - stun_message_observer_->ReceivedChannelData(packet.payload()); + stun_message_observer_->ReceivedChannelData(payload); } } } void TurnServer::HandleStunMessage(TurnServerConnection* conn, - ArrayView payload, + std::span payload, EcnMarking ecn) { RTC_DCHECK_RUN_ON(thread_); TurnMessage msg; @@ -397,7 +398,7 @@ bool TurnServer::ValidateNonce(absl::string_view nonce) const { // Decode the timestamp. int64_t then; char* p = reinterpret_cast(&then); - size_t len = hex_decode(ArrayView(p, sizeof(then)), + size_t len = hex_decode(std::span(p, sizeof(then)), nonce.substr(0, sizeof(then) * 2)); if (len != sizeof(then)) { return false; @@ -554,10 +555,9 @@ bool TurnServerConnection::operator<(const TurnServerConnection& c) const { } std::string TurnServerConnection::ToString() const { - const char* const kProtos[] = {"unknown", "udp", "tcp", "ssltcp"}; StringBuilder ost; ost << src_.ToSensitiveString() << "-" << dst_.ToSensitiveString() << ":" - << kProtos[proto_]; + << ProtoToString(proto_); return ost.Release(); } @@ -784,10 +784,10 @@ void TurnServerAllocation::HandleChannelBindRequest(const TurnMessage* msg) { SendResponse(&response); } -void TurnServerAllocation::HandleChannelData(ArrayView payload, +void TurnServerAllocation::HandleChannelData(std::span payload, EcnMarking ecn) { // Extract the channel number from the data. - uint16_t channel_id = GetBE16(payload.data()); + uint16_t channel_id = GetBE16(payload); auto channel = FindChannel(channel_id); if (channel != channels_.end()) { // Send the data to the peer address. @@ -809,7 +809,7 @@ void TurnServerAllocation::OnExternalPacket(AsyncPacketSocket* socket, ByteBufferWriter buf; buf.WriteUInt16(channel->id); buf.WriteUInt16(static_cast(packet.payload().size())); - buf.Write(ArrayView(packet.payload())); + buf.Write(std::span(packet.payload())); server_->Send(&conn_, buf, packet.ecn()); } else if (!server_->enable_permission_checks_ || HasPermission(packet.source_address().ipaddr())) { diff --git a/p2p/test/turn_server.h b/p2p/test/turn_server.h index 501bafe4659..1258682cd9e 100644 --- a/p2p/test/turn_server.h +++ b/p2p/test/turn_server.h @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/packet_socket_factory.h" #include "api/sequence_checker.h" @@ -97,7 +97,7 @@ class TurnServerAllocation final { std::string ToString() const; void HandleTurnMessage(const TurnMessage* msg, EcnMarking ecn); - void HandleChannelData(ArrayView payload, EcnMarking ecn); + void HandleChannelData(std::span payload, EcnMarking ecn); private: struct Channel { @@ -176,7 +176,7 @@ class TurnRedirectInterface { class StunMessageObserver { public: virtual void ReceivedMessage(const TurnMessage* msg) = 0; - virtual void ReceivedChannelData(ArrayView payload) = 0; + virtual void ReceivedChannelData(std::span payload) = 0; virtual ~StunMessageObserver() {} }; @@ -290,7 +290,7 @@ class TurnServer { void OnInternalSocketClose(AsyncPacketSocket* socket, int err); void HandleStunMessage(TurnServerConnection* conn, - ArrayView payload, + std::span payload, EcnMarking ecn) RTC_RUN_ON(thread_); void HandleBindingRequest(TurnServerConnection* conn, const StunMessage* msg) RTC_RUN_ON(thread_); diff --git a/p2p/test/turn_server_unittest.cc b/p2p/test/turn_server_unittest.cc index 2b63b139339..455e1da4d8a 100644 --- a/p2p/test/turn_server_unittest.cc +++ b/p2p/test/turn_server_unittest.cc @@ -14,13 +14,13 @@ #include "api/environment/environment.h" #include "p2p/base/basic_packet_socket_factory.h" -#include "p2p/base/port_interface.h" #include "rtc_base/async_packet_socket.h" +#include "rtc_base/net_helper.h" #include "rtc_base/socket_address.h" -#include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/create_test_environment.h" #include "test/gtest.h" +#include "test/run_loop.h" // NOTE: This is a work in progress. Currently this file only has tests for // TurnServerConnection, a primitive class used by TurnServer. @@ -48,7 +48,7 @@ class TurnServerConnectionTest : public ::testing::Test { protected: VirtualSocketServer vss_; - AutoSocketServerThread thread_; + test::RunLoop thread_; BasicPacketSocketFactory socket_factory_; }; diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 47c96c61543..5ff1d726145 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -103,7 +103,9 @@ rtc_library("channel") { "../rtc_base/containers:flat_set", "../rtc_base/network:sent_packet", "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/cleanup", "//third_party/abseil-cpp/absl/functional:any_invocable", + "//third_party/abseil-cpp/absl/strings:str_format", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -115,6 +117,7 @@ rtc_source_set("channel_interface") { ":rtp_transport_internal", ":session_description", "../api:jsep", + "../api:rtc_error", "../api:rtp_parameters", "../media:media_channel", "../media:stream_params", @@ -169,21 +172,6 @@ rtc_library("dtls_transport") { ] } -rtc_library("external_hmac") { - visibility = [ ":*" ] - sources = [ - "external_hmac.cc", - "external_hmac.h", - ] - deps = [ - "../rtc_base:logging", - "../rtc_base:zero_memory", - ] - if (rtc_build_libsrtp) { - deps += [ "//third_party/libsrtp" ] - } -} - rtc_library("ice_transport") { visibility = [ ":*" ] sources = [ @@ -214,7 +202,6 @@ rtc_library("jsep_transport") { ":sctp_transport", ":session_description", ":transport_stats", - "../api:array_view", "../api:candidate", "../api:dtls_transport_interface", "../api:ice_transport_interface", @@ -366,6 +353,7 @@ rtc_library("media_session") { "../api:sctp_transport_interface", "../api/environment", "../api/transport:sctp_transport_factory_interface", + "../call:payload_type", "../media:codec", "../media:media_constants", "../media:media_engine", @@ -383,6 +371,7 @@ rtc_library("media_session") { "../rtc_base/experiments:field_trial_parser", "../rtc_base/memory:always_valid_pointer", "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -404,6 +393,7 @@ rtc_library("media_options") { "../p2p:transport_description", "../p2p:transport_description_factory", "../rtc_base:checks", + "../rtc_base/system:plan_b_only", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -422,6 +412,7 @@ rtc_library("codec_vendor") { ":typed_codec_vendor", ":used_ids", "../api:field_trials_view", + "../api:payload_type", "../api:rtc_error", "../api:rtp_parameters", "../api:rtp_transceiver_direction", @@ -525,6 +516,7 @@ rtc_source_set("peer_connection_proxy") { "../api/transport:bandwidth_estimation_settings", "../api/transport:bitrate_settings", "../rtc_base:threading", + "../rtc_base/system:plan_b_only", ] } @@ -560,9 +552,12 @@ rtc_source_set("rtp_receiver_proxy") { "../api:dtls_transport_interface", "../api:frame_transformer_interface", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtp_parameters", "../api:rtp_receiver_interface", "../api:scoped_refptr", + "../api:sframe_decrypter_interface", + "../api:sframe_types", "../api/crypto:frame_decryptor_interface", "../api/transport/rtp:rtp_source", ] @@ -581,6 +576,7 @@ rtc_source_set("rtp_sender_proxy") { "../api:rtp_parameters", "../api:rtp_sender_interface", "../api:scoped_refptr", + "../api:sframe_encrypter_interface", "../api/crypto:frame_encryptor_interface", "../api/video_codecs:video_codecs_api", ] @@ -595,8 +591,10 @@ rtc_library("rtp_transport") { deps = [ ":rtp_transport_internal", ":session_description", - "../api:array_view", "../api:field_trials_view", + "../api:rtc_error", + "../api:rtp_parameters", + "../api:sequence_checker", "../api/task_queue", "../api/task_queue:pending_task_safety_flag", "../api/transport:ecn_marking", @@ -611,11 +609,18 @@ rtc_library("rtp_transport") { "../rtc_base:copy_on_write_buffer", "../rtc_base:event_tracer", "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:network_route", "../rtc_base:socket", + "../rtc_base:stringutils", + "../rtc_base/containers:flat_map", "../rtc_base/containers:flat_set", "../rtc_base/network:received_packet", "../rtc_base/network:sent_packet", + "../rtc_base/system:no_unique_address", + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/container:flat_hash_map", + "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -627,6 +632,7 @@ rtc_source_set("rtp_transport_internal") { sources = [ "rtp_transport_internal.h" ] deps = [ ":session_description", + "../api:rtc_error", "../api/task_queue:pending_task_safety_flag", "../api/transport:ecn_marking", "../api/units:timestamp", @@ -638,6 +644,7 @@ rtc_source_set("rtp_transport_internal") { "../rtc_base:socket", "../rtc_base/network:sent_packet", "//third_party/abseil-cpp/absl/functional:any_invocable", + "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -692,8 +699,6 @@ rtc_library("srtp_session") { "srtp_session.h", ] deps = [ - ":external_hmac", - "../api:array_view", "../api:field_trials_view", "../api:sequence_checker", "../modules/rtp_rtcp:rtp_rtcp_format", @@ -706,6 +711,7 @@ rtc_library("srtp_session") { "../rtc_base:macromagic", "../rtc_base:ssl_adapter", "../rtc_base:stringutils", + "../rtc_base:text2pcap", "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../system_wrappers:metrics", @@ -724,7 +730,6 @@ rtc_library("srtp_transport") { deps = [ ":rtp_transport", ":srtp_session", - "../api:array_view", "../api:field_trials_view", "../api/units:timestamp", "../call:rtp_receiver", @@ -932,7 +937,6 @@ rtc_library("data_channel_controller") { ":peer_connection_internal", ":sctp_data_channel", ":sctp_utils", - "../api:array_view", "../api:data_channel_event_observer_interface", "../api:data_channel_interface", "../api:priority", @@ -1016,7 +1020,6 @@ rtc_library("rtc_stats_collector") { ":rtp_transceiver", ":track_media_info_map", ":transport_stats", - "../api:array_view", "../api:candidate", "../api:data_channel_interface", "../api:dtls_transport_interface", @@ -1060,6 +1063,7 @@ rtc_library("rtc_stats_collector") { "../rtc_base:threading", "../rtc_base:timeutils", "../rtc_base/containers:flat_set", + "../rtc_base/system:plan_b_only", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", @@ -1076,6 +1080,24 @@ rtc_library("rtc_stats_traversal") { "../api:rtc_stats_api", "../api:scoped_refptr", "../rtc_base:checks", + "../stats:rtc_stats", + ] +} + +rtc_library("scoped_operations_batcher") { + visibility = [ ":*" ] + sources = [ + "scoped_operations_batcher.cc", + "scoped_operations_batcher.h", + ] + deps = [ + "../api:rtc_error", + "../api:sequence_checker", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:threading", + "../rtc_base/system:no_unique_address", + "//third_party/abseil-cpp/absl/functional:any_invocable", ] } @@ -1131,6 +1153,8 @@ rtc_library("sdp_offer_answer") { ":rtp_sender_proxy", ":rtp_transceiver", ":rtp_transmission_manager", + ":rtp_transport_internal", + ":scoped_operations_batcher", ":sdp_munging_detector", ":sdp_payload_type_suggester", ":sdp_state_provider", @@ -1140,12 +1164,12 @@ rtc_library("sdp_offer_answer") { ":transceiver_list", ":usage_pattern", ":webrtc_session_description_factory", - "../api:array_view", "../api:audio_options_api", "../api:candidate", "../api:jsep", "../api:make_ref_counted", "../api:media_stream_interface", + "../api:payload_type", "../api:peer_connection_interface", "../api:rtc_error", "../api:rtp_parameters", @@ -1192,6 +1216,7 @@ rtc_library("sdp_offer_answer") { "../rtc_base:weak_ptr", "../rtc_base/containers:flat_map", "../rtc_base/containers:flat_set", + "../rtc_base/system:plan_b_only", "../system_wrappers:metrics", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -1210,13 +1235,16 @@ rtc_library("sdp_payload_type_suggester") { ":jsep_transport_collection", ":session_description", "../api:jsep", + "../api:payload_type", "../api:peer_connection_interface", "../api:rtc_error", + "../api:rtp_parameters", "../call:payload_type", "../call:payload_type_picker", "../media:codec", "../rtc_base:checks", "../rtc_base:event_tracer", + "../rtc_base:logging", "../rtc_base:threading", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -1350,6 +1378,7 @@ rtc_library("peer_connection") { "../rtc_base:unique_id_generator", "../rtc_base:weak_ptr", "../rtc_base/containers:flat_map", + "../rtc_base/system:plan_b_only", "../system_wrappers:metrics", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -1371,6 +1400,7 @@ rtc_library("simulcast_sdp_serializer") { deps = [ ":session_description", ":simulcast_description", + "../api:payload_type", "../api:rtc_error", "../api:rtp_parameters", "../media:codec", @@ -1404,7 +1434,6 @@ rtc_library("legacy_stats_collector") { "legacy_stats_collector.h", ] deps = [ - ":channel", ":channel_interface", ":data_channel_utils", ":jsep_transport_controller", @@ -1446,7 +1475,10 @@ rtc_library("legacy_stats_collector") { "../rtc_base:ssl_adapter", "../rtc_base:threading", "../rtc_base:timeutils", + "../rtc_base/system:plan_b_only", "../system_wrappers", + "//third_party/abseil-cpp/absl/base:nullability", + "//third_party/abseil-cpp/absl/container:flat_hash_set", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", @@ -1469,7 +1501,6 @@ rtc_library("track_media_info_map") { "track_media_info_map.h", ] deps = [ - "../api:array_view", "../api:rtp_parameters", "../media:media_channel", "../rtc_base:checks", @@ -1667,6 +1698,7 @@ rtc_library("rtp_transceiver") { ":channel_interface", ":codec_vendor", ":connection_context", + ":dtls_transport", ":legacy_stats_collector_interface", ":proxy", ":rtp_media_utils", @@ -1675,9 +1707,9 @@ rtc_library("rtp_transceiver") { ":rtp_sender", ":rtp_sender_proxy", ":rtp_transport_internal", + ":scoped_operations_batcher", ":session_description", ":video_rtp_receiver", - "../api:array_view", "../api:audio_options_api", "../api:jsep", "../api:make_ref_counted", @@ -1701,12 +1733,15 @@ rtc_library("rtp_transceiver") { "../media:media_channel", "../media:media_engine", "../media:rtc_media_config", + "../media:stream_params", "../rtc_base:checks", "../rtc_base:crypto_random", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:threading", + "../rtc_base/system:plan_b_only", "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -1757,6 +1792,7 @@ rtc_library("rtp_transmission_manager") { "../rtc_base:threading", "../rtc_base:unique_id_generator", "../rtc_base:weak_ptr", + "../rtc_base/system:plan_b_only", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings:string_view", @@ -1794,10 +1830,16 @@ rtc_library("rtp_receiver") { ":media_stream_proxy", "../api:dtls_transport_interface", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtp_receiver_interface", "../api:scoped_refptr", + "../api:sequence_checker", + "../api:sframe_decrypter_interface", + "../api:sframe_types", "../media:media_channel", "../rtc_base:threading", + "../rtc_base/system:no_unique_address", + "//third_party/abseil-cpp/absl/functional:any_invocable", ] } @@ -1828,7 +1870,7 @@ rtc_library("audio_rtp_receiver") { "../rtc_base:checks", "../rtc_base:macromagic", "../rtc_base:threading", - "../rtc_base/system:no_unique_address", + "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -1862,7 +1904,7 @@ rtc_library("video_rtp_receiver") { "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:threading", - "../rtc_base/system:no_unique_address", + "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -1991,6 +2033,7 @@ rtc_library("rtp_sender") { "../api:rtp_sender_interface", "../api:scoped_refptr", "../api:sequence_checker", + "../api:sframe_encrypter_interface", "../api/crypto:frame_encryptor_interface", "../api/environment", "../api/task_queue", @@ -2116,7 +2159,6 @@ rtc_library("datagram_connection_internal") { ":dtls_srtp_transport", ":dtls_transport", ":ice_transport", - "../api:array_view", "../api:candidate", "../api:datagram_connection", "../api:dtls_transport_interface", @@ -2191,7 +2233,6 @@ if (rtc_include_tests && !build_with_chromium) { "srtp_transport_unittest.cc", "test/rtp_transport_test_util.h", "test/srtp_test_util.h", - "used_ids_unittest.cc", "video_rtp_receiver_unittest.cc", ] @@ -2226,7 +2267,6 @@ if (rtc_include_tests && !build_with_chromium) { ":transport_stats", ":used_ids", ":video_rtp_receiver", - "../api:array_view", "../api:audio_options_api", "../api:candidate", "../api:datagram_connection", @@ -2239,6 +2279,7 @@ if (rtc_include_tests && !build_with_chromium) { "../api:make_ref_counted", "../api:media_stream_interface", "../api:mock_datagram_connection_observer", + "../api:payload_type", "../api:peer_connection_interface", "../api:priority", "../api:rtc_error", @@ -2313,6 +2354,7 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base/containers:flat_set", "../rtc_base/network:received_packet", "../rtc_base/network:sent_packet", + "../rtc_base/system:plan_b_only", "../system_wrappers:metrics", "../test:create_test_environment", "../test:create_test_field_trials", @@ -2387,6 +2429,7 @@ if (rtc_include_tests && !build_with_chromium) { "../p2p:transport_description", "../rtc_base:checks", "../rtc_base:crypto_random", + "../rtc_base:net_helper", "../rtc_base:rtc_base_tests_utils", "../rtc_base:socket_address", "../rtc_base:socket_factory", @@ -2395,6 +2438,7 @@ if (rtc_include_tests && !build_with_chromium) { "../system_wrappers", "../test:create_test_environment", "../test:create_test_field_trials", + "../test:run_loop", "../test:test_support", "../test:wait_until", "//third_party/abseil-cpp/absl/strings", @@ -2425,8 +2469,11 @@ if (rtc_include_tests && !build_with_chromium) { "../api:rtp_sender_interface", "../api:rtp_transceiver_interface", "../api:scoped_refptr", + "../api/units:time_delta", "../rtc_base:checks", "../rtc_base:logging", + "../rtc_base:rtc_event", + "../test:run_loop", "../test:test_support", "../test:wait_until", ] @@ -2451,6 +2498,7 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:logging", "../rtc_base:rtc_base_tests_utils", "../rtc_base:socket_address", + "../test:run_loop", "../test:test_support", "../test:wait_until", "//third_party/abseil-cpp/absl/algorithm:container", @@ -2463,7 +2511,6 @@ if (rtc_include_tests && !build_with_chromium) { sources = [ "congestion_control_integrationtest.cc", "data_channel_integrationtest.cc", - "data_channel_unittest.cc", "dtmf_sender_unittest.cc", "ice_server_parsing_unittest.cc", "jitter_buffer_delay_unittest.cc", @@ -2501,6 +2548,8 @@ if (rtc_include_tests && !build_with_chromium) { "rtp_parameters_conversion_unittest.cc", "rtp_sender_receiver_unittest.cc", "rtp_transceiver_unittest.cc", + "scoped_operations_batcher_unittest.cc", + "sctp_data_channel_unittest.cc", "sctp_utils_unittest.cc", "sdp_munging_detector_unittest.cc", "sdp_offer_answer_unittest.cc", @@ -2516,10 +2565,9 @@ if (rtc_include_tests && !build_with_chromium) { deps = [ ":audio_rtp_receiver", ":audio_track", - ":channel", - ":channel_interface", ":codec_vendor", ":connection_context", + ":data_channel_utils", ":dtls_srtp_transport", ":dtls_transport", ":dtmf_sender", @@ -2528,6 +2576,7 @@ if (rtc_include_tests && !build_with_chromium) { ":ice_server_parsing", ":integration_test_helpers", ":jitter_buffer_delay", + ":jsep_transport_controller", ":legacy_stats_collector", ":local_audio_source", ":media_protocol_names", @@ -2549,7 +2598,9 @@ if (rtc_include_tests && !build_with_chromium) { ":rtp_sender", ":rtp_sender_proxy", ":rtp_transceiver", + ":rtp_transport", ":rtp_transport_internal", + ":scoped_operations_batcher", ":sctp_data_channel", ":sctp_transport", ":sctp_utils", @@ -2589,6 +2640,7 @@ if (rtc_include_tests && !build_with_chromium) { "../api:mock_encoder_selector", "../api:mock_packet_socket_factory", "../api:mock_video_track", + "../api:payload_type", "../api:peer_connection_interface", "../api:priority", "../api:ref_count", @@ -2623,6 +2675,7 @@ if (rtc_include_tests && !build_with_chromium) { "../api/environment:environment_factory", "../api/rtc_event_log", "../api/rtc_event_log:rtc_event_log_factory", + "../api/task_queue", "../api/task_queue:pending_task_safety_flag", "../api/transport:bitrate_settings", "../api/transport:datagram_transport_interface", @@ -2634,10 +2687,12 @@ if (rtc_include_tests && !build_with_chromium) { "../api/video:builtin_video_bitrate_allocator_factory", "../api/video:encoded_image", "../api/video:recordable_encoded_frame", + "../api/video:render_resolution", "../api/video:video_bitrate_allocator_factory", "../api/video:video_codec_constants", "../api/video:video_frame", "../api/video:video_rtp_headers", + "../api/video:video_stream_encoder", "../api/video_codecs:builtin_video_decoder_factory", "../api/video_codecs:builtin_video_encoder_factory", "../api/video_codecs:scalability_mode", @@ -2662,6 +2717,7 @@ if (rtc_include_tests && !build_with_chromium) { "../media:media_constants", "../media:media_engine", "../media:rid_description", + "../media:rtc_audio_video", "../media:rtc_data_sctp_transport_internal", "../media:rtc_media_config", "../media:rtc_media_tests_utils", @@ -2710,6 +2766,7 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:weak_ptr", "../rtc_base/containers:flat_map", "../rtc_base/synchronization:mutex", + "../rtc_base/system:plan_b_only", "../system_wrappers", "../system_wrappers:metrics", "../test:audio_codec_mocks", @@ -2720,6 +2777,7 @@ if (rtc_include_tests && !build_with_chromium) { "../test:test_support", "../test:wait_until", "../test/pc/sctp:fake_sctp_transport", + "../test/time_controller", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -2859,6 +2917,7 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:ssl_adapter", "../rtc_base:task_queue_for_test", "../rtc_base:threading", + "../rtc_base/system:plan_b_only", "../system_wrappers", "../system_wrappers:metrics", "../test:create_test_environment", @@ -2896,12 +2955,14 @@ if (rtc_include_tests && !build_with_chromium) { sources = [ "test/fake_audio_capture_module.cc", "test/fake_audio_capture_module.h", + "test/fake_audio_track.h", "test/fake_data_channel_controller.h", "test/fake_peer_connection_base.h", "test/fake_peer_connection_for_stats.h", "test/fake_periodic_video_source.h", "test/fake_periodic_video_track_source.h", "test/fake_rtc_certificate_generator.h", + "test/fake_video_track.h", "test/fake_video_track_renderer.h", "test/fake_video_track_source.h", "test/frame_generator_capturer_video_track_source.h", @@ -3045,9 +3106,11 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:weak_ptr", "../rtc_base/containers:flat_map", "../rtc_base/synchronization:mutex", + "../rtc_base/system:plan_b_only", "../rtc_base/task_utils:repeating_task", "../system_wrappers", "../test:frame_generator_capturer", + "../test:run_loop", "../test:test_support", "../test:wait_until", "//testing/gmock", @@ -3103,6 +3166,7 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:logging", "../rtc_base/containers:flat_map", "../system_wrappers", + "../test:run_loop", "../test:test_support", "../test/pc/e2e/analyzer/video:default_video_quality_analyzer", "../test/pc/e2e/analyzer/video:default_video_quality_analyzer_shared", diff --git a/pc/DEPS b/pc/DEPS index 53ef6e45ee2..aa4bbbe6650 100644 --- a/pc/DEPS +++ b/pc/DEPS @@ -18,7 +18,7 @@ include_rules = [ ] specific_include_rules = { - "rtc_stats_collector_unittest.cc": [ + "rtc_stats_collector_unittest\\.cc": [ "+json/reader.h", "+json/value.h", ], diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc index 35243ecdce1..dd90cfb62c9 100644 --- a/pc/audio_rtp_receiver.cc +++ b/pc/audio_rtp_receiver.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/dtls_transport_interface.h" @@ -33,6 +34,7 @@ #include "pc/audio_track.h" #include "pc/media_stream_track_proxy.h" #include "pc/remote_audio_source.h" +#include "pc/rtp_receiver.h" #include "rtc_base/checks.h" #include "rtc_base/thread.h" @@ -54,11 +56,11 @@ AudioRtpReceiver::AudioRtpReceiver( absl::string_view receiver_id, const std::vector>& streams, bool is_unified_plan, - VoiceMediaReceiveChannelInterface* voice_channel) + VoiceMediaReceiveChannelInterface* media_channel) : AudioRtpReceiver(worker_thread, receiver_id, streams, - voice_channel, + media_channel, RemoteAudioSource::OnAudioChannelGoneAction::kEnd) { RTC_DCHECK(!is_unified_plan); } @@ -67,11 +69,11 @@ AudioRtpReceiver::AudioRtpReceiver( Thread* worker_thread, absl::string_view receiver_id, const std::vector>& streams, - VoiceMediaReceiveChannelInterface* voice_channel) + VoiceMediaReceiveChannelInterface* media_channel) : AudioRtpReceiver(worker_thread, receiver_id, streams, - voice_channel, + media_channel, RemoteAudioSource::OnAudioChannelGoneAction::kSurvive) {} AudioRtpReceiver::AudioRtpReceiver( @@ -80,7 +82,7 @@ AudioRtpReceiver::AudioRtpReceiver( const std::vector>& streams, VoiceMediaReceiveChannelInterface* voice_channel, RemoteAudioSource::OnAudioChannelGoneAction source_gone_action) - : worker_thread_(worker_thread), + : RtpReceiverBase(worker_thread), id_(receiver_id), source_(make_ref_counted(worker_thread, source_gone_action)), @@ -200,18 +202,20 @@ void AudioRtpReceiver::Stop() { track_->internal()->set_ended(); } -void AudioRtpReceiver::RestartMediaChannel(std::optional ssrc) { +absl::AnyInvocable +AudioRtpReceiver::GetRestartFunctionForMediaChannel( + std::optional ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); bool enabled = track_->internal()->enabled(); MediaSourceInterface::SourceState state = source_->state(); - worker_thread_->BlockingCall([&]() { - RTC_DCHECK_RUN_ON(worker_thread_); - RestartMediaChannel_w(std::move(ssrc), enabled, state); - }); source_->SetState(MediaSourceInterface::kLive); + return [this, ssrc = std::move(ssrc), enabled, state]() mutable { + RTC_DCHECK_RUN_ON(worker_thread_); + GetRestartFunctionForMediaChannel_w(std::move(ssrc), enabled, state); + }; } -void AudioRtpReceiver::RestartMediaChannel_w( +void AudioRtpReceiver::GetRestartFunctionForMediaChannel_w( std::optional ssrc, bool track_enabled, MediaSourceInterface::SourceState state) { @@ -240,14 +244,16 @@ void AudioRtpReceiver::RestartMediaChannel_w( Reconfigure(track_enabled); } -void AudioRtpReceiver::SetupMediaChannel(uint32_t ssrc) { +absl::AnyInvocable AudioRtpReceiver::GetSetupForMediaChannel( + uint32_t ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - RestartMediaChannel(ssrc); + return GetRestartFunctionForMediaChannel(ssrc); } -void AudioRtpReceiver::SetupUnsignaledMediaChannel() { +absl::AnyInvocable +AudioRtpReceiver::GetSetupForUnsignaledMediaChannel() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - RestartMediaChannel(std::nullopt); + return GetRestartFunctionForMediaChannel(std::nullopt); } std::optional AudioRtpReceiver::ssrc() const { @@ -371,7 +377,7 @@ void AudioRtpReceiver::SetMediaChannel( static_cast(media_channel); } -void AudioRtpReceiver::NotifyFirstPacketReceived() { +void AudioRtpReceiver::NotifyFirstPacketReceived(uint32_t ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (observer_) { observer_->OnFirstPacketReceived(media_type()); @@ -379,7 +385,8 @@ void AudioRtpReceiver::NotifyFirstPacketReceived() { received_first_packet_ = true; } -void AudioRtpReceiver::NotifyFirstPacketReceivedAfterReceptiveChange() { +void AudioRtpReceiver::NotifyFirstPacketReceivedAfterReceptiveChange( + uint32_t ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (observer_) { observer_->OnFirstPacketReceivedAfterReceptiveChange(media_type()); diff --git a/pc/audio_rtp_receiver.h b/pc/audio_rtp_receiver.h index fa6a061b7a8..f89ab659764 100644 --- a/pc/audio_rtp_receiver.h +++ b/pc/audio_rtp_receiver.h @@ -17,6 +17,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/dtls_transport_interface.h" @@ -35,7 +36,6 @@ #include "pc/media_stream_track_proxy.h" #include "pc/remote_audio_source.h" #include "pc/rtp_receiver.h" -#include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" @@ -43,20 +43,23 @@ namespace webrtc { class AudioRtpReceiver : public ObserverInterface, public AudioSourceInterface::AudioObserver, - public RtpReceiverInternal { + public RtpReceiverBase { public: // The constructor supports optionally passing the voice channel to the // instance at construction time without having to call `SetMediaChannel()` // on the worker thread straight after construction. // However, when using that, the assumption is that right after construction, - // a call to either `SetupUnsignaledMediaChannel` or `SetupMediaChannel` - // will be made, which will internally start the source on the worker thread. + // a call to either `GetSetupForUnsignaledMediaChannel` or + // `GetSetupForMediaChannel` will be made, which will internally start the + // source on the worker thread. AudioRtpReceiver(Thread* worker_thread, absl::string_view receiver_id, std::vector stream_ids, VoiceMediaReceiveChannelInterface* voice_channel = nullptr); // Note: This is a PlanB-only constructor. // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed. + // This should be PLAN_B_ONLY; but this marking is deferred due to templating + // issues AudioRtpReceiver( Thread* worker_thread, absl::string_view receiver_id, @@ -64,6 +67,8 @@ class AudioRtpReceiver : public ObserverInterface, bool is_unified_plan, // must always be set to false. VoiceMediaReceiveChannelInterface* media_channel = nullptr); // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed. + // This should be PLAN_B_ONLY; but this marking is deferred due to templating + // issues AudioRtpReceiver( Thread* worker_thread, absl::string_view receiver_id, @@ -102,11 +107,11 @@ class AudioRtpReceiver : public ObserverInterface, // RtpReceiverInternal implementation. void Stop() override; - void SetupMediaChannel(uint32_t ssrc) override; - void SetupUnsignaledMediaChannel() override; + absl::AnyInvocable GetSetupForMediaChannel(uint32_t ssrc) override; + absl::AnyInvocable GetSetupForUnsignaledMediaChannel() override; std::optional ssrc() const override; - void NotifyFirstPacketReceived() override; - void NotifyFirstPacketReceivedAfterReceptiveChange() override; + void NotifyFirstPacketReceived(uint32_t ssrc) override; + void NotifyFirstPacketReceivedAfterReceptiveChange(uint32_t ssrc) override; void set_stream_ids(std::vector stream_ids) override; void set_transport( scoped_refptr dtls_transport) override; @@ -132,17 +137,15 @@ class AudioRtpReceiver : public ObserverInterface, VoiceMediaReceiveChannelInterface* media_channel, RemoteAudioSource::OnAudioChannelGoneAction source_gone_action); - void RestartMediaChannel(std::optional ssrc) - RTC_RUN_ON(&signaling_thread_checker_); - void RestartMediaChannel_w(std::optional ssrc, - bool track_enabled, - MediaSourceInterface::SourceState state) - RTC_RUN_ON(worker_thread_); + absl::AnyInvocable GetRestartFunctionForMediaChannel( + std::optional ssrc) RTC_RUN_ON(&signaling_thread_checker_); + void GetRestartFunctionForMediaChannel_w( + std::optional ssrc, + bool track_enabled, + MediaSourceInterface::SourceState state) RTC_RUN_ON(worker_thread_); void Reconfigure(bool track_enabled) RTC_RUN_ON(worker_thread_); void SetOutputVolume_w(double volume) RTC_RUN_ON(worker_thread_); - RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; - Thread* const worker_thread_; const std::string id_; const scoped_refptr source_; const scoped_refptr> track_; diff --git a/pc/audio_rtp_receiver_unittest.cc b/pc/audio_rtp_receiver_unittest.cc index 0471003faed..6a69cd66deb 100644 --- a/pc/audio_rtp_receiver_unittest.cc +++ b/pc/audio_rtp_receiver_unittest.cc @@ -12,13 +12,16 @@ #include #include +#include #include +#include #include #include "api/make_ref_counted.h" #include "api/scoped_refptr.h" #include "api/test/rtc_error_matchers.h" #include "api/units/time_delta.h" +#include "media/base/media_channel.h" #include "pc/test/mock_voice_media_receive_channel_interface.h" #include "rtc_base/thread.h" #include "test/gmock.h" @@ -40,22 +43,28 @@ namespace webrtc { class AudioRtpReceiverTest : public ::testing::Test { protected: AudioRtpReceiverTest() - : worker_(Thread::Current()), + : worker_thread_(Thread::Create()), receiver_( - make_ref_counted(worker_, + make_ref_counted(worker_thread_.get(), std::string(), std::vector())) { + worker_thread_->Start(); EXPECT_CALL(receive_channel_, SetRawAudioSink(kSsrc, _)); EXPECT_CALL(receive_channel_, SetBaseMinimumPlayoutDelayMs(kSsrc, _)); } ~AudioRtpReceiverTest() override { EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolumeMuted)); - receiver_->SetMediaChannel(nullptr); + SetMediaChannel(nullptr); } - AutoThread main_thread_; - Thread* worker_; + void SetMediaChannel(MediaReceiveChannelInterface* media_channel) { + worker_thread_->BlockingCall( + [&]() { receiver_->SetMediaChannel(media_channel); }); + } + + test::RunLoop loop_; + std::unique_ptr worker_thread_; scoped_refptr receiver_; MockVoiceMediaReceiveChannelInterface receive_channel_; }; @@ -71,9 +80,10 @@ TEST_F(AudioRtpReceiverTest, SetOutputVolumeIsCalled) { receiver_->track(); receiver_->track()->set_enabled(true); - receiver_->SetMediaChannel(&receive_channel_); + SetMediaChannel(&receive_channel_); EXPECT_CALL(receive_channel_, SetDefaultRawAudioSink(_)).Times(0); - receiver_->SetupMediaChannel(kSsrc); + auto setup_task = receiver_->GetSetupForMediaChannel(kSsrc); + worker_thread_->BlockingCall([&]() { std::move(setup_task)(); }); EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolume)) .WillOnce(InvokeWithoutArgs([&] { @@ -93,13 +103,14 @@ TEST_F(AudioRtpReceiverTest, VolumesSetBeforeStartingAreRespected) { receiver_->OnSetVolume(kVolume); receiver_->track()->set_enabled(true); - receiver_->SetMediaChannel(&receive_channel_); + SetMediaChannel(&receive_channel_); // The previosly set initial volume should be propagated to the provided - // media_channel_ as soon as SetupMediaChannel is called. + // media_channel_ as soon as GetSetupForMediaChannel is called. EXPECT_CALL(receive_channel_, SetOutputVolume(kSsrc, kVolume)); - receiver_->SetupMediaChannel(kSsrc); + auto setup_task = receiver_->GetSetupForMediaChannel(kSsrc); + worker_thread_->BlockingCall([&]() { std::move(setup_task)(); }); } // Tests that OnChanged notifications are processed correctly on the worker @@ -107,28 +118,33 @@ TEST_F(AudioRtpReceiverTest, VolumesSetBeforeStartingAreRespected) { // constructor. TEST(AudioRtpReceiver, OnChangedNotificationsAfterConstruction) { test::RunLoop loop; - auto* thread = Thread::Current(); // Points to loop's thread. + + std::unique_ptr worker_thread = Thread::Create(); + worker_thread->Start(); MockVoiceMediaReceiveChannelInterface receive_channel; auto receiver = make_ref_counted( - thread, std::string(), std::vector(), &receive_channel); + worker_thread.get(), std::string(), std::vector(), + &receive_channel); EXPECT_CALL(receive_channel, SetDefaultRawAudioSink(_)).Times(1); EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kDefaultVolume)).Times(1); - receiver->SetupUnsignaledMediaChannel(); - loop.Flush(); - - // Mark the track as disabled. - receiver->track()->set_enabled(false); + auto setup_task = receiver->GetSetupForUnsignaledMediaChannel(); + worker_thread->BlockingCall([&]() { std::move(setup_task)(); }); - // When the track was marked as disabled, an async notification was queued + // When the track is marked as disabled, an async notification is queued // for the worker thread. This notification should trigger the volume // of the media channel to be set to kVolumeMuted. - // Flush the worker thread, but set the expectation first for the call. + // Set the expectation first for the call, before changing the track state. EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1); - loop.Flush(); + + // Mark the track as disabled. + receiver->track()->set_enabled(false); + + // Flush the worker thread. + worker_thread->BlockingCall([] {}); EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1); - receiver->SetMediaChannel(nullptr); + worker_thread->BlockingCall([&]() { receiver->SetMediaChannel(nullptr); }); } } // namespace webrtc diff --git a/pc/channel.cc b/pc/channel.cc index 5fed0b364ed..59921fa4ffc 100644 --- a/pc/channel.cc +++ b/pc/channel.cc @@ -19,7 +19,9 @@ #include #include "absl/algorithm/container.h" +#include "absl/cleanup/cleanup.h" #include "absl/functional/any_invocable.h" +#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "api/crypto/crypto_options.h" #include "api/jsep.h" @@ -30,6 +32,7 @@ #include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" +#include "call/rtp_demuxer.h" #include "media/base/codec.h" #include "media/base/media_channel.h" #include "media/base/rid_description.h" @@ -154,9 +157,9 @@ RTCError MaybeIgnorePacketization(const MediaChannelParameters& new_params, // Note: this writes into old_params codec.packetization = std::nullopt; } else if (!has_matching_packetization) { - std::string error_desc = StringFormat( + std::string error_desc = absl::StrFormat( "Failed to set local answer due to incompatible codec " - "packetization for pt='%d' specified.", + "packetization for pt='%v' specified.", codec.id); return RTCError(RTCErrorType::INTERNAL_ERROR, error_desc); } @@ -191,7 +194,7 @@ BaseChannel::BaseChannel( crypto_options.srtp.enable_encrypted_rtp_header_extensions ? RtpExtension::kPreferEncryptedExtension : RtpExtension::kDiscardEncryptedExtension), - demuxer_criteria_(mid), + mid_(std::string(mid)), ssrc_generator_(ssrc_generator) { RTC_DCHECK_RUN_ON(worker_thread_); RTC_DCHECK(media_send_channel_); @@ -224,7 +227,8 @@ bool BaseChannel::ConnectToRtpTransport_n(RtpTransportInternal* rtp_transport) { // We don't need to call OnDemuxerCriteriaUpdatePending/Complete because // there's no previous criteria to worry about. - if (!rtp_transport->RegisterRtpDemuxerSink(demuxer_criteria_, this)) { + RtpDemuxerCriteria criteria = demuxer_criteria(); + if (!rtp_transport->RegisterRtpDemuxerSink(criteria, this)) { return false; } rtp_transport_ = rtp_transport; @@ -264,23 +268,6 @@ bool BaseChannel::SetRtpTransport(RtpTransportInternal* rtp_transport) { if (rtp_transport_) { DisconnectFromRtpTransport_n(); - // Clear the cached header extensions on the worker. - // If the network and worker thread pointers are configured to map to the - // same thread object, we'll do this synchronously. To start with, we're on - // the correct thread anyway, but an important second reason is that other - // parts of the code (SetLocalContent_w, SetRemoteContent_w) may execute a - // BlockingCall that touches `rtp_header_extensions` which, for the case - // where the threads are the same, will be executed before the lambda in the - // PostTask and not after, which may lead to unexpected behavior. - if (worker_thread_ == network_thread_) { - RTC_DCHECK_RUN_ON(worker_thread()); - rtp_header_extensions_.clear(); - } else { - worker_thread_->PostTask(SafeTask(alive_, [this] { - RTC_DCHECK_RUN_ON(worker_thread()); - rtp_header_extensions_.clear(); - })); - } } RTC_DCHECK(!rtp_transport_); @@ -337,33 +324,20 @@ void BaseChannel::Enable(bool enable) { })); } -bool BaseChannel::SetLocalContent(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) { +RTCError BaseChannel::SetLocalContent(const MediaContentDescription* content, + SdpType type) { RTC_DCHECK_RUN_ON(worker_thread()); TRACE_EVENT0("webrtc", "BaseChannel::SetLocalContent"); - return SetLocalContent_w(content, type, error_desc); + return SetLocalContent_w(content, type); } -bool BaseChannel::SetRemoteContent(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) { +RTCError BaseChannel::SetRemoteContent(const MediaContentDescription* content, + SdpType type) { RTC_DCHECK_RUN_ON(worker_thread()); TRACE_EVENT0("webrtc", "BaseChannel::SetRemoteContent"); - return SetRemoteContent_w(content, type, error_desc); + return SetRemoteContent_w(content, type); } -bool BaseChannel::SetPayloadTypeDemuxingEnabled(bool enabled) { - // TODO(bugs.webrtc.org/11993): The demuxer state needs to be managed on the - // network thread. At the moment there's a workaround for inconsistent state - // between the worker and network thread because of this (see - // OnDemuxerCriteriaUpdatePending elsewhere in this file) and - // SetPayloadTypeDemuxingEnabled_w has a BlockingCall over to the network - // thread to apply state updates. - RTC_DCHECK_RUN_ON(worker_thread()); - TRACE_EVENT0("webrtc", "BaseChannel::SetPayloadTypeDemuxingEnabled"); - return SetPayloadTypeDemuxingEnabled_w(enabled); -} bool BaseChannel::IsReadyToSendMedia_w() const { // Send outgoing data if we are enabled, have local and remote content, @@ -428,12 +402,12 @@ void BaseChannel::OnNetworkRouteChanged( media_send_channel()->OnNetworkRouteChanged(transport_name(), new_route); } -void BaseChannel::SetFirstPacketReceivedCallback( - absl::AnyInvocable callback) { +void BaseChannel::SetFirstPacketReceivedCallback_n( + absl::AnyInvocable callback) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(!on_first_packet_received_ || !callback); - // TODO(bugs.webrtc.org/11992): Rename SetFirstPacketReceivedCallback to + // TODO(bugs.webrtc.org/11992): Rename SetFirstPacketReceivedCallback_n to // something that indicates network thread initialization/uninitialization and // call Init_n() / Deinit_n() respectively. // if (!callback) @@ -442,7 +416,7 @@ void BaseChannel::SetFirstPacketReceivedCallback( on_first_packet_received_ = std::move(callback); } -void BaseChannel::SetFirstPacketSentCallback( +void BaseChannel::SetFirstPacketSentCallback_n( absl::AnyInvocable callback) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(!on_first_packet_sent_ || !callback); @@ -451,7 +425,7 @@ void BaseChannel::SetFirstPacketSentCallback( } void BaseChannel::SetPacketReceivedCallback_n( - absl::AnyInvocable callback) { + absl::AnyInvocable callback) { RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(!on_packet_received_n_ || !callback); @@ -520,7 +494,7 @@ void BaseChannel::OnRtpPacket(const RtpPacketReceived& parsed_packet) { RTC_DCHECK(network_initialized()); if (on_first_packet_received_) { - std::move(on_first_packet_received_)(); + std::move(on_first_packet_received_)(parsed_packet); on_first_packet_received_ = nullptr; } @@ -542,88 +516,120 @@ void BaseChannel::OnRtpPacket(const RtpPacketReceived& parsed_packet) { return; } if (on_packet_received_n_) { - on_packet_received_n_(); + on_packet_received_n_(parsed_packet); } media_receive_channel()->OnPacketReceived(parsed_packet); } -bool BaseChannel::MaybeUpdateDemuxerAndRtpExtensions_w( +RTCError BaseChannel::MaybeUpdateDemuxerAndRtpExtensions_w( bool update_demuxer, - std::optional extensions, - std::string& error_desc) { - if (extensions) { - if (rtp_header_extensions_ == extensions) { - extensions.reset(); // No need to update header extensions. - } else { - rtp_header_extensions_ = *extensions; - } + std::optional> payload_types, + const RtpHeaderExtensions& extensions, + std::optional> ssrcs) { + const bool pending_update = + update_demuxer || payload_types.has_value() || ssrcs.has_value(); + if (pending_update) { + media_receive_channel()->OnDemuxerCriteriaUpdatePending(); } - - if (!update_demuxer && !extensions) - return true; // No update needed. + absl::Cleanup cleanup = [this, pending_update] { + if (pending_update) { + media_receive_channel()->OnDemuxerCriteriaUpdateComplete(); + } + }; // TODO(bugs.webrtc.org/13536): See if we can do this asynchronously. - - if (update_demuxer) - media_receive_channel()->OnDemuxerCriteriaUpdatePending(); - - bool success = network_thread()->BlockingCall([&]() mutable { + RTCError error = network_thread()->BlockingCall([&]() -> RTCError { RTC_DCHECK_RUN_ON(network_thread()); - if (!rtp_transport_) { - // To repro this situation, run the - // `ApplyDescriptionWithSameSsrcsBundledFails` test. - RTC_LOG(LS_ERROR) << "No transport assigned for mid=" << mid(); - return false; + RTCError error = + rtp_transport_ + ? rtp_transport_->RegisterRtpHeaderExtensionMap(mid(), extensions) + : (RTCError::InvalidState() << "No transport assigned."); + if (!error.ok()) { + error.string_builder() << " (mid=" << mid() << ")"; + return LOG_ERROR(error); } - // NOTE: This doesn't take the BUNDLE case in account meaning the RTP header - // extension maps are not merged when BUNDLE is enabled. This is fine - // because the ID for MID should be consistent among all the RTP transports. - if (extensions) - rtp_transport_->UpdateRtpHeaderExtensionMap(*extensions); + if (payload_types) { + if (payload_types_ != *payload_types) { + payload_types_ = std::move(*payload_types); + update_demuxer = true; + } + } + + if (ssrcs) { + if (ssrcs_ != *ssrcs) { + ssrcs_ = std::move(*ssrcs); + update_demuxer = true; + } + } if (!update_demuxer) - return true; + return RTCError::OK(); - if (!rtp_transport_->RegisterRtpDemuxerSink(demuxer_criteria_, this)) { - error_desc = - StringFormat("Failed to apply demuxer criteria for '%s': '%s'.", - mid().c_str(), demuxer_criteria_.ToString().c_str()); - return false; + RtpDemuxerCriteria criteria = demuxer_criteria(); + if (!rtp_transport_->RegisterRtpDemuxerSink(criteria, this)) { + return RTCError::InvalidParameter() + << "Failed to apply demuxer criteria for '" << mid() << "': '" + << criteria.ToString() << "'."; } - return true; + return RTCError::OK(); }); - if (update_demuxer) - media_receive_channel()->OnDemuxerCriteriaUpdateComplete(); - - return success; + return error; } -bool BaseChannel::RegisterRtpDemuxerSink_w() { +bool BaseChannel::RegisterRtpDemuxerSink_w( + bool clear_payload_types, + std::optional> ssrcs) { media_receive_channel()->OnDemuxerCriteriaUpdatePending(); - // Copy demuxer criteria, since they're a worker-thread variable - // and we want to pass them to the network thread - bool ret = network_thread_->BlockingCall( - [this, demuxer_criteria = demuxer_criteria_] { - RTC_DCHECK_RUN_ON(network_thread()); - if (!rtp_transport_) { - // Transport was disconnected before attempting to update the - // criteria. This can happen while setting the remote description. - // See chromium:1295469 for an example. - return false; - } - // Note that RegisterRtpDemuxerSink first unregisters the sink if - // already registered. So this will change the state of the class - // whether the call succeeds or not. - return rtp_transport_->RegisterRtpDemuxerSink(demuxer_criteria, this); - }); + absl::Cleanup cleanup = [this] { + media_receive_channel()->OnDemuxerCriteriaUpdateComplete(); + }; + bool ret = network_thread_->BlockingCall([&] { + RTC_DCHECK_RUN_ON(network_thread()); + if (!rtp_transport_) { + // Transport was disconnected before attempting to update the + // criteria. This can happen while setting the remote description. + // See chromium:1295469 for an example. + return false; + } - media_receive_channel()->OnDemuxerCriteriaUpdateComplete(); + bool needs_re_registration = false; + + if (clear_payload_types && !payload_types_.empty()) { + payload_types_.clear(); + needs_re_registration = true; + } + + if (ssrcs) { + if (ssrcs_ != *ssrcs) { + ssrcs_ = std::move(*ssrcs); + needs_re_registration = true; + } + } + + if (!needs_re_registration) { + return true; + } + + // Note that RegisterRtpDemuxerSink first unregisters the sink if + // already registered. So this will change the state of the class + // whether the call succeeds or not. + RtpDemuxerCriteria criteria = demuxer_criteria(); + return rtp_transport_->RegisterRtpDemuxerSink(criteria, this); + }); return ret; } +RtpDemuxerCriteria BaseChannel::demuxer_criteria() const { + RTC_DCHECK_RUN_ON(network_thread()); + RtpDemuxerCriteria criteria(mid_); + criteria.payload_types() = payload_types_; + criteria.ssrcs() = ssrcs_; + return criteria; +} + void BaseChannel::EnableMedia_w() { if (enabled_) return; @@ -681,50 +687,9 @@ void BaseChannel::ChannelNotWritable_n() { RTC_LOG(LS_INFO) << "Channel not writable (" << ToString() << ")"; } -bool BaseChannel::SetPayloadTypeDemuxingEnabled_w(bool enabled) { - RTC_LOG_THREAD_BLOCK_COUNT(); - - if (enabled == payload_type_demuxing_enabled_) { - return true; - } - - payload_type_demuxing_enabled_ = enabled; - - bool config_changed = false; - - if (!enabled) { - // TODO(crbug.com/11477): This will remove *all* unsignaled streams (those - // without an explicitly signaled SSRC), which may include streams that - // were matched to this channel by MID or RID. Ideally we'd remove only the - // streams that were matched based on payload type alone, but currently - // there is no straightforward way to identify those streams. - media_receive_channel()->ResetUnsignaledRecvStream(); - if (!demuxer_criteria_.payload_types().empty()) { - config_changed = true; - demuxer_criteria_.payload_types().clear(); - } - } else if (!payload_types_.empty()) { - for (const auto& type : payload_types_) { - if (demuxer_criteria_.payload_types().insert(type).second) { - config_changed = true; - } - } - } else { - RTC_DCHECK(demuxer_criteria_.payload_types().empty()); - } - - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); - - if (!config_changed) - return true; - - // Note: This synchronously hops to the network thread. - return RegisterRtpDemuxerSink_w(); -} - -bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, - SdpType type, - std::string& error_desc) { +RTCError BaseChannel::UpdateLocalStreams_w( + const std::vector& streams, + SdpType type) { // In the case of RIDs (where SSRCs are not negotiated), this method will // generate an SSRC for each layer in StreamParams. That representation will // be stored internally in `local_streams_`. @@ -737,18 +702,16 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, // in which niether SSRCs or RIDs are negotiated. // Check for streams that have been removed. - bool ret = true; for (const StreamParams& old_stream : local_streams_) { if (!old_stream.has_ssrcs() || GetStream(streams, StreamFinder(&old_stream))) { continue; } if (!media_send_channel()->RemoveSendStream(old_stream.first_ssrc())) { - error_desc = StringFormat( - "Failed to remove send stream with ssrc %u from m-section with " - "mid='%s'.", - old_stream.first_ssrc(), mid().c_str()); - ret = false; + return RTCError::InvalidParameter() + << "Failed to remove send stream with ssrc " + << old_stream.first_ssrc() << " from m-section with mid='" << mid() + << "'."; } } // Check for new streams. @@ -770,12 +733,10 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, RTC_DCHECK(new_stream.has_ssrcs() || new_stream.has_rids()); if (new_stream.has_ssrcs() && new_stream.has_rids()) { - error_desc = StringFormat( - "Failed to add send stream: %u into m-section with mid='%s'. Stream " - "has both SSRCs and RIDs.", - new_stream.first_ssrc(), mid().c_str()); - ret = false; - continue; + return RTCError::InvalidParameter() + << "Failed to add send stream: " << new_stream.first_ssrc() + << " into m-section with mid='" << mid() + << "'. Stream has both SSRCs and RIDs."; } // At this point we use the legacy simulcast group in StreamParams to @@ -787,31 +748,28 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector& streams, } if (media_send_channel()->AddSendStream(new_stream)) { - RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.ssrcs[0] + RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.first_ssrc() << " into " << ToString(); } else { - error_desc = StringFormat( - "Failed to add send stream ssrc: %u into m-section with mid='%s'", - new_stream.first_ssrc(), mid().c_str()); - ret = false; + return RTCError::InvalidParameter() + << "Failed to add send stream ssrc: " << new_stream.first_ssrc() + << " into m-section with mid='" << mid() << "'"; } } local_streams_ = all_streams; - return ret; + return RTCError::OK(); } -bool BaseChannel::UpdateRemoteStreams_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) { +RTCError BaseChannel::UpdateRemoteStreams_w( + const MediaContentDescription* content, + SdpType type) { RTC_LOG_THREAD_BLOCK_COUNT(); - bool needs_re_registration = false; + bool clear_payload_types = false; if (!RtpTransceiverDirectionHasSend(content->direction())) { RTC_DLOG(LS_VERBOSE) << "UpdateRemoteStreams_w: remote side will not send " "- disable payload type demuxing for " << ToString(); - if (ClearHandledPayloadTypes()) { - needs_re_registration = payload_type_demuxing_enabled_; - } + clear_payload_types = true; } const std::vector& streams = content->streams(); @@ -832,11 +790,10 @@ bool BaseChannel::UpdateRemoteStreams_w(const MediaContentDescription* content, RTC_LOG(LS_INFO) << "Remove remote ssrc: " << old_stream.first_ssrc() << " from " << ToString() << "."; } else { - error_desc = StringFormat( - "Failed to remove remote stream with ssrc %u from m-section with " - "mid='%s'.", - old_stream.first_ssrc(), mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to remove remote stream with ssrc " + << old_stream.first_ssrc() << " from m-section with mid='" + << mid() << "'."; } } } @@ -856,31 +813,24 @@ bool BaseChannel::UpdateRemoteStreams_w(const MediaContentDescription* content, : "unsignaled") << " to " << ToString(); } else { - error_desc = - StringFormat("Failed to add remote stream ssrc: %s to %s", - new_stream.has_ssrcs() - ? std::to_string(new_stream.first_ssrc()).c_str() - : "unsignaled", - ToString().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to add remote stream ssrc: " + << (new_stream.has_ssrcs() + ? std::to_string(new_stream.first_ssrc()) + : "unsignaled") + << " to " << ToString(); } } // Update the receiving SSRCs. ssrcs.insert(new_stream.ssrcs.begin(), new_stream.ssrcs.end()); } - if (demuxer_criteria_.ssrcs() != ssrcs) { - demuxer_criteria_.ssrcs() = std::move(ssrcs); - needs_re_registration = true; - } - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); // Re-register the sink to update after changing the demuxer criteria. - if (needs_re_registration && !RegisterRtpDemuxerSink_w()) { - error_desc = StringFormat("Failed to set up audio demuxing for mid='%s'.", - mid().c_str()); - return false; + if (!RegisterRtpDemuxerSink_w(clear_payload_types, std::move(ssrcs))) { + return RTCError::InvalidParameter() + << "Failed to set up audio demuxing for mid='" << mid() << "'."; } remote_streams_ = streams; @@ -890,7 +840,7 @@ bool BaseChannel::UpdateRemoteStreams_w(const MediaContentDescription* content, RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); - return true; + return RTCError::OK(); } RtpHeaderExtensions BaseChannel::GetDeduplicatedRtpHeaderExtensions( @@ -899,24 +849,24 @@ RtpHeaderExtensions BaseChannel::GetDeduplicatedRtpHeaderExtensions( extensions_filter_); } -bool BaseChannel::MaybeAddHandledPayloadType(int payload_type) { - bool demuxer_criteria_modified = false; - if (payload_type_demuxing_enabled_) { - demuxer_criteria_modified = demuxer_criteria_.payload_types() - .insert(static_cast(payload_type)) - .second; - } - // Even if payload type demuxing is currently disabled, we need to remember - // the payload types in case it's re-enabled later. - payload_types_.insert(static_cast(payload_type)); - return demuxer_criteria_modified; -} - -bool BaseChannel::ClearHandledPayloadTypes() { - const bool was_empty = demuxer_criteria_.payload_types().empty(); - demuxer_criteria_.payload_types().clear(); - payload_types_.clear(); - return !was_empty; +// TODO: webrtc:42222117 - Move header extension logic in the channel classes +// to the network thread. At the moment, this function does a BlockingCall +// to the network thread in order to delegate the check to the transport. +// The worker and network threads are commonly configured to map to the same +// actual thread, so a blocking call in those cases isn't expensive, although +// not ideal. +RTCError BaseChannel::CheckRtpExtensionValidity( + const RtpHeaderExtensions& extensions) const { + return network_thread()->BlockingCall([&]() -> RTCError { + RTC_DCHECK_RUN_ON(network_thread()); + RTCError error = + rtp_transport_ ? rtp_transport_->VerifyRtpHeaderExtensionMap(extensions) + : (RTCError::InvalidState() << "No transport assigned."); + if (!error.ok()) { + error.string_builder() << " (mid=" << mid() << ")"; + } + return error; + }); } void BaseChannel::SignalSentPacket_n(const SentPacketInfo& sent_packet) { @@ -967,9 +917,8 @@ void VoiceChannel::UpdateMediaSendRecvState_w() { << " send=" << send << " for " << ToString(); } -bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) { +RTCError VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, + SdpType type) { TRACE_EVENT0("webrtc", "VoiceChannel::SetLocalContent_w"); RTC_DLOG(LS_INFO) << "Setting local voice description for " << ToString(); @@ -977,7 +926,12 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, RtpHeaderExtensions header_extensions = GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions()); - bool update_header_extensions = true; + + RTCError error = CheckRtpExtensionValidity(header_extensions); + if (!error.ok()) { + return error; + } + // TODO: issues.webrtc.org/383078466 - remove if pushdown on answer is enough. media_send_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed()); @@ -988,19 +942,17 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, recv_params.mid = mid(); if (!media_receive_channel()->SetReceiverParameters(recv_params)) { - error_desc = StringFormat( - "Failed to set local audio description recv parameters for m-section " - "with mid='%s'.", - mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to set local audio description recv parameters for " + "m-section with mid='" + << mid() << "'."; } - bool criteria_modified = false; + std::optional> payload_types; if (RtpTransceiverDirectionHasRecv(content->direction())) { + payload_types.emplace(); for (const Codec& codec : content->codecs()) { - if (MaybeAddHandledPayloadType(codec.id)) { - criteria_modified = true; - } + payload_types->insert(codec.id); } } @@ -1008,20 +960,20 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { AudioSenderParameter send_params = last_send_params_; + RTC_DCHECK(!send_params.mid.empty()); send_params.extensions = header_extensions; send_params.extmap_allow_mixed = content->extmap_allow_mixed(); if (!media_send_channel()->SetSenderParameters(send_params)) { - error_desc = StringFormat( - "Failed to set send parameters for m-section with mid='%s'.", - mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to set send parameters for m-section with mid='" + << mid() << "'."; } last_send_params_ = send_params; } - if (!UpdateLocalStreams_w(content->streams(), type, error_desc)) { - RTC_DCHECK(!error_desc.empty()); - return false; + error = UpdateLocalStreams_w(content->streams(), type); + if (!error.ok()) { + return error; } set_local_content_direction(content->direction()); @@ -1031,21 +983,15 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content, // TODO: https://issues.webrtc.org/360058654 - reenable after cleanup // RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); - bool success = MaybeUpdateDemuxerAndRtpExtensions_w( - criteria_modified, - update_header_extensions - ? std::optional(std::move(header_extensions)) - : std::nullopt, - error_desc); - - // RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); - - return success; + return MaybeUpdateDemuxerAndRtpExtensions_w( + /*update_demuxer=*/false, std::move(payload_types), + recv_params.extensions, + /*ssrcs=*/std::nullopt); } -bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) { +RTCError VoiceChannel::SetRemoteContent_w( + const MediaContentDescription* content, + SdpType type) { TRACE_EVENT0("webrtc", "VoiceChannel::SetRemoteContent_w"); RTC_LOG(LS_INFO) << "Setting remote voice description for " << ToString(); @@ -1054,24 +1000,25 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, &send_params); send_params.mid = mid(); - bool parameters_applied = - media_send_channel()->SetSenderParameters(send_params); - if (!parameters_applied) { - error_desc = StringFormat( - "Failed to set remote audio description send parameters for m-section " - "with mid='%s'.", - mid().c_str()); - return false; + RTCError error = CheckRtpExtensionValidity(send_params.extensions); + if (!error.ok()) { + return error; + } + + if (!media_send_channel()->SetSenderParameters(send_params)) { + return RTCError::InvalidParameter() + << "Failed to set remote audio description send parameters for " + "m-section with mid='" + << mid() << "'."; } if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { AudioReceiverParameters recv_params = last_recv_params_; recv_params.extensions = send_params.extensions; if (!media_receive_channel()->SetReceiverParameters(recv_params)) { - error_desc = StringFormat( - "Failed to set recv parameters for m-section with mid='%s'.", - mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to set recv parameters for m-section with mid='" + << mid() << "'."; } last_recv_params_ = recv_params; } @@ -1089,7 +1036,14 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, media_send_channel()->SenderNonSenderRttEnabled()); last_send_params_ = send_params; - return UpdateRemoteStreams_w(content, type, error_desc); + error = UpdateRemoteStreams_w(content, type); + if (error.ok() && (type == SdpType::kAnswer || type == SdpType::kPrAnswer)) { + error = MaybeUpdateDemuxerAndRtpExtensions_w( + /*update_demuxer=*/false, /*payload_types=*/std::nullopt, + last_recv_params_.extensions, /*ssrcs=*/std::nullopt); + } + + return error; } VideoChannel::VideoChannel( @@ -1132,21 +1086,24 @@ void VideoChannel::UpdateMediaSendRecvState_w() { << " send=" << send << " for " << ToString(); } -bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) { +RTCError VideoChannel::SetLocalContent_w(const MediaContentDescription* content, + SdpType type) { TRACE_EVENT0("webrtc", "VideoChannel::SetLocalContent_w"); - RTC_LOG_THREAD_BLOCK_COUNT(); - RtpHeaderExtensions header_extensions = GetDeduplicatedRtpHeaderExtensions(content->rtp_header_extensions()); - bool update_header_extensions = true; + + RTCError error = CheckRtpExtensionValidity(header_extensions); + if (!error.ok()) { + return error; + } + + RTC_LOG_THREAD_BLOCK_COUNT(); + // TODO: issues.webrtc.org/383078466 - remove if pushdown on answer is enough. media_send_channel()->SetExtmapAllowMixed(content->extmap_allow_mixed()); VideoReceiverParameters recv_params = last_recv_params_; - MediaChannelParametersFromMediaDescription( content, header_extensions, RtpTransceiverDirectionHasRecv(content->direction()), &recv_params); @@ -1161,26 +1118,24 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, // offer by ignoring the packetiztion and fall back to standard packetization // instead. if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { - RTCError status = MaybeIgnorePacketization(recv_params, send_params); - if (!status.ok()) { - error_desc = status.message(); - return false; + error = MaybeIgnorePacketization(recv_params, send_params); + if (!error.ok()) { + return error; } } if (!media_receive_channel()->SetReceiverParameters(recv_params)) { - error_desc = StringFormat( - "Failed to set local video description recv parameters for m-section " - "with mid='%s'.", - mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to set local video description recv parameters for " + "m-section with mid='" + << mid() << "'."; } - bool criteria_modified = false; + std::optional> payload_types; if (RtpTransceiverDirectionHasRecv(content->direction())) { + payload_types.emplace(); for (const Codec& codec : content->codecs()) { - if (MaybeAddHandledPayloadType(codec.id)) - criteria_modified = true; + payload_types->insert(codec.id); } } @@ -1188,17 +1143,16 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { if (!media_send_channel()->SetSenderParameters(send_params)) { - error_desc = StringFormat( - "Failed to set send parameters for m-section with mid='%s'.", - mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to set send parameters for m-section with mid='" + << mid() << "'."; } last_send_params_ = send_params; } - if (!UpdateLocalStreams_w(content->streams(), type, error_desc)) { - RTC_DCHECK(!error_desc.empty()); - return false; + error = UpdateLocalStreams_w(content->streams(), type); + if (!error.ok()) { + return error; } set_local_content_direction(content->direction()); @@ -1206,21 +1160,18 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0); - bool success = MaybeUpdateDemuxerAndRtpExtensions_w( - criteria_modified, - update_header_extensions - ? std::optional(std::move(header_extensions)) - : std::nullopt, - error_desc); + error = MaybeUpdateDemuxerAndRtpExtensions_w( + /*update_demuxer=*/false, std::move(payload_types), + recv_params.extensions, /*ssrcs=*/std::nullopt); RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); - return success; + return error; } -bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) { +RTCError VideoChannel::SetRemoteContent_w( + const MediaContentDescription* content, + SdpType type) { TRACE_EVENT0("webrtc", "VideoChannel::SetRemoteContent_w"); RTC_LOG(LS_INFO) << "Setting remote video description for " << ToString(); @@ -1230,6 +1181,11 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, send_params.mid = mid(); send_params.conference_mode = content->conference_mode(); + RTCError error = CheckRtpExtensionValidity(send_params.extensions); + if (!error.ok()) { + return error; + } + VideoReceiverParameters recv_params = last_recv_params_; // Ensure that there is a matching packetization for each receive codec. If we @@ -1238,34 +1194,38 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, // offer by ignoring the packetiztion and fall back to standard packetization // instead. if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { - RTCError status = MaybeIgnorePacketization(send_params, recv_params); - if (!status.ok()) { - error_desc = status.message(); - return false; + error = MaybeIgnorePacketization(send_params, recv_params); + if (!error.ok()) { + return error; } } if (!media_send_channel()->SetSenderParameters(send_params)) { - error_desc = StringFormat( - "Failed to set remote video description send parameters for m-section " - "with mid='%s'.", - mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to set remote video description send parameters for " + "m-section with mid='" + << mid() << "'."; } if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) { recv_params.extensions = send_params.extensions; recv_params.rtcp.reduced_size = send_params.rtcp.reduced_size; if (!media_receive_channel()->SetReceiverParameters(recv_params)) { - error_desc = StringFormat( - "Failed to set recv parameters for m-section with mid='%s'.", - mid().c_str()); - return false; + return RTCError::InvalidParameter() + << "Failed to set recv parameters for m-section with mid='" + << mid() << "'."; } last_recv_params_ = recv_params; } last_send_params_ = send_params; - return UpdateRemoteStreams_w(content, type, error_desc); + error = UpdateRemoteStreams_w(content, type); + if (error.ok() && (type == SdpType::kAnswer || type == SdpType::kPrAnswer)) { + error = MaybeUpdateDemuxerAndRtpExtensions_w( + /*update_demuxer=*/false, /*payload_types=*/std::nullopt, + last_recv_params_.extensions, /*ssrcs=*/std::nullopt); + } + + return error; } } // namespace webrtc diff --git a/pc/channel.h b/pc/channel.h index 615bb571859..a19950fbc5e 100644 --- a/pc/channel.h +++ b/pc/channel.h @@ -24,6 +24,7 @@ #include "api/crypto/crypto_options.h" #include "api/jsep.h" #include "api/media_types.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" @@ -95,7 +96,7 @@ class BaseChannel : public ChannelInterface, TaskQueueBase* worker_thread() const { return worker_thread_; } Thread* network_thread() const { return network_thread_; } - const std::string& mid() const override { return demuxer_criteria_.mid(); } + const std::string& mid() const override { return mid_; } // TODO(deadbeef): This is redundant; remove this. absl::string_view transport_name() const override { RTC_DCHECK_RUN_ON(network_thread()); @@ -122,21 +123,10 @@ class BaseChannel : public ChannelInterface, } // Channel control - bool SetLocalContent(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) override; - bool SetRemoteContent(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) override; - // Controls whether this channel will receive packets on the basis of - // matching payload type alone. This is needed for legacy endpoints that - // don't signal SSRCs or use MID/RID, but doesn't make sense if there is - // more than channel of specific media type, As that creates an ambiguity. - // - // This method will also remove any existing streams that were bound to this - // channel on the basis of payload type, since one of these streams might - // actually belong to a new channel. See: crbug.com/webrtc/11477 - bool SetPayloadTypeDemuxingEnabled(bool enabled) override; + RTCError SetLocalContent(const MediaContentDescription* content, + SdpType type) override; + RTCError SetRemoteContent(const MediaContentDescription* content, + SdpType type) override; void Enable(bool enable) override; @@ -148,12 +138,13 @@ class BaseChannel : public ChannelInterface, } // Used for latency measurements. - void SetFirstPacketReceivedCallback( - absl::AnyInvocable callback) override; - void SetFirstPacketSentCallback( + void SetFirstPacketReceivedCallback_n( + absl::AnyInvocable callback) override; + void SetFirstPacketSentCallback_n( absl::AnyInvocable callback) override; - void SetPacketReceivedCallback_n(absl::AnyInvocable callback) override + void SetPacketReceivedCallback_n( + absl::AnyInvocable callback) override RTC_RUN_ON(network_thread()); // From RtpTransport - public for testing only @@ -248,29 +239,20 @@ class BaseChannel : public ChannelInterface, void ChannelWritable_n() RTC_RUN_ON(network_thread()); void ChannelNotWritable_n() RTC_RUN_ON(network_thread()); - bool SetPayloadTypeDemuxingEnabled_w(bool enabled) - RTC_RUN_ON(worker_thread()); - // Should be called whenever the conditions for // IsReadyToReceiveMedia/IsReadyToSendMedia are satisfied (or unsatisfied). // Updates the send/recv state of the media channel. virtual void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) = 0; - bool UpdateLocalStreams_w(const std::vector& streams, - SdpType type, - std::string& error_desc) - RTC_RUN_ON(worker_thread()); - bool UpdateRemoteStreams_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) - RTC_RUN_ON(worker_thread()); - virtual bool SetLocalContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) + RTCError UpdateLocalStreams_w(const std::vector& streams, + SdpType type) RTC_RUN_ON(worker_thread()); + RTCError UpdateRemoteStreams_w(const MediaContentDescription* content, + SdpType type) RTC_RUN_ON(worker_thread()); + virtual RTCError SetLocalContent_w(const MediaContentDescription* content, + SdpType type) RTC_RUN_ON(worker_thread()) = 0; - virtual bool SetRemoteContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) + virtual RTCError SetRemoteContent_w(const MediaContentDescription* content, + SdpType type) RTC_RUN_ON(worker_thread()) = 0; // Returns a list of RTP header extensions where any extension URI is unique. @@ -279,30 +261,28 @@ class BaseChannel : public ChannelInterface, RtpHeaderExtensions GetDeduplicatedRtpHeaderExtensions( const RtpHeaderExtensions& extensions); - // Add `payload_type` to `demuxer_criteria_` if payload type demuxing is - // enabled. - // Returns true if the demuxer payload type changed and a re-registration - // is needed. - bool MaybeAddHandledPayloadType(int payload_type) RTC_RUN_ON(worker_thread()); - - // Returns true if the demuxer payload type criteria was non-empty before - // clearing. - bool ClearHandledPayloadTypes() RTC_RUN_ON(worker_thread()); - - // Hops to the network thread to update the transport if an update is - // requested. If `update_demuxer` is false and `extensions` is not set, the - // function simply returns. If either of these is set, the function updates - // the transport with either or both of the demuxer criteria and the supplied - // rtp header extensions. + // Checks that the provided RTP header extensions are valid. + // This verifies that all extension IDs are within the valid range, + // that there are no duplicate IDs, and that no existing extension ID + // has been reassigned to a different URI. + RTCError CheckRtpExtensionValidity( + const RtpHeaderExtensions& extensions) const RTC_RUN_ON(worker_thread()); + // Returns `true` if either an update wasn't needed or one was successfully // applied. If the return value is `false`, then updating the demuxer criteria // failed, which needs to be treated as an error. - bool MaybeUpdateDemuxerAndRtpExtensions_w( + RTCError MaybeUpdateDemuxerAndRtpExtensions_w( bool update_demuxer, - std::optional extensions, - std::string& error_desc) RTC_RUN_ON(worker_thread()); - - bool RegisterRtpDemuxerSink_w() RTC_RUN_ON(worker_thread()); + std::optional> payload_types, + const RtpHeaderExtensions& extensions, + std::optional> ssrcs) RTC_RUN_ON(worker_thread()); + + // Registers a demuxer criteria with the transport, on the network thread. + // This function will fail if there's no transport of if a sink is already + // registered for this channel's demuxer_critera(). + bool RegisterRtpDemuxerSink_w(bool clear_payload_types, + std::optional> ssrcs) + RTC_RUN_ON(worker_thread()); // Return description of media channel to facilitate logging std::string ToString() const; @@ -315,6 +295,8 @@ class BaseChannel : public ChannelInterface, RTC_RUN_ON(network_thread()); void DisconnectFromRtpTransport_n() RTC_RUN_ON(network_thread()); void SignalSentPacket_n(const SentPacketInfo& sent_packet); + // Only called on the network thread. + RtpDemuxerCriteria demuxer_criteria() const RTC_RUN_ON(network_thread()); TaskQueueBase* const worker_thread_; Thread* const network_thread_; @@ -322,13 +304,13 @@ class BaseChannel : public ChannelInterface, scoped_refptr alive_; // The functions are deleted after they have been called. - absl::AnyInvocable on_first_packet_received_ - RTC_GUARDED_BY(network_thread()); + absl::AnyInvocable + on_first_packet_received_ RTC_GUARDED_BY(network_thread()); absl::AnyInvocable on_first_packet_sent_ RTC_GUARDED_BY(network_thread()); // Used to unmute. - absl::AnyInvocable on_packet_received_n_ + absl::AnyInvocable on_packet_received_n_ RTC_GUARDED_BY(network_thread()); RtpTransportInternal* rtp_transport_ RTC_GUARDED_BY(network_thread()) = @@ -352,7 +334,6 @@ class BaseChannel : public ChannelInterface, // call to the worker thread, so it should be safe. bool enabled_ RTC_GUARDED_BY(worker_thread()) = false; bool enabled_s_ RTC_GUARDED_BY(signaling_thread()) = false; - bool payload_type_demuxing_enabled_ RTC_GUARDED_BY(worker_thread()) = true; std::vector local_streams_ RTC_GUARDED_BY(worker_thread()); std::vector remote_streams_ RTC_GUARDED_BY(worker_thread()); RtpTransceiverDirection local_content_direction_ @@ -361,12 +342,10 @@ class BaseChannel : public ChannelInterface, RTC_GUARDED_BY(worker_thread()) = RtpTransceiverDirection::kInactive; // Cached list of payload types, used if payload type demuxing is re-enabled. - flat_set payload_types_ RTC_GUARDED_BY(worker_thread()); - // A stored copy of the rtp header extensions as applied to the transport. - RtpHeaderExtensions rtp_header_extensions_ RTC_GUARDED_BY(worker_thread()); - // TODO(bugs.webrtc.org/12239): Modified on worker thread, accessed - // on network thread in RegisterRtpDemuxerSink_n (called from Init_w) - RtpDemuxerCriteria demuxer_criteria_; + flat_set payload_types_ RTC_GUARDED_BY(network_thread()); + + const std::string mid_; + flat_set ssrcs_ RTC_GUARDED_BY(network_thread()); // This generator is used to generate SSRCs for local streams. // This is needed in cases where SSRCs are not negotiated or set explicitly // like in Simulcast. @@ -426,13 +405,10 @@ class VoiceChannel : public BaseChannel { private: // overrides from BaseChannel void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) override; - bool SetLocalContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) - RTC_RUN_ON(worker_thread()) override; - bool SetRemoteContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) + RTCError SetLocalContent_w(const MediaContentDescription* content, + SdpType type) RTC_RUN_ON(worker_thread()) override; + RTCError SetRemoteContent_w(const MediaContentDescription* content, + SdpType type) RTC_RUN_ON(worker_thread()) override; // Last AudioSenderParameter sent down to the media_channel() via @@ -493,13 +469,10 @@ class VideoChannel : public BaseChannel { private: // overrides from BaseChannel void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) override; - bool SetLocalContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) - RTC_RUN_ON(worker_thread()) override; - bool SetRemoteContent_w(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) + RTCError SetLocalContent_w(const MediaContentDescription* content, + SdpType type) RTC_RUN_ON(worker_thread()) override; + RTCError SetRemoteContent_w(const MediaContentDescription* content, + SdpType type) RTC_RUN_ON(worker_thread()) override; // Last VideoSenderParameters sent down to the media_channel() via diff --git a/pc/channel_interface.h b/pc/channel_interface.h index efeea359bae..dfbe7496cc9 100644 --- a/pc/channel_interface.h +++ b/pc/channel_interface.h @@ -18,6 +18,7 @@ #include "absl/strings/string_view.h" #include "api/jsep.h" #include "api/media_types.h" +#include "api/rtc_error.h" #include "media/base/media_channel.h" #include "media/base/stream_params.h" #include "pc/rtp_transport_internal.h" @@ -25,6 +26,7 @@ namespace webrtc { class Call; +class RtpPacketReceived; class VideoBitrateAllocatorFactory; class VideoChannel; class VoiceChannel; @@ -75,23 +77,20 @@ class ChannelInterface { virtual void Enable(bool enable) = 0; // Used for latency measurements. - virtual void SetFirstPacketReceivedCallback( - absl::AnyInvocable callback) = 0; - virtual void SetFirstPacketSentCallback( + virtual void SetFirstPacketReceivedCallback_n( + absl::AnyInvocable callback) = 0; + virtual void SetFirstPacketSentCallback_n( absl::AnyInvocable callback) = 0; // Used to unmute. virtual void SetPacketReceivedCallback_n( - absl::AnyInvocable callback) = 0; + absl::AnyInvocable callback) = 0; // Channel control - virtual bool SetLocalContent(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) = 0; - virtual bool SetRemoteContent(const MediaContentDescription* content, - SdpType type, - std::string& error_desc) = 0; - virtual bool SetPayloadTypeDemuxingEnabled(bool enabled) = 0; + virtual RTCError SetLocalContent(const MediaContentDescription* content, + SdpType type) = 0; + virtual RTCError SetRemoteContent(const MediaContentDescription* content, + SdpType type) = 0; // Access to the local and remote streams that were set on the channel. virtual const std::vector& local_streams() const = 0; diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc index e277fece3f4..62ea4d8f729 100644 --- a/pc/channel_unittest.cc +++ b/pc/channel_unittest.cc @@ -14,16 +14,17 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" -#include "api/array_view.h" #include "api/audio_options.h" #include "api/crypto/crypto_options.h" #include "api/field_trials.h" #include "api/jsep.h" +#include "api/rtc_error.h" #include "api/rtp_headers.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" @@ -62,13 +63,14 @@ #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace { using ::testing::AllOf; using ::testing::ElementsAre; using ::testing::Field; -using ::webrtc::ArrayView; +using ::testing::HasSubstr; using ::webrtc::CreateTestFieldTrials; using ::webrtc::DtlsTransportInternal; using ::webrtc::FakeVoiceMediaReceiveChannel; @@ -76,6 +78,7 @@ using ::webrtc::FakeVoiceMediaSendChannel; using ::webrtc::FieldTrials; using ::webrtc::RidDescription; using ::webrtc::RidDirection; +using ::webrtc::RTCError; using ::webrtc::RtpExtension; using ::webrtc::RtpTransceiverDirection; using ::webrtc::SdpType; @@ -149,8 +152,8 @@ class ChannelTest : public ::testing::Test { }; ChannelTest(bool verify_playout, - webrtc::ArrayView rtp_data, - webrtc::ArrayView rtcp_data, + std::span rtp_data, + std::span rtcp_data, NetworkIsWorker network_is_worker) : verify_playout_(verify_playout), rtp_packet_(rtp_data.data(), rtp_data.size()), @@ -198,6 +201,7 @@ class ChannelTest : public ::testing::Test { typename T::Options(), network_thread_), flags1, flags2); } + void CreateChannels(std::unique_ptr ch1s, std::unique_ptr ch1r, std::unique_ptr ch2s, @@ -306,6 +310,7 @@ class ChannelTest : public ::testing::Test { AddLegacyStreamInContent(kSsrc2, flags2, &remote_media_content2_); } } + std::unique_ptr CreateChannel( webrtc::Thread* worker_thread, webrtc::Thread* network_thread, @@ -412,65 +417,61 @@ class ChannelTest : public ::testing::Test { } bool SendInitiate() { - std::string err; - bool result = channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, err); - if (result) { + RTCError result = + channel1_->SetLocalContent(&local_media_content1_, SdpType::kOffer); + if (result.ok()) { channel1_->Enable(true); FlushCurrentThread(); - result = channel2_->SetRemoteContent(&remote_media_content1_, - SdpType::kOffer, err); - if (result) { + result = + channel2_->SetRemoteContent(&remote_media_content1_, SdpType::kOffer); + if (result.ok()) { ConnectFakeTransports(); result = channel2_->SetLocalContent(&local_media_content2_, - SdpType::kAnswer, err); + SdpType::kAnswer); } } - return result; + return result.ok(); } bool SendAccept() { channel2_->Enable(true); FlushCurrentThread(); - std::string err; - return channel1_->SetRemoteContent(&remote_media_content2_, - SdpType::kAnswer, err); + return channel1_ + ->SetRemoteContent(&remote_media_content2_, SdpType::kAnswer) + .ok(); } bool SendOffer() { - std::string err; - bool result = channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, err); - if (result) { + RTCError result = + channel1_->SetLocalContent(&local_media_content1_, SdpType::kOffer); + if (result.ok()) { channel1_->Enable(true); - result = channel2_->SetRemoteContent(&remote_media_content1_, - SdpType::kOffer, err); + result = + channel2_->SetRemoteContent(&remote_media_content1_, SdpType::kOffer); } - return result; + return result.ok(); } bool SendProvisionalAnswer() { - std::string err; - bool result = channel2_->SetLocalContent(&local_media_content2_, - SdpType::kPrAnswer, err); - if (result) { + RTCError result = + channel2_->SetLocalContent(&local_media_content2_, SdpType::kPrAnswer); + if (result.ok()) { channel2_->Enable(true); result = channel1_->SetRemoteContent(&remote_media_content2_, - SdpType::kPrAnswer, err); + SdpType::kPrAnswer); ConnectFakeTransports(); } - return result; + return result.ok(); } bool SendFinalAnswer() { - std::string err; - bool result = channel2_->SetLocalContent(&local_media_content2_, - SdpType::kAnswer, err); - if (result) { + RTCError result = + channel2_->SetLocalContent(&local_media_content2_, SdpType::kAnswer); + if (result.ok()) { result = channel1_->SetRemoteContent(&remote_media_content2_, - SdpType::kAnswer, err); + SdpType::kAnswer); } - return result; + return result.ok(); } void SendRtp(typename T::MediaSendChannel* media_channel, @@ -528,10 +529,10 @@ class ChannelTest : public ::testing::Test { int pl_type) { webrtc::Buffer data(rtp_packet_.data(), rtp_packet_.size()); // Set SSRC in the rtp packet copy. - webrtc::SetBE32(data.data() + 8, ssrc); - webrtc::SetBE16(data.data() + 2, sequence_number); + webrtc::SetBE32(std::span(data).subspan(8, 4), ssrc); + webrtc::SetBE16(std::span(data).subspan(2, 2), sequence_number); if (pl_type >= 0) { - webrtc::Set8(data.data(), 1, static_cast(pl_type)); + webrtc::Set8(data, 1, static_cast(pl_type)); } return data; } @@ -637,10 +638,9 @@ class ChannelTest : public ::testing::Test { CreateChannels(0, 0); typename T::Content content; CreateContent(0, kPcmuCodec, kH264Codec, &content); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok()); EXPECT_EQ(0U, media_send_channel1_impl()->send_codecs().size()); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok()); ASSERT_EQ(1U, media_send_channel1_impl()->send_codecs().size()); EXPECT_EQ(content.codecs()[0], media_send_channel1_impl()->send_codecs()[0]); @@ -660,11 +660,8 @@ class ChannelTest : public ::testing::Test { }); CreateChannels(0, 0); - std::string err; - ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer, err)) - << err; - ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kAnswer, err)) - << err; + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer).ok()); + ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kAnswer).ok()); EXPECT_THAT(media_receive_channel1_impl()->recv_extensions(), ElementsAre(AllOf(Field("id", &RtpExtension::id, 1), @@ -689,11 +686,8 @@ class ChannelTest : public ::testing::Test { }); CreateChannels(0, 0); - std::string err; - ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kOffer, err)) - << err; - ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kAnswer, err)) - << err; + ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kOffer).ok()); + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kAnswer).ok()); EXPECT_THAT(media_receive_channel1_impl()->recv_extensions(), ElementsAre(AllOf(Field("id", &RtpExtension::id, 1), @@ -705,6 +699,82 @@ class ChannelTest : public ::testing::Test { RtpExtension::kVideoRotationUri)))); } + void TestDuplicateRtpHeaderExtensionIds() { + typename T::Content local; + CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local); + local.set_rtp_header_extensions({ + RtpExtension(RtpExtension::kTransportSequenceNumberUri, 1), + RtpExtension(RtpExtension::kVideoRotationUri, 1), + }); + + CreateChannels(0, 0); + RTCError error = channel1_->SetLocalContent(&local, SdpType::kOffer); + EXPECT_FALSE(error.ok()); + EXPECT_THAT(error.message(), HasSubstr("Duplicate extension ID")); + } + + void TestInvalidRtpHeaderExtensionIds() { + typename T::Content local; + CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local); + local.set_rtp_header_extensions({ + RtpExtension(RtpExtension::kTransportSequenceNumberUri, 256), + }); + + CreateChannels(0, 0); + RTCError error = channel1_->SetLocalContent(&local, SdpType::kOffer); + EXPECT_FALSE(error.ok()); + EXPECT_THAT(error.message(), HasSubstr("Bad extension ID")); + } + + void TestRtpHeaderExtensionIdReassignment() { + typename T::Content local; + CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local); + local.set_rtp_header_extensions({ + RtpExtension(RtpExtension::kTransportSequenceNumberUri, 1), + }); + + CreateChannels(0, 0); + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer).ok()); + + typename T::Content local_updated; + CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local_updated); + local_updated.set_rtp_header_extensions({ + RtpExtension(RtpExtension::kVideoRotationUri, 1), + }); + RTCError error = + channel1_->SetLocalContent(&local_updated, SdpType::kOffer); + EXPECT_FALSE(error.ok()); + EXPECT_THAT(error.message(), HasSubstr("RTP extension ID reassignment")); + } + + void TestRtpHeaderExtensionIdHistoryReassignment() { + typename T::Content local; + CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local); + local.set_rtp_header_extensions({ + RtpExtension(RtpExtension::kTransportSequenceNumberUri, 1), + }); + + CreateChannels(0, 0); + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer).ok()); + + typename T::Content local_empty; + CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local_empty); + local_empty.set_rtp_header_extensions({}); + ASSERT_TRUE(channel1_->SetLocalContent(&local_empty, SdpType::kOffer).ok()); + + typename T::Content local_updated; + CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local_updated); + local_updated.set_rtp_header_extensions({ + RtpExtension(RtpExtension::kVideoRotationUri, 1), + }); + RTCError error = + channel1_->SetLocalContent(&local_updated, SdpType::kOffer); + // Expected to succeed because the mapping for ID 1 was cleared by the + // previous SetLocalContent call (which set extensions to empty). + // RtpTransport allows reuse when not in use by any active MID. + EXPECT_TRUE(error.ok()); + } + // Test that SetLocalContent and SetRemoteContent properly configure // extmap-allow-mixed. void TestSetContentsExtmapAllowMixedCaller(bool offer, bool answer) { @@ -716,12 +786,12 @@ class ChannelTest : public ::testing::Test { auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo); auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo); content.set_extmap_allow_mixed_enum(offer_enum); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok()); content.set_extmap_allow_mixed_enum(answer_enum); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok()); EXPECT_EQ(answer, media_send_channel1_impl()->ExtmapAllowMixed()); } + void TestSetContentsExtmapAllowMixedCallee(bool offer, bool answer) { // For a callee, SetRemoteContent() is called first with an offer and next // SetLocalContent() is called with the answer. @@ -731,10 +801,9 @@ class ChannelTest : public ::testing::Test { auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo); auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo); content.set_extmap_allow_mixed_enum(offer_enum); - std::string err; - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer).ok()); content.set_extmap_allow_mixed_enum(answer_enum); - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer).ok()); EXPECT_EQ(answer, media_send_channel1()->ExtmapAllowMixed()); } @@ -743,11 +812,10 @@ class ChannelTest : public ::testing::Test { void TestSetContentsNullOffer() { CreateChannels(0, 0); typename T::Content content; - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok()); CreateContent(0, kPcmuCodec, kH264Codec, &content); EXPECT_EQ(0U, media_send_channel1_impl()->send_codecs().size()); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok()); ASSERT_EQ(1U, media_send_channel1_impl()->send_codecs().size()); EXPECT_EQ(content.codecs()[0], media_send_channel1_impl()->send_codecs()[0]); @@ -761,13 +829,12 @@ class ChannelTest : public ::testing::Test { CreateContent(0, kPcmuCodec, kH264Codec, &content); // Both sides agree on mux. Should no longer be a separate RTCP channel. content.set_rtcp_mux(true); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok()); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok()); // Only initiator supports mux. Should still have a separate RTCP channel. - EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer).ok()); content.set_rtcp_mux(false); - EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer).ok()); } // Test that SetLocalContent and SetRemoteContent properly set RTCP @@ -778,25 +845,24 @@ class ChannelTest : public ::testing::Test { CreateContent(0, kPcmuCodec, kH264Codec, &content); // Both sides agree on reduced size. content.set_rtcp_reduced_size(true); - std::string err; // The RTCP mode is a send property and should be configured based on // the remote content and not the local content. - EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok()); EXPECT_EQ(media_receive_channel1_impl()->RtcpMode(), webrtc::RtcpMode::kCompound); - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok()); EXPECT_EQ(media_receive_channel1_impl()->RtcpMode(), webrtc::RtcpMode::kReducedSize); // Only initiator supports reduced size. - EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer, err)); + EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer).ok()); EXPECT_EQ(media_receive_channel2_impl()->RtcpMode(), webrtc::RtcpMode::kCompound); content.set_rtcp_reduced_size(false); - EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer).ok()); EXPECT_EQ(media_receive_channel2_impl()->RtcpMode(), webrtc::RtcpMode::kCompound); // Peer renegotiates without reduced size. - EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok()); EXPECT_EQ(media_receive_channel1_impl()->RtcpMode(), webrtc::RtcpMode::kCompound); } @@ -820,21 +886,19 @@ class ChannelTest : public ::testing::Test { typename T::Content content1; CreateContent(0, kPcmuCodec, kH264Codec, &content1); content1.AddStream(stream1); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer).ok()); channel1_->Enable(true); EXPECT_EQ(1u, media_send_channel1_impl()->send_streams().size()); - - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, err)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer).ok()); EXPECT_EQ(1u, media_receive_channel2_impl()->recv_streams().size()); ConnectFakeTransports(); // Channel 2 do not send anything. typename T::Content content2; CreateContent(0, kPcmuCodec, kH264Codec, &content2); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer).ok()); EXPECT_EQ(0u, media_receive_channel1_impl()->recv_streams().size()); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer).ok()); channel2_->Enable(true); EXPECT_EQ(0u, media_send_channel2_impl()->send_streams().size()); @@ -846,21 +910,21 @@ class ChannelTest : public ::testing::Test { typename T::Content content3; CreateContent(0, kPcmuCodec, kH264Codec, &content3); content3.AddStream(stream2); - EXPECT_TRUE(channel2_->SetLocalContent(&content3, SdpType::kOffer, err)); + EXPECT_TRUE(channel2_->SetLocalContent(&content3, SdpType::kOffer).ok()); ASSERT_EQ(1u, media_send_channel2_impl()->send_streams().size()); EXPECT_EQ(stream2, media_send_channel2_impl()->send_streams()[0]); - EXPECT_TRUE(channel1_->SetRemoteContent(&content3, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content3, SdpType::kOffer).ok()); ASSERT_EQ(1u, media_receive_channel1_impl()->recv_streams().size()); EXPECT_EQ(stream2, media_receive_channel1_impl()->recv_streams()[0]); // Channel 1 replies but stop sending stream1. typename T::Content content4; CreateContent(0, kPcmuCodec, kH264Codec, &content4); - EXPECT_TRUE(channel1_->SetLocalContent(&content4, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content4, SdpType::kAnswer).ok()); EXPECT_EQ(0u, media_send_channel1_impl()->send_streams().size()); - EXPECT_TRUE(channel2_->SetRemoteContent(&content4, SdpType::kAnswer, err)); + EXPECT_TRUE(channel2_->SetRemoteContent(&content4, SdpType::kAnswer).ok()); EXPECT_EQ(0u, media_receive_channel2_impl()->recv_streams().size()); SendCustomRtp2(kSsrc2, 0); @@ -885,21 +949,23 @@ class ChannelTest : public ::testing::Test { EXPECT_FALSE(media_receive_channel1_impl()->playout()); } EXPECT_FALSE(media_send_channel1_impl()->sending()); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, err)); + EXPECT_TRUE( + channel1_->SetLocalContent(&local_media_content1_, SdpType::kOffer) + .ok()); if (verify_playout_) { EXPECT_TRUE(media_receive_channel1_impl()->playout()); } EXPECT_FALSE(media_send_channel1_impl()->sending()); - EXPECT_TRUE(channel2_->SetRemoteContent(&local_media_content1_, - SdpType::kOffer, err)); + EXPECT_TRUE( + channel2_->SetRemoteContent(&local_media_content1_, SdpType::kOffer) + .ok()); if (verify_playout_) { EXPECT_FALSE(media_receive_channel2_impl()->playout()); } EXPECT_FALSE(media_send_channel2_impl()->sending()); - EXPECT_TRUE(channel2_->SetLocalContent(&local_media_content2_, - SdpType::kAnswer, err)); + EXPECT_TRUE( + channel2_->SetLocalContent(&local_media_content2_, SdpType::kAnswer) + .ok()); if (verify_playout_) { EXPECT_FALSE(media_receive_channel2_impl()->playout()); } @@ -919,8 +985,9 @@ class ChannelTest : public ::testing::Test { EXPECT_TRUE(media_receive_channel2_impl()->playout()); } EXPECT_TRUE(media_send_channel2_impl()->sending()); - EXPECT_TRUE(channel1_->SetRemoteContent(&local_media_content2_, - SdpType::kAnswer, err)); + EXPECT_TRUE( + channel1_->SetRemoteContent(&local_media_content2_, SdpType::kAnswer) + .ok()); if (verify_playout_) { EXPECT_TRUE(media_receive_channel1_impl()->playout()); } @@ -950,12 +1017,11 @@ class ChannelTest : public ::testing::Test { } EXPECT_FALSE(media_send_channel2_impl()->sending()); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, err)); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kPrAnswer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer).ok()); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer).ok()); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kPrAnswer).ok()); EXPECT_TRUE( - channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer, err)); + channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer).ok()); ConnectFakeTransports(); if (verify_playout_) { @@ -969,9 +1035,9 @@ class ChannelTest : public ::testing::Test { // Update `content2` to be RecvOnly. content2.set_direction(RtpTransceiverDirection::kRecvOnly); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kPrAnswer, err)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kPrAnswer).ok()); EXPECT_TRUE( - channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer, err)); + channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer).ok()); if (verify_playout_) { EXPECT_TRUE(media_receive_channel1_impl()->playout()); @@ -984,8 +1050,8 @@ class ChannelTest : public ::testing::Test { // Update `content2` to be SendRecv. content2.set_direction(RtpTransceiverDirection::kSendRecv); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err)); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer).ok()); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer).ok()); if (verify_playout_) { EXPECT_TRUE(media_receive_channel1_impl()->playout()); @@ -999,11 +1065,11 @@ class ChannelTest : public ::testing::Test { // Update `content2` to be inactive on the receiver while sending at the // sender. content2.set_direction(RtpTransceiverDirection::kInactive); - EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, err)); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer).ok()); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer).ok()); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer).ok()); content2.set_direction(RtpTransceiverDirection::kRecvOnly); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer).ok()); if (verify_playout_) { EXPECT_FALSE(media_receive_channel2_impl()->playout()); } @@ -1011,10 +1077,10 @@ class ChannelTest : public ::testing::Test { // Re-enable `content2`. content2.set_direction(RtpTransceiverDirection::kSendRecv); - EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); - EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, err)); - EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err)); - EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer).ok()); + EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer).ok()); + EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer).ok()); + EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer).ok()); if (verify_playout_) { EXPECT_TRUE(media_receive_channel2_impl()->playout()); } @@ -1269,7 +1335,7 @@ class ChannelTest : public ::testing::Test { EXPECT_TRUE(CheckNoRtp2()); } - void SendBundleToBundle(ArrayView pl_types, + void SendBundleToBundle(std::span pl_types, bool rtcp_mux, bool secure) { int sequence_number1_1 = 0, sequence_number2_2 = 0; @@ -1306,39 +1372,37 @@ class ChannelTest : public ::testing::Test { void TestSetContentFailure() { CreateChannels(0, 0); - std::string err; std::unique_ptr content( CreateMediaContentWithStream(1)); media_receive_channel1_impl()->set_fail_set_recv_codecs(true); EXPECT_FALSE( - channel1_->SetLocalContent(content.get(), SdpType::kOffer, err)); + channel1_->SetLocalContent(content.get(), SdpType::kOffer).ok()); EXPECT_FALSE( - channel1_->SetLocalContent(content.get(), SdpType::kAnswer, err)); + channel1_->SetLocalContent(content.get(), SdpType::kAnswer).ok()); media_send_channel1_impl()->set_fail_set_send_codecs(true); EXPECT_FALSE( - channel1_->SetRemoteContent(content.get(), SdpType::kOffer, err)); + channel1_->SetRemoteContent(content.get(), SdpType::kOffer).ok()); media_send_channel1_impl()->set_fail_set_send_codecs(true); EXPECT_FALSE( - channel1_->SetRemoteContent(content.get(), SdpType::kAnswer, err)); + channel1_->SetRemoteContent(content.get(), SdpType::kAnswer).ok()); } void TestSendTwoOffers() { CreateChannels(0, 0); - std::string err; std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetLocalContent(content1.get(), SdpType::kOffer, err)); + channel1_->SetLocalContent(content1.get(), SdpType::kOffer).ok()); EXPECT_TRUE(media_send_channel1_impl()->HasSendStream(1)); std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetLocalContent(content2.get(), SdpType::kOffer, err)); + channel1_->SetLocalContent(content2.get(), SdpType::kOffer).ok()); EXPECT_FALSE(media_send_channel1_impl()->HasSendStream(1)); EXPECT_TRUE(media_send_channel1_impl()->HasSendStream(2)); } @@ -1346,17 +1410,16 @@ class ChannelTest : public ::testing::Test { void TestReceiveTwoOffers() { CreateChannels(0, 0); - std::string err; std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetRemoteContent(content1.get(), SdpType::kOffer, err)); + channel1_->SetRemoteContent(content1.get(), SdpType::kOffer).ok()); EXPECT_TRUE(media_receive_channel1_impl()->HasRecvStream(1)); std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetRemoteContent(content2.get(), SdpType::kOffer, err)); + channel1_->SetRemoteContent(content2.get(), SdpType::kOffer).ok()); EXPECT_FALSE(media_receive_channel1_impl()->HasRecvStream(1)); EXPECT_TRUE(media_receive_channel1_impl()->HasRecvStream(2)); } @@ -1364,19 +1427,18 @@ class ChannelTest : public ::testing::Test { void TestSendPrAnswer() { CreateChannels(0, 0); - std::string err; // Receive offer std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetRemoteContent(content1.get(), SdpType::kOffer, err)); + channel1_->SetRemoteContent(content1.get(), SdpType::kOffer).ok()); EXPECT_TRUE(media_receive_channel1_impl()->HasRecvStream(1)); // Send PR answer std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetLocalContent(content2.get(), SdpType::kPrAnswer, err)); + channel1_->SetLocalContent(content2.get(), SdpType::kPrAnswer).ok()); EXPECT_TRUE(media_receive_channel1_impl()->HasRecvStream(1)); EXPECT_TRUE(media_send_channel1_impl()->HasSendStream(2)); @@ -1384,7 +1446,7 @@ class ChannelTest : public ::testing::Test { std::unique_ptr content3( CreateMediaContentWithStream(3)); EXPECT_TRUE( - channel1_->SetLocalContent(content3.get(), SdpType::kAnswer, err)); + channel1_->SetLocalContent(content3.get(), SdpType::kAnswer).ok()); EXPECT_TRUE(media_receive_channel1_impl()->HasRecvStream(1)); EXPECT_FALSE(media_send_channel1_impl()->HasSendStream(2)); EXPECT_TRUE(media_send_channel1_impl()->HasSendStream(3)); @@ -1393,19 +1455,18 @@ class ChannelTest : public ::testing::Test { void TestReceivePrAnswer() { CreateChannels(0, 0); - std::string err; // Send offer std::unique_ptr content1( CreateMediaContentWithStream(1)); EXPECT_TRUE( - channel1_->SetLocalContent(content1.get(), SdpType::kOffer, err)); + channel1_->SetLocalContent(content1.get(), SdpType::kOffer).ok()); EXPECT_TRUE(media_send_channel1_impl()->HasSendStream(1)); // Receive PR answer std::unique_ptr content2( CreateMediaContentWithStream(2)); EXPECT_TRUE( - channel1_->SetRemoteContent(content2.get(), SdpType::kPrAnswer, err)); + channel1_->SetRemoteContent(content2.get(), SdpType::kPrAnswer).ok()); EXPECT_TRUE(media_send_channel1_impl()->HasSendStream(1)); EXPECT_TRUE(media_receive_channel1_impl()->HasRecvStream(2)); @@ -1413,7 +1474,7 @@ class ChannelTest : public ::testing::Test { std::unique_ptr content3( CreateMediaContentWithStream(3)); EXPECT_TRUE( - channel1_->SetRemoteContent(content3.get(), SdpType::kAnswer, err)); + channel1_->SetRemoteContent(content3.get(), SdpType::kAnswer).ok()); EXPECT_TRUE(media_send_channel1_impl()->HasSendStream(1)); EXPECT_FALSE(media_receive_channel1_impl()->HasRecvStream(2)); EXPECT_TRUE(media_receive_channel1_impl()->HasRecvStream(3)); @@ -1457,9 +1518,9 @@ class ChannelTest : public ::testing::Test { void DefaultMaxBitrateIsUnlimited() { CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_, - SdpType::kOffer, err)); + EXPECT_TRUE( + channel1_->SetLocalContent(&local_media_content1_, SdpType::kOffer) + .ok()); EXPECT_EQ(media_send_channel1_impl()->max_bps(), -1); VerifyMaxBitrate(media_send_channel1()->GetRtpSendParameters(kSsrc1), std::nullopt); @@ -1532,14 +1593,13 @@ class ChannelTest : public ::testing::Test { CreateChannels(0, 0); typename T::Content content1, content2, content3; CreateSimulcastContent({"f", "h", "q"}, &content1); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer).ok()); VerifySimulcastStreamParams(content1.streams()[0], channel1_.get()); StreamParams stream1 = channel1_->local_streams()[0]; // Create a similar offer. SetLocalContent should not remove and add. CreateSimulcastContent({"f", "h", "q"}, &content2); - EXPECT_TRUE(channel1_->SetLocalContent(&content2, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content2, SdpType::kOffer).ok()); VerifySimulcastStreamParams(content2.streams()[0], channel1_.get()); StreamParams stream2 = channel1_->local_streams()[0]; // Check that the streams are identical (SSRCs didn't change). @@ -1547,24 +1607,20 @@ class ChannelTest : public ::testing::Test { // Create third offer that has same RIDs in different order. CreateSimulcastContent({"f", "q", "h"}, &content3); - EXPECT_TRUE(channel1_->SetLocalContent(&content3, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&content3, SdpType::kOffer).ok()); VerifySimulcastStreamParams(content3.streams()[0], channel1_.get()); } protected: - void WaitForThreads() { - WaitForThreads(webrtc::ArrayView()); - } + void WaitForThreads() { WaitForThreads(std::span()); } static void ProcessThreadQueue(webrtc::Thread* thread) { RTC_DCHECK(thread->IsCurrent()); while (!thread->empty()) { thread->ProcessMessages(0); } } - static void FlushCurrentThread() { - webrtc::Thread::Current()->ProcessMessages(0); - } - void WaitForThreads(webrtc::ArrayView threads) { + void FlushCurrentThread() { main_thread_.Flush(); } + void WaitForThreads(std::span threads) { // `threads` and current thread post packets to network thread. for (webrtc::Thread* thread : threads) { SendTask(thread, [thread] { ProcessThreadQueue(thread); }); @@ -1624,7 +1680,7 @@ class ChannelTest : public ::testing::Test { channel2_->media_receive_channel()); } - webrtc::AutoThread main_thread_; + webrtc::test::RunLoop main_thread_; // TODO(pbos): Remove playout from all media channels and let renderers mute // themselves. const bool verify_playout_; @@ -1945,6 +2001,23 @@ TEST_F(VoiceChannelSingleThreadTest, RemovesExtensionNotPresentInLocalAnswer) { Base::TestRemovesExtensionNotPresentInLocalAnswer(); } +TEST_F(VoiceChannelSingleThreadTest, DuplicateRtpHeaderExtensionIds) { + Base::TestDuplicateRtpHeaderExtensionIds(); +} + +TEST_F(VoiceChannelSingleThreadTest, InvalidRtpHeaderExtensionIds) { + Base::TestInvalidRtpHeaderExtensionIds(); +} + +TEST_F(VoiceChannelSingleThreadTest, RtpHeaderExtensionIdReassignment) { + Base::TestRtpHeaderExtensionIdReassignment(); +} + +TEST_F(VoiceChannelSingleThreadTest, + TestRtpHeaderExtensionIdHistoryReassignment) { + Base::TestRtpHeaderExtensionIdHistoryReassignment(); +} + // VoiceChannelDoubleThreadTest TEST_F(VoiceChannelDoubleThreadTest, TestInit) { Base::TestInit(); @@ -2233,6 +2306,23 @@ TEST_F(VideoChannelSingleThreadTest, RemovesExtensionNotPresentInLocalAnswer) { Base::TestRemovesExtensionNotPresentInLocalAnswer(); } +TEST_F(VideoChannelSingleThreadTest, DuplicateRtpHeaderExtensionIds) { + Base::TestDuplicateRtpHeaderExtensionIds(); +} + +TEST_F(VideoChannelSingleThreadTest, InvalidRtpHeaderExtensionIds) { + Base::TestInvalidRtpHeaderExtensionIds(); +} + +TEST_F(VideoChannelSingleThreadTest, RtpHeaderExtensionIdReassignment) { + Base::TestRtpHeaderExtensionIdReassignment(); +} + +TEST_F(VideoChannelSingleThreadTest, + TestRtpHeaderExtensionIdHistoryReassignment) { + Base::TestRtpHeaderExtensionIdHistoryReassignment(); +} + TEST_F(VideoChannelSingleThreadTest, TestSetLocalOfferWithPacketization) { const webrtc::Codec kVp8Codec = webrtc::CreateVideoCodec(97, "VP8"); webrtc::Codec vp9_codec = webrtc::CreateVideoCodec(98, "VP9"); @@ -2242,8 +2332,7 @@ TEST_F(VideoChannelSingleThreadTest, TestSetLocalOfferWithPacketization) { CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer).ok()); EXPECT_THAT(media_send_channel1_impl()->send_codecs(), testing::IsEmpty()); ASSERT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::SizeIs(2)); EXPECT_TRUE( @@ -2265,9 +2354,7 @@ TEST_F(VideoChannelSingleThreadTest, TestSetRemoteOfferWithPacketization) { CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kOffer, err)); - EXPECT_TRUE(err.empty()); + EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kOffer).ok()); EXPECT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::IsEmpty()); ASSERT_THAT(media_send_channel1_impl()->send_codecs(), testing::SizeIs(2)); EXPECT_TRUE(media_send_channel1_impl()->send_codecs()[0].Matches(kVp8Codec)); @@ -2287,11 +2374,8 @@ TEST_F(VideoChannelSingleThreadTest, TestSetAnswerWithPacketization) { CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, err)); - EXPECT_TRUE(err.empty()); - EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kAnswer, err)); - EXPECT_TRUE(err.empty()); + EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer).ok()); + EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kAnswer).ok()); ASSERT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::SizeIs(2)); EXPECT_TRUE( media_receive_channel1_impl()->recv_codecs()[0].Matches(kVp8Codec)); @@ -2321,9 +2405,8 @@ TEST_F(VideoChannelSingleThreadTest, TestSetLocalAnswerWithoutPacketization) { CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetRemoteContent(&remote_video, SdpType::kOffer, err)); - EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kAnswer, err)); + EXPECT_TRUE(channel1_->SetRemoteContent(&remote_video, SdpType::kOffer).ok()); + EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kAnswer).ok()); ASSERT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_receive_channel1_impl()->recv_codecs()[0].packetization, std::nullopt); @@ -2343,10 +2426,9 @@ TEST_F(VideoChannelSingleThreadTest, TestSetRemoteAnswerWithoutPacketization) { CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer, err)); + EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer).ok()); EXPECT_TRUE( - channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer, err)); + channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer).ok()); ASSERT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_receive_channel1_impl()->recv_codecs()[0].packetization, std::nullopt); @@ -2368,12 +2450,9 @@ TEST_F(VideoChannelSingleThreadTest, CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer, err)); - EXPECT_TRUE(err.empty()); + EXPECT_TRUE(channel1_->SetLocalContent(&local_video, SdpType::kOffer).ok()); EXPECT_FALSE( - channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer, err)); - EXPECT_FALSE(err.empty()); + channel1_->SetRemoteContent(&remote_video, SdpType::kAnswer).ok()); ASSERT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_receive_channel1_impl()->recv_codecs()[0].packetization, webrtc::kPacketizationParamRaw); @@ -2392,11 +2471,8 @@ TEST_F(VideoChannelSingleThreadTest, CreateChannels(0, 0); - std::string err; - EXPECT_TRUE(channel1_->SetRemoteContent(&remote_video, SdpType::kOffer, err)); - EXPECT_TRUE(err.empty()); - EXPECT_FALSE(channel1_->SetLocalContent(&local_video, SdpType::kAnswer, err)); - EXPECT_FALSE(err.empty()); + EXPECT_TRUE(channel1_->SetRemoteContent(&remote_video, SdpType::kOffer).ok()); + EXPECT_FALSE(channel1_->SetLocalContent(&local_video, SdpType::kAnswer).ok()); EXPECT_THAT(media_receive_channel1_impl()->recv_codecs(), testing::IsEmpty()); ASSERT_THAT(media_send_channel1_impl()->send_codecs(), testing::SizeIs(1)); EXPECT_EQ(media_send_channel1_impl()->send_codecs()[0].packetization, @@ -2418,10 +2494,8 @@ TEST_F(VideoChannelSingleThreadTest, remote.set_codecs({vp8_foo, vp9}); CreateChannels(0, 0); - std::string err; - ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer, err)) << err; - ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kAnswer, err)) - << err; + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer).ok()); + ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kAnswer).ok()); EXPECT_THAT( media_receive_channel1_impl()->recv_codecs(), @@ -2454,10 +2528,8 @@ TEST_F(VideoChannelSingleThreadTest, remote.set_codecs({vp8_foo, vp8_bar, vp9_foo}); CreateChannels(0, 0); - std::string err; - ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kOffer, err)) - << err; - ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kAnswer, err)) << err; + ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kOffer).ok()); + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kAnswer).ok()); EXPECT_THAT( media_receive_channel1_impl()->recv_codecs(), @@ -2490,10 +2562,8 @@ TEST_F(VideoChannelSingleThreadTest, remote.set_codecs({vp8_raw, vp8}); CreateChannels(0, 0); - std::string err; - ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer, err)) << err; - ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kAnswer, err)) - << err; + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer).ok()); + ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kAnswer).ok()); EXPECT_THAT(media_receive_channel1_impl()->recv_codecs(), UnorderedElementsAre( @@ -2522,10 +2592,8 @@ TEST_F(VideoChannelSingleThreadTest, remote.set_codecs({vp8, vp8_raw}); CreateChannels(0, 0); - std::string err; - ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kOffer, err)) - << err; - ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kAnswer, err)) << err; + ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kOffer).ok()); + ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kAnswer).ok()); EXPECT_THAT( media_receive_channel1_impl()->recv_codecs(), diff --git a/pc/codec_vendor.cc b/pc/codec_vendor.cc index 1d865fd7de2..cb9f65cecde 100644 --- a/pc/codec_vendor.cc +++ b/pc/codec_vendor.cc @@ -23,6 +23,7 @@ #include "absl/strings/string_view.h" #include "api/field_trials_view.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" @@ -562,7 +563,7 @@ RTCError AssignCodecIdsAndLinkRed(PayloadTypeSuggester* pt_suggester, RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); int codec_payload_type = Codec::kIdNotSet; for (Codec& codec : codecs) { - if (codec.id == Codec::kIdNotSet) { + if (codec.id == PayloadType::NotSet()) { // Add payload types to codecs, if needed // This should only happen if WebRTC-PayloadTypesInTransport field trial // is enabled. @@ -576,7 +577,7 @@ RTCError AssignCodecIdsAndLinkRed(PayloadTypeSuggester* pt_suggester, // record first Opus codec id if (absl::EqualsIgnoreCase(codec.name, kOpusCodecName) && codec_payload_type == Codec::kIdNotSet) { - codec_payload_type = codec.id; + codec_payload_type = codec.id.value(); } } if (codec_payload_type != Codec::kIdNotSet) { @@ -620,7 +621,8 @@ RTCErrorOr> CodecVendor::GetNegotiatedCodecsForOffer( CodecList codecs; std::string mid = media_description_options.mid; // If current content exists and is not being recycled, use its codecs. - if (current_content && current_content->mid() == mid) { + if (current_content && current_content->mid() == mid && + IsMediaContentOfType(current_content, media_description_options.type)) { RTCErrorOr checked_codec_list = CodecList::Create(current_content->media_description()->codecs()); if (!checked_codec_list.ok()) { @@ -658,10 +660,10 @@ RTCErrorOr> CodecVendor::GetNegotiatedCodecsForOffer( if (!IsMediaContentOfType(current_content, media_description_options.type)) { // Can happen if the remote side re-uses a MID while recycling. - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Media type for content with mid='" + - current_content->mid() + - "' does not match previous type."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Media type for content with mid='" + << current_content->mid() + << "' does not match previous type."); } const MediaContentDescription* mcd = current_content->media_description(); @@ -758,7 +760,8 @@ RTCErrorOr CodecVendor::GetNegotiatedCodecsForAnswer( RTC_LOG_THREAD_BLOCK_COUNT(); CodecList codecs; std::string mid = media_description_options.mid; - if (current_content && current_content->mid() == mid) { + if (current_content && current_content->mid() == mid && + IsMediaContentOfType(current_content, media_description_options.type)) { RTCErrorOr checked_codec_list = CodecList::Create(current_content->media_description()->codecs()); if (!checked_codec_list.ok()) { @@ -793,10 +796,10 @@ RTCErrorOr CodecVendor::GetNegotiatedCodecsForAnswer( if (!IsMediaContentOfType(current_content, media_description_options.type)) { // Can happen if the remote side re-uses a MID while recycling. - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Media type for content with mid='" + - current_content->mid() + - "' does not match previous type."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Media type for content with mid='" + << current_content->mid() + << "' does not match previous type."); } const MediaContentDescription* mcd = current_content->media_description(); diff --git a/pc/codec_vendor_unittest.cc b/pc/codec_vendor_unittest.cc index d8f88f6f584..76b4cbe676e 100644 --- a/pc/codec_vendor_unittest.cc +++ b/pc/codec_vendor_unittest.cc @@ -18,6 +18,7 @@ #include "api/environment/environment_factory.h" #include "api/field_trials.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/rtc_error.h" #include "api/rtp_transceiver_direction.h" #include "api/test/rtc_error_matchers.h" @@ -194,7 +195,7 @@ TEST(CodecVendorTest, PreferencesAffectCodecChoice) { MediaDescriptionOptions options(MediaType::VIDEO, "mid", RtpTransceiverDirection::kSendOnly, false); options.codec_preferences = { - ToRtpCodecCapability(CreateVideoCodec(-1, "vp9")), + ToRtpCodecCapability(CreateVideoCodec(PayloadType::NotSet(), "vp9")), }; FakePayloadTypeSuggester pt_suggester; diff --git a/pc/congestion_control_integrationtest.cc b/pc/congestion_control_integrationtest.cc index b712b6ba25b..4cf7793e04d 100644 --- a/pc/congestion_control_integrationtest.cc +++ b/pc/congestion_control_integrationtest.cc @@ -13,6 +13,7 @@ #include #include +#include #include #include "absl/strings/str_cat.h" @@ -31,6 +32,7 @@ namespace webrtc { +using ::testing::ContainsRegex; using ::testing::Eq; using ::testing::Field; using ::testing::Gt; @@ -45,6 +47,9 @@ class PeerConnectionCongestionControlTest : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {} }; +// This regexp matches both wildcard and non-wildcard ccfb lines. +constexpr std::string_view ccfb_regex = "a=rtcp-fb:[0-9*]* ack ccfb\r\n"; + TEST_F(PeerConnectionCongestionControlTest, OfferDoesNotContainCcfbEvenIfEnabled) { SetFieldTrials("WebRTC-RFC8888CongestionControlFeedback/Enabled/"); @@ -53,7 +58,7 @@ TEST_F(PeerConnectionCongestionControlTest, std::unique_ptr offer = caller()->CreateOfferAndWait(); std::string offer_str = absl::StrCat(*offer); - EXPECT_THAT(offer_str, Not(HasSubstr("a=rtcp-fb:* ack ccfb\r\n"))); + EXPECT_THAT(offer_str, Not(ContainsRegex(ccfb_regex))); } TEST_F(PeerConnectionCongestionControlTest, OfferContainsCcfbIfFieldTrial) { @@ -63,7 +68,7 @@ TEST_F(PeerConnectionCongestionControlTest, OfferContainsCcfbIfFieldTrial) { std::unique_ptr offer = caller()->CreateOfferAndWait(); std::string offer_str = absl::StrCat(*offer); - EXPECT_THAT(offer_str, HasSubstr("a=rtcp-fb:* ack ccfb\r\n")); + EXPECT_THAT(offer_str, ContainsRegex(ccfb_regex)); } TEST_F(PeerConnectionCongestionControlTest, ReceiveOfferSetsCcfbFlag) { diff --git a/pc/data_channel_controller.cc b/pc/data_channel_controller.cc index 7807ccb6ee0..9688699bfe8 100644 --- a/pc/data_channel_controller.cc +++ b/pc/data_channel_controller.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include "absl/algorithm/container.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/data_channel_event_observer_interface.h" #include "api/data_channel_interface.h" #include "api/priority.h" @@ -458,7 +458,7 @@ void DataChannelController::AllocateSctpSids(SSLRole role) { const bool ready_to_send = data_channel_transport_ && data_channel_transport_->IsReadyToSend(); - std::vector channels_to_start; + std::vector> channels_to_start; std::vector> channels_to_close; for (auto it = sctp_data_channels_n_.begin(); it != sctp_data_channels_n_.end();) { @@ -467,7 +467,7 @@ void DataChannelController::AllocateSctpSids(SSLRole role) { if (sid.has_value()) { (*it)->SetSctpSid_n(*sid); AddSctpDataStream(*sid, (*it)->priority()); - channels_to_start.push_back((*it).get()); + channels_to_start.push_back(*it); } else { channels_to_close.push_back(std::move(*it)); it = sctp_data_channels_n_.erase(it); @@ -479,7 +479,7 @@ void DataChannelController::AllocateSctpSids(SSLRole role) { // Since OnTransportReady can cause sending, and sending may fail and cause // channel to close, do this outside the loop. if (ready_to_send) { - for (auto* channel : channels_to_start) { + for (auto& channel : channels_to_start) { RTC_LOG(LS_INFO) << "AllocateSctpSids: Id assigned, ready to send."; channel->OnTransportReady(); } @@ -527,7 +527,7 @@ void DataChannelController::set_data_channel_transport( std::optional DataChannelController::BuildObserverMessage( StreamId sid, DataMessageType type, - ArrayView payload, + std::span payload, Message::Direction direction) const { RTC_DCHECK_RUN_ON(network_thread()); diff --git a/pc/data_channel_controller.h b/pc/data_channel_controller.h index 2553810c0cd..de9aa7b7db9 100644 --- a/pc/data_channel_controller.h +++ b/pc/data_channel_controller.h @@ -15,10 +15,10 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/data_channel_event_observer_interface.h" #include "api/data_channel_interface.h" #include "api/priority.h" @@ -154,7 +154,7 @@ class DataChannelController : public SctpDataChannelControllerInterface, BuildObserverMessage( StreamId sid, DataMessageType type, - ArrayView payload, + std::span payload, DataChannelEventObserverInterface::Message::Direction direction) const RTC_RUN_ON(network_thread()); diff --git a/pc/data_channel_integrationtest.cc b/pc/data_channel_integrationtest.cc index c3f0c78495c..cf7a5b74598 100644 --- a/pc/data_channel_integrationtest.cc +++ b/pc/data_channel_integrationtest.cc @@ -1226,20 +1226,20 @@ TEST_P(DataChannelIntegrationTest, ASSERT_THAT(WaitUntil([&] { return callee()->data_channel(); }, IsTrue()), IsRtcOk()); - auto caller_report = caller()->NewGetStats(); + auto caller_report = caller()->NewGetStats(run_loop()); ASSERT_THAT(caller_report, NotNull()); EXPECT_EQ(1u, caller_report->GetStatsOfType().size()); - auto callee_report = callee()->NewGetStats(); + auto callee_report = callee()->NewGetStats(run_loop()); ASSERT_THAT(callee_report, NotNull()); EXPECT_EQ(1u, callee_report->GetStatsOfType().size()); } TEST_P(DataChannelIntegrationTest, CreateDataChannelInvalidatesStatsCache) { ASSERT_TRUE(CreatePeerConnectionWrappers()); - auto first_report = caller()->NewGetStats(); + auto first_report = caller()->NewGetStats(run_loop()); ASSERT_THAT(first_report, NotNull()); caller()->CreateDataChannel(); - auto second_report = caller()->NewGetStats(); + auto second_report = caller()->NewGetStats(run_loop()); ASSERT_THAT(second_report, NotNull()); EXPECT_EQ(0u, first_report->GetStatsOfType().size()); @@ -1479,8 +1479,7 @@ TEST_P(DataChannelIntegrationTest, ChangingSctpPortIsNotAllowed) { caller()->CreateAndSetAndSignalOffer(); ASSERT_THAT(answer, NotNull()); - // Currently SRD succeeds. - EXPECT_TRUE(caller()->SetRemoteDescription(std::move(answer))); + EXPECT_FALSE(caller()->SetRemoteDescription(std::move(answer))); // Check the state of the SCTP transport. VerifySctpState(caller(), SctpTransportState::kClosed); } diff --git a/pc/datagram_connection_internal.cc b/pc/datagram_connection_internal.cc index afddb3aa361..38c2e432803 100644 --- a/pc/datagram_connection_internal.cc +++ b/pc/datagram_connection_internal.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/crypto/crypto_options.h" #include "api/datagram_connection.h" @@ -58,6 +58,7 @@ using PacketMetadata = DatagramConnection::Observer::PacketMetadata; const size_t kMaxRtpPacketLen = 2048; const size_t kIceUfragLength = 16; +const size_t kMinPayloadLengthForRtpCheck = 2; // Helper function to create IceTransportInit IceTransportInit CreateIceTransportInit(const Environment& env, @@ -254,7 +255,7 @@ void DatagramConnectionInternal::SetRemoteDtlsParameters( } void DatagramConnectionInternal::SendPackets( - ArrayView packets) { + std::span packets) { RTC_DCHECK_RUN_ON(&sequence_checker_); for (size_t i = 0; i < packets.size(); ++i) { SendSinglePacket(packets[i], @@ -291,7 +292,8 @@ void DatagramConnectionInternal::SendSinglePacket( return; } - if (IsRtpOrRtcpPacket(packet.payload[0])) { + if (packet.payload.size() >= kMinPayloadLengthForRtpCheck && + IsRtpOrRtcpPacket(packet.payload[0])) { // Copy the payload into a buffer with some extra capacity to allow space // for the SRTP encryption tag to be added. CopyOnWriteBuffer buffer(packet.payload.data(), packet.payload.size(), diff --git a/pc/datagram_connection_internal.h b/pc/datagram_connection_internal.h index 68705b7cba6..11aa5a64ccb 100644 --- a/pc/datagram_connection_internal.h +++ b/pc/datagram_connection_internal.h @@ -13,11 +13,11 @@ #include #include #include +#include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/datagram_connection.h" #include "api/environment/environment.h" @@ -62,7 +62,7 @@ class RTC_EXPORT DatagramConnectionInternal : public DatagramConnection, const uint8_t* digest, size_t digest_len, SSLRole ssl_role) override; - void SendPackets(ArrayView packets) override; + void SendPackets(std::span packets) override; void Terminate( absl::AnyInvocable terminate_complete_callback) override; diff --git a/pc/datagram_connection_unittest.cc b/pc/datagram_connection_unittest.cc index 37507b5fd96..a352816b388 100644 --- a/pc/datagram_connection_unittest.cc +++ b/pc/datagram_connection_unittest.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "api/candidate.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" @@ -190,7 +190,7 @@ TEST_F(DatagramConnectionTest, ObserverCalledOnReceivedRtpPacket) { bool callback_called = false; EXPECT_CALL(*observer1_ptr_, OnPacketReceived(_, _)) .WillOnce( - [&](ArrayView data, + [&](std::span data, const DatagramConnection::Observer::PacketMetadata& metadata) { EXPECT_EQ(data.size(), packet_data.size()); EXPECT_EQ( @@ -256,7 +256,7 @@ TEST_F(DatagramConnectionTest, RtpPacketsAreReceived) { EXPECT_CALL(*observer2_ptr_, OnPacketReceived(_, _)) .WillOnce( - [&](ArrayView received_data, + [&](std::span received_data, const DatagramConnection::Observer::PacketMetadata& metadata) { EXPECT_EQ(received_data.size(), data.size()); EXPECT_EQ(memcmp(received_data.data(), data.data(), data.size()), @@ -319,7 +319,7 @@ TEST_F(DatagramConnectionTest, SendMultipleRtpPackets) { EXPECT_CALL(*observer2_ptr_, OnPacketReceived(_, _)) .Times(data.size()) .WillRepeatedly( - [&](ArrayView received_data, + [&](std::span received_data, const DatagramConnection::Observer::PacketMetadata& metadata) { received_packets.emplace_back(received_data.begin(), received_data.end()); @@ -395,7 +395,7 @@ TEST_F(DatagramConnectionTest, NonRtpPacketsInSRTPModeAreDTLSProtected) { bool callback_called = false; EXPECT_CALL(*observer2_ptr_, OnPacketReceived(_, _)) .WillOnce( - [&](ArrayView received_data, + [&](std::span received_data, const DatagramConnection::Observer::PacketMetadata& metadata) { // Check the data is decrypted correctly. EXPECT_EQ(received_data.size(), non_rtp_data.size()); @@ -490,7 +490,7 @@ TEST_F(DatagramConnectionTest, DirectDtlsPacketsAreReceived) { EXPECT_CALL(*observer2_ptr_, OnPacketReceived(_, _)) .WillOnce( - [&](ArrayView received_data, + [&](std::span received_data, const DatagramConnection::Observer::PacketMetadata& metadata) { EXPECT_EQ(received_data.size(), data.size()); EXPECT_EQ(memcmp(received_data.data(), data.data(), data.size()), @@ -517,5 +517,31 @@ TEST_F(DatagramConnectionTest, DirectDtlsPacketsAreReceived) { EXPECT_TRUE(callbacks.send_outcome); } +TEST_F(DatagramConnectionTest, SingleBytePacketsAreSent) { + CreateConnections(WireProtocol::kDtls); + Connect(); + ASSERT_TRUE( + WaitUntil([&]() { return conn1_->Writable() && conn2_->Writable(); })); + + std::vector data = {1}; + bool callback_called = false; + EXPECT_CALL(*observer1_ptr_, OnSendOutcome(_)) + .WillOnce([&](const SendOutcome& outcome) { + EXPECT_EQ(outcome.id, 1u); + EXPECT_EQ(outcome.status, SendOutcome::Status::kSuccess); + EXPECT_NE(outcome.send_time, Timestamp::MinusInfinity()); + callback_called = true; + loop_.Quit(); + }); + std::vector packets = { + PacketSendParameters{.id = 1, .payload = data}}; + conn1_->SendPackets(packets); + // For direct DTLS, the sent packet should be larger than the data due to + // DTLS overhead. + EXPECT_GT(ice1_->last_sent_packet().size(), data.size()); + loop_.Run(); + EXPECT_TRUE(callback_called); +} + } // namespace } // namespace webrtc diff --git a/pc/dtls_srtp_transport.cc b/pc/dtls_srtp_transport.cc index 7bc834c78a6..5fd1636be63 100644 --- a/pc/dtls_srtp_transport.cc +++ b/pc/dtls_srtp_transport.cc @@ -10,6 +10,7 @@ #include "pc/dtls_srtp_transport.h" +#include #include #include #include @@ -240,9 +241,6 @@ bool DtlsSrtpTransport::ExtractParams(DtlsTransportInternal* dtls_transport, return false; } - RTC_LOG(LS_INFO) << "Extracting keys from transport: " - << dtls_transport->transport_name(); - int key_len; int salt_len; if (!GetSrtpKeyAndSaltLengths((*selected_crypto_suite), &key_len, @@ -252,16 +250,19 @@ bool DtlsSrtpTransport::ExtractParams(DtlsTransportInternal* dtls_transport, return false; } - // OK, we're now doing DTLS (RFC 5764) - auto dtls_buffer = ZeroOnFreeBuffer::CreateUninitializedWithSize( - key_len * 2 + salt_len * 2); + RTC_LOG(LS_INFO) << "Extracting keys from transport: " + << dtls_transport->transport_name(); // RFC 5705 exporter using the RFC 5764 parameters - if (!dtls_transport->ExportSrtpKeyingMaterial(dtls_buffer)) { + ZeroOnFreeBuffer dtls_buffer; + if (!dtls_transport->AppendSrtpKeyingMaterial(dtls_buffer)) { RTC_LOG(LS_ERROR) << "DTLS-SRTP key export failed"; RTC_DCHECK_NOTREACHED(); // This should never happen return false; } + // Verify that key material size is as expected. + RTC_DCHECK_EQ(dtls_buffer.size(), + static_cast(2 * key_len + 2 * salt_len)); // Sync up the keys with the DTLS-SRTP interface // https://datatracker.ietf.org/doc/html/rfc5764#section-4.2 diff --git a/pc/dtls_srtp_transport_integrationtest.cc b/pc/dtls_srtp_transport_integrationtest.cc index 7199894d3b7..4c3144a9c35 100644 --- a/pc/dtls_srtp_transport_integrationtest.cc +++ b/pc/dtls_srtp_transport_integrationtest.cc @@ -40,10 +40,10 @@ #include "rtc_base/ssl_fingerprint.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/thread.h" #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -130,15 +130,15 @@ class DtlsSrtpTransportIntegrationTest : public ::testing::Test { client_ice_transport_->SetDestination(server_ice_transport_.get()); // Wait for the DTLS connection to be up. - EXPECT_THAT(WaitUntil( - [&] { - return client_dtls_transport_->writable() && - server_dtls_transport_->writable(); - }, - IsTrue(), - {.timeout = TimeDelta::Millis(kTimeout), - .clock = &fake_clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil( + [&] { + return client_dtls_transport_->writable() && + server_dtls_transport_->writable(); + }, + IsTrue(), + {.timeout = TimeDelta::Millis(kTimeout), .clock = &fake_clock_}), + IsRtcOk()); EXPECT_EQ(client_dtls_transport_->dtls_state(), DtlsTransportState::kConnected); EXPECT_EQ(server_dtls_transport_->dtls_state(), @@ -156,10 +156,8 @@ class DtlsSrtpTransportIntegrationTest : public ::testing::Test { GetSrtpKeyAndSaltLengths((selected_crypto_suite), &key_len, &salt_len)); // Extract the keys. The order depends on the role! - ZeroOnFreeBuffer dtls_buffer = - ZeroOnFreeBuffer::CreateUninitializedWithSize(key_len * 2 + - salt_len * 2); - ASSERT_TRUE(server_dtls_transport_->ExportSrtpKeyingMaterial(dtls_buffer)); + ZeroOnFreeBuffer dtls_buffer; + ASSERT_TRUE(server_dtls_transport_->AppendSrtpKeyingMaterial(dtls_buffer)); ZeroOnFreeBuffer client_write_key(&dtls_buffer[0], key_len, key_len + salt_len); @@ -222,7 +220,7 @@ class DtlsSrtpTransportIntegrationTest : public ::testing::Test { } private: - AutoThread main_thread_; + test::RunLoop main_thread_; ScopedFakeClock fake_clock_; const Environment env_ = CreateTestEnvironment(); diff --git a/pc/dtls_srtp_transport_unittest.cc b/pc/dtls_srtp_transport_unittest.cc index d6e259a47ba..ab58a8c6a1b 100644 --- a/pc/dtls_srtp_transport_unittest.cc +++ b/pc/dtls_srtp_transport_unittest.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -37,9 +38,9 @@ #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_identity.h" -#include "rtc_base/thread.h" #include "test/create_test_field_trials.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { namespace { @@ -137,7 +138,7 @@ class DtlsSrtpTransportTest : public ::testing::Test { memcpy(rtp_packet_data, kPcmuFrame, rtp_len); // In order to be able to run this test function multiple times we can not // use the same sequence number twice. Increase the sequence number by one. - SetBE16(reinterpret_cast(rtp_packet_data) + 2, + SetBE16(std::span(rtp_packet_buffer).subspan(2), ++sequence_number_); CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len, packet_size); CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len, packet_size); @@ -210,7 +211,7 @@ class DtlsSrtpTransportTest : public ::testing::Test { memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len); // In order to be able to run this test function multiple times we can not // use the same sequence number twice. Increase the sequence number by one. - SetBE16(reinterpret_cast(rtp_packet_data) + 2, + SetBE16(std::span(rtp_packet_buffer).subspan(2), ++sequence_number_); CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len, packet_size); CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len, packet_size); @@ -266,7 +267,7 @@ class DtlsSrtpTransportTest : public ::testing::Test { SendRecvRtcpPackets(); } - AutoThread main_thread_; + test::RunLoop main_thread_; std::unique_ptr dtls_srtp_transport1_; std::unique_ptr dtls_srtp_transport2_; TransportObserver transport_observer1_; diff --git a/pc/dtls_transport_unittest.cc b/pc/dtls_transport_unittest.cc index 480357b71c8..44e17d46b93 100644 --- a/pc/dtls_transport_unittest.cc +++ b/pc/dtls_transport_unittest.cc @@ -25,9 +25,9 @@ #include "rtc_base/fake_ssl_identity.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_identity.h" -#include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -99,7 +99,7 @@ class DtlsTransportTest : public ::testing::Test { fake_dtls1->SetDestination(fake_dtls2.get()); } - AutoThread main_thread_; + test::RunLoop main_thread_; scoped_refptr transport_; std::unique_ptr internal_transport_; TestDtlsTransportObserver observer_; diff --git a/pc/dtmf_sender_unittest.cc b/pc/dtmf_sender_unittest.cc index 9c62cc151e6..7e4063cad03 100644 --- a/pc/dtmf_sender_unittest.cc +++ b/pc/dtmf_sender_unittest.cc @@ -20,11 +20,11 @@ #include "api/scoped_refptr.h" #include "api/test/rtc_error_matchers.h" #include "api/units/time_delta.h" -#include "rtc_base/fake_clock.h" -#include "rtc_base/thread.h" +#include "api/units/timestamp.h" #include "rtc_base/time_utils.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" using webrtc::DtmfProviderInterface; @@ -116,9 +116,12 @@ class FakeDtmfProvider : public DtmfProviderInterface { class DtmfSenderTest : public ::testing::Test { protected: DtmfSenderTest() - : observer_(new FakeDtmfObserver()), provider_(new FakeDtmfProvider()) { + : time_controller_(webrtc::Timestamp::Seconds(1)), + observer_(new FakeDtmfObserver()), + provider_(new FakeDtmfProvider()) { provider_->SetCanInsertDtmf(true); - dtmf_ = DtmfSender::Create(webrtc::Thread::Current(), provider_.get()); + dtmf_ = + DtmfSender::Create(time_controller_.GetMainThread(), provider_.get()); dtmf_->RegisterObserver(observer_.get()); } @@ -211,11 +214,10 @@ class DtmfSenderTest : public ::testing::Test { } } - webrtc::AutoThread main_thread_; + webrtc::GlobalSimulatedTimeController time_controller_; std::unique_ptr observer_; std::unique_ptr provider_; webrtc::scoped_refptr dtmf_; - webrtc::ScopedFakeClock fake_clock_; }; TEST_F(DtmfSenderTest, CanInsertDtmf) { @@ -232,7 +234,7 @@ TEST_F(DtmfSenderTest, InsertDtmf) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->completed(); }, ::testing::IsTrue(), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); // The unrecognized characters should be ignored. @@ -252,7 +254,7 @@ TEST_F(DtmfSenderTest, InsertDtmfTwice) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->tones().size(); }, ::testing::Eq(1), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); VerifyExpectedState("2", duration, inter_tone_gap); // Insert with another tone buffer. @@ -262,7 +264,7 @@ TEST_F(DtmfSenderTest, InsertDtmfTwice) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->completed(); }, ::testing::IsTrue(), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); std::vector dtmf_queue_ref; @@ -281,13 +283,13 @@ TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->tones().size(); }, ::testing::Eq(1), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); // Delete provider. dtmf_->OnDtmfProviderDestroyed(); provider_.reset(); // The queue should be discontinued so no more tone callbacks. - fake_clock_.AdvanceTime(webrtc::TimeDelta::Millis(200)); + time_controller_.AdvanceTime(webrtc::TimeDelta::Millis(200)); EXPECT_EQ(1U, observer_->tones().size()); } @@ -300,12 +302,12 @@ TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->tones().size(); }, ::testing::Eq(1), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); // Delete the sender. dtmf_ = nullptr; // The queue should be discontinued so no more tone callbacks. - fake_clock_.AdvanceTime(webrtc::TimeDelta::Millis(200)); + time_controller_.AdvanceTime(webrtc::TimeDelta::Millis(200)); EXPECT_EQ(1U, observer_->tones().size()); } @@ -319,7 +321,7 @@ TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->tones().size(); }, ::testing::Eq(1), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); // Insert with another tone buffer. EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap)); @@ -327,7 +329,7 @@ TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->completed(); }, ::testing::IsTrue(), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); std::vector dtmf_queue_ref; @@ -346,7 +348,7 @@ TEST_F(DtmfSenderTest, InsertDtmfWithDefaultCommaDelay) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->completed(); }, ::testing::IsTrue(), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); VerifyOnProvider(tones, duration, inter_tone_gap); @@ -365,7 +367,7 @@ TEST_F(DtmfSenderTest, InsertDtmfWithNonDefaultCommaDelay) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->completed(); }, ::testing::IsTrue(), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); VerifyOnProvider(tones, duration, inter_tone_gap, comma_delay); @@ -404,7 +406,7 @@ TEST_F(DtmfSenderTest, InsertDtmfSendsAfterWait) { EXPECT_THAT(webrtc::WaitUntil( [&] { return observer_->tones().size(); }, ::testing::Eq(1), {.timeout = webrtc::TimeDelta::Millis(kMaxWaitMs), - .clock = &fake_clock_}), + .clock = &time_controller_}), webrtc::IsRtcOk()); VerifyExpectedState("BC", duration, inter_tone_gap); } diff --git a/pc/external_hmac.cc b/pc/external_hmac.cc deleted file mode 100644 index 805b9969946..00000000000 --- a/pc/external_hmac.cc +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "pc/external_hmac.h" - -#include -#include -#include - -#include "rtc_base/logging.h" -#include "rtc_base/zero_memory.h" -#include "third_party/libsrtp/crypto/include/auth.h" -#include "third_party/libsrtp/include/srtp.h" - -// Begin test case 0 */ -static const uint8_t kExternalHmacTestCase0Key[20] = { - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}; - -static const uint8_t kExternalHmacTestCase0Data[8] = { - 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 // "Hi There" -}; - -static const uint8_t kExternalHmacFakeTag[10] = {0xba, 0xdd, 0xba, 0xdd, 0xba, - 0xdd, 0xba, 0xdd, 0xba, 0xdd}; - -static const srtp_auth_test_case_t kExternalHmacTestCase0 = { - .key_length_octets = 20, // Octets in key - .key = const_cast(kExternalHmacTestCase0Key), // Key - .data_length_octets = 8, // Octets in data - .data = const_cast(kExternalHmacTestCase0Data), // Data - .tag_length_octets = 10, // Octets in tag - .tag = const_cast(kExternalHmacFakeTag), // Tag - .next_test_case = nullptr // Pointer to next - // testcase -}; - -static const char kExternalHmacDescription[] = - "external hmac sha-1 authentication"; - -// srtp_auth_type_t external_hmac is the hmac metaobject - -static const srtp_auth_type_t external_hmac = { - .alloc = external_hmac_alloc, - .dealloc = external_hmac_dealloc, - .init = external_hmac_init, - .compute = external_hmac_compute, - .update = external_hmac_update, - .start = external_hmac_start, - .description = const_cast(kExternalHmacDescription), - .test_data = const_cast(&kExternalHmacTestCase0), - .id = EXTERNAL_HMAC_SHA1}; - -srtp_err_status_t external_hmac_alloc(srtp_auth_t** a, - int key_len, - int out_len) { - uint8_t* pointer; - - // Check key length - note that we don't support keys larger - // than 20 bytes yet - if (key_len > 20) - return srtp_err_status_bad_param; - - // Check output length - should be less than 20 bytes/ - if (out_len > 20) - return srtp_err_status_bad_param; - - // Allocate memory for auth and hmac_ctx_t structures. - pointer = new uint8_t[(sizeof(ExternalHmacContext) + sizeof(srtp_auth_t))]; - if (pointer == nullptr) - return srtp_err_status_alloc_fail; - - // Set pointers - *a = reinterpret_cast(pointer); - // `external_hmac` is const and libsrtp expects `type` to be non-const. - // const conversion is required. `external_hmac` is constant because we don't - // want to increase global count in Chrome. - (*a)->type = const_cast(&external_hmac); - (*a)->state = pointer + sizeof(srtp_auth_t); - (*a)->out_len = out_len; - (*a)->key_len = key_len; - (*a)->prefix_len = 0; - - return srtp_err_status_ok; -} - -srtp_err_status_t external_hmac_dealloc(srtp_auth_t* a) { - webrtc::ExplicitZeroMemory(a, - sizeof(ExternalHmacContext) + sizeof(srtp_auth_t)); - - // Free memory - delete[] a; - - return srtp_err_status_ok; -} - -srtp_err_status_t external_hmac_init(void* state, - const uint8_t* key, - int key_len) { - if (key_len > HMAC_KEY_LENGTH) - return srtp_err_status_bad_param; - - ExternalHmacContext* context = static_cast(state); - memcpy(context->key, key, key_len); - context->key_length = key_len; - return srtp_err_status_ok; -} - -srtp_err_status_t external_hmac_start(void* /*state*/) { - return srtp_err_status_ok; -} - -srtp_err_status_t external_hmac_update(void* /*state*/, - const uint8_t* /*message*/, - int /*msg_octets*/) { - return srtp_err_status_ok; -} - -srtp_err_status_t external_hmac_compute(void* /*state*/, - const uint8_t* /*message*/, - int /*msg_octets*/, - int tag_len, - uint8_t* result) { - memcpy(result, kExternalHmacFakeTag, tag_len); - return srtp_err_status_ok; -} - -srtp_err_status_t external_crypto_init() { - // `external_hmac` is const. const_cast is required as libsrtp expects - // non-const. - srtp_err_status_t status = srtp_replace_auth_type( - const_cast(&external_hmac), EXTERNAL_HMAC_SHA1); - if (status) { - RTC_LOG(LS_ERROR) << "Error in replacing default auth module, error: " - << status; - return srtp_err_status_fail; - } - return srtp_err_status_ok; -} diff --git a/pc/external_hmac.h b/pc/external_hmac.h deleted file mode 100644 index 7dec6360dfe..00000000000 --- a/pc/external_hmac.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef PC_EXTERNAL_HMAC_H_ -#define PC_EXTERNAL_HMAC_H_ - -// External libsrtp HMAC auth module which implements methods defined in -// auth_type_t. -// The default auth module will be replaced only when the ENABLE_EXTERNAL_AUTH -// flag is enabled. This allows us to access to authentication keys, -// as the default auth implementation doesn't provide access and avoids -// hashing each packet twice. - -// How will libsrtp select this module? -// Libsrtp defines authentication function types identified by an unsigned -// integer, e.g. SRTP_HMAC_SHA1 is 3. Using authentication ids, the -// application can plug any desired authentication modules into libsrtp. -// libsrtp also provides a mechanism to select different auth functions for -// individual streams. This can be done by setting the right value in -// the auth_type of srtp_policy_t. The application must first register auth -// functions and the corresponding authentication id using -// crypto_kernel_replace_auth_type function. - -#include - -#include "third_party/libsrtp/crypto/include/auth.h" -#include "third_party/libsrtp/crypto/include/crypto_types.h" -#include "third_party/libsrtp/include/srtp.h" - -#define EXTERNAL_HMAC_SHA1 SRTP_HMAC_SHA1 + 1 -#define HMAC_KEY_LENGTH 20 - -// The HMAC context structure used to store authentication keys. -// The pointer to the key will be allocated in the external_hmac_init function. -// This pointer is owned by srtp_t in a template context. -struct ExternalHmacContext { - uint8_t key[HMAC_KEY_LENGTH]; - int key_length; -}; - -srtp_err_status_t external_hmac_alloc(srtp_auth_t** a, - int key_len, - int out_len); - -srtp_err_status_t external_hmac_dealloc(srtp_auth_t* a); - -srtp_err_status_t external_hmac_init(void* state, - const uint8_t* key, - int key_len); - -srtp_err_status_t external_hmac_start(void* state); - -srtp_err_status_t external_hmac_update(void* state, - const uint8_t* message, - int msg_octets); - -srtp_err_status_t external_hmac_compute(void* state, - const uint8_t* message, - int msg_octets, - int tag_len, - uint8_t* result); - -srtp_err_status_t external_crypto_init(); - -#endif // PC_EXTERNAL_HMAC_H_ diff --git a/pc/ice_server_parsing.cc b/pc/ice_server_parsing.cc index 15ecfe3367c..e2b87268fc8 100644 --- a/pc/ice_server_parsing.cc +++ b/pc/ice_server_parsing.cc @@ -10,6 +10,7 @@ #include "pc/ice_server_parsing.h" +#include #include #include #include @@ -52,7 +53,8 @@ const char kRegNameCharacters[] = "!$&'()*+,;="; // sub-delims // NOTE: Must be in the same order as the ServiceType enum. -const char* kValidIceServiceTypes[] = {"stun", "stuns", "turn", "turns"}; +constexpr std::array kValidIceServiceTypes{ + "stun", "stuns", "turn", "turns"}; // NOTE: A loop below assumes that the first value of this enum is 0 and all // other values are incremental. @@ -188,36 +190,37 @@ RTCError ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, std::vector tokens = split(url, '?'); absl::string_view uri_without_transport = tokens[0]; // Let's look into transport= param, if it exists. + bool transport_given_explicitly = false; if (tokens.size() == kTurnTransportTokensNum) { // ?transport= is present. std::vector transport_tokens = split(tokens[1], '='); if (transport_tokens[0] != kTransport) { - LOG_AND_RETURN_ERROR( - RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Invalid transport parameter key."); + return LOG_ERROR( + RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Invalid transport parameter key."); } if (transport_tokens.size() < 2) { - LOG_AND_RETURN_ERROR( - RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Transport parameter missing value."); + return LOG_ERROR( + RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Transport parameter missing value."); } std::optional proto = StringToProto(transport_tokens[1]); if (!proto || (*proto != PROTO_UDP && *proto != PROTO_TCP)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Transport parameter should " - "always be udp or tcp."); + return LOG_ERROR(RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Transport parameter " + "should always be udp or tcp."); } turn_transport_type = *proto; + transport_given_explicitly = true; } auto [service_type, hoststring] = GetServiceTypeAndHostnameFromUri(uri_without_transport); if (service_type == ServiceType::INVALID) { RTC_LOG(LS_ERROR) << "Invalid transport parameter in ICE URI: " << url; - LOG_AND_RETURN_ERROR( - RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Invalid transport parameter in ICE URI"); + return LOG_ERROR( + RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Invalid transport parameter in ICE URI"); } // GetServiceTypeAndHostnameFromUri should never give an empty hoststring @@ -227,37 +230,44 @@ RTCError ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, if ((service_type == ServiceType::STUN || service_type == ServiceType::STUNS) && tokens.size() > 1) { - LOG_AND_RETURN_ERROR( - RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Invalid stun url with query parameters"); + return LOG_ERROR( + RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Invalid stun url with query parameters"); } int default_port = kDefaultStunPort; if (service_type == ServiceType::TURNS) { default_port = kDefaultStunTlsPort; turn_transport_type = PROTO_TLS; + // When transport is given explicitly, the default transport is not + // specified in the RFC, but it's long-standing behavior to use TLS. + if (!transport_given_explicitly || turn_transport_type == PROTO_TCP) { + turn_transport_type = PROTO_TLS; + } else if (turn_transport_type == PROTO_UDP) { + turn_transport_type = PROTO_DTLS; + } } if (hoststring.find('@') != absl::string_view::npos) { RTC_LOG(LS_ERROR) << "Invalid url with long deprecated user@host syntax: " << uri_without_transport; - LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Invalid url with long " - "deprecated user@host syntax"); + return LOG_ERROR(RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Invalid url with long " + "deprecated user@host syntax"); } auto [success, address, port] = ParseHostnameAndPortFromString(hoststring, default_port); if (!success) { RTC_LOG(LS_ERROR) << "Invalid hostname format: " << uri_without_transport; - LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Invalid hostname format"); + return LOG_ERROR(RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Invalid hostname format"); } if (port <= 0 || port > 0xffff) { RTC_LOG(LS_ERROR) << "Invalid port: " << port; - LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Invalid port"); + return LOG_ERROR(RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Invalid port"); } switch (service_type) { @@ -270,16 +280,15 @@ RTCError ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, if (server.username.empty() || server.password.empty()) { // The WebRTC spec requires throwing an InvalidAccessError when username // or credential are ommitted; this is the native equivalent. - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "ICE server parsing failed: TURN server with empty " - "username or password"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "ICE server parsing failed: TURN server with empty " + "username or password"); } // RFC 8489 limits the size of the STUN username field to 509 characters. if (server.username.size() > kMaxTurnUsernameLength) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "ICE server parsing failed: TURN server username is too long"); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "ICE server parsing failed: TURN server username is too long"); } // If the hostname field is not empty, then the server address must be // the resolved IP for that host, the hostname is needed later for TLS @@ -292,11 +301,10 @@ RTCError ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, if (!IPFromString(address, &ip)) { // When hostname is set, the server address must be a // resolved ip address. - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "ICE server parsing failed: " - "IceServer has hostname field set, but URI does not " - "contain an IP address."); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "ICE server parsing failed: IceServer has hostname field " + "set, but URI does not contain an IP address."); } socket_address.SetResolvedIP(ip); } @@ -317,9 +325,8 @@ RTCError ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, default: // We shouldn't get to this point with an invalid service_type, we should // have returned an error already. - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "ICE server parsing failed: Unexpected service type"); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "ICE server parsing failed: Unexpected service type"); } return RTCError::OK(); } @@ -334,8 +341,8 @@ RTCError ParseIceServersOrError( if (!server.urls.empty()) { for (const std::string& url : server.urls) { if (url.empty()) { - LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Empty uri."); + return LOG_ERROR(RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Empty uri."); } RTCError err = ParseIceServerUrl(server, url, stun_servers, turn_servers); @@ -352,8 +359,8 @@ RTCError ParseIceServersOrError( return err; } } else { - LOG_AND_RETURN_ERROR(RTCErrorType::SYNTAX_ERROR, - "ICE server parsing failed: Empty uri."); + return LOG_ERROR(RTCError(RTCErrorType::SYNTAX_ERROR) + << "ICE server parsing failed: Empty uri."); } } return RTCError::OK(); diff --git a/pc/ice_transport_unittest.cc b/pc/ice_transport_unittest.cc index 91d04a06c48..9b91e86e7e6 100644 --- a/pc/ice_transport_unittest.cc +++ b/pc/ice_transport_unittest.cc @@ -22,9 +22,9 @@ #include "p2p/test/fake_port_allocator.h" #include "rtc_base/internal/default_socket_server.h" #include "rtc_base/socket_server.h" -#include "rtc_base/thread.h" #include "test/create_test_environment.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { @@ -38,7 +38,7 @@ class IceTransportTest : public ::testing::Test { private: std::unique_ptr socket_server_; - AutoSocketServerThread main_thread_; + test::RunLoop main_thread_; }; TEST_F(IceTransportTest, CreateNonSelfDeletingTransport) { diff --git a/pc/jitter_buffer_delay.cc b/pc/jitter_buffer_delay.cc index 7e8c3a55931..60c38256c37 100644 --- a/pc/jitter_buffer_delay.cc +++ b/pc/jitter_buffer_delay.cc @@ -16,13 +16,13 @@ #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/numerics/safe_minmax.h" +namespace webrtc { + namespace { constexpr int kDefaultDelay = 0; constexpr int kMaximumDelayMs = 10000; } // namespace -namespace webrtc { - void JitterBufferDelay::Set(std::optional delay_seconds) { RTC_DCHECK_RUN_ON(&worker_thread_checker_); cached_delay_seconds_ = delay_seconds; diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc index c1972c6bf25..415aa507b9f 100644 --- a/pc/jsep_transport.cc +++ b/pc/jsep_transport.cc @@ -13,13 +13,13 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/dtls_transport_interface.h" #include "api/ice_transport_interface.h" @@ -57,18 +57,15 @@ JsepTransportDescription::JsepTransportDescription() {} JsepTransportDescription::JsepTransportDescription( bool rtcp_mux_enabled, const std::vector& encrypted_header_extension_ids, - int rtp_abs_sendtime_extn_id, const TransportDescription& transport_desc) : rtcp_mux_enabled(rtcp_mux_enabled), encrypted_header_extension_ids(encrypted_header_extension_ids), - rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id), transport_desc(transport_desc) {} JsepTransportDescription::JsepTransportDescription( const JsepTransportDescription& from) : rtcp_mux_enabled(from.rtcp_mux_enabled), encrypted_header_extension_ids(from.encrypted_header_extension_ids), - rtp_abs_sendtime_extn_id(from.rtp_abs_sendtime_extn_id), transport_desc(from.transport_desc) {} JsepTransportDescription::~JsepTransportDescription() = default; @@ -80,7 +77,6 @@ JsepTransportDescription& JsepTransportDescription::operator=( } rtcp_mux_enabled = from.rtcp_mux_enabled; encrypted_header_extension_ids = from.encrypted_header_extension_ids; - rtp_abs_sendtime_extn_id = from.rtp_abs_sendtime_extn_id; transport_desc = from.transport_desc; return *this; @@ -222,8 +218,6 @@ RTCError JsepTransport::SetRemoteJsepTransportDescription( if (auto* dtls_srtp_transport = rtp_transport_->AsDtlsSrtpTransport()) { dtls_srtp_transport->UpdateSendEncryptedHeaderExtensionIds( jsep_description.encrypted_header_extension_ids); - dtls_srtp_transport->CacheRtpAbsSendTimeHeaderExtension( - jsep_description.rtp_abs_sendtime_extn_id); } remote_description_.reset(new JsepTransportDescription(jsep_description)); @@ -439,7 +433,7 @@ RTCError JsepTransport::NegotiateAndSetDtlsParameters( } else { // We are not doing DTLS remote_fingerprint = - std::make_unique("", ArrayView()); + std::make_unique("", std::span()); } // Now that we have negotiated everything, push it downward. // Note that we cache the result so that if we have race conditions diff --git a/pc/jsep_transport.h b/pc/jsep_transport.h index 6475a271774..9ec919b9b72 100644 --- a/pc/jsep_transport.h +++ b/pc/jsep_transport.h @@ -48,7 +48,6 @@ struct JsepTransportDescription { JsepTransportDescription( bool rtcp_mux_enabled, const std::vector& encrypted_header_extension_ids, - int rtp_abs_sendtime_extn_id, const TransportDescription& transport_description); JsepTransportDescription(const JsepTransportDescription& from); ~JsepTransportDescription(); @@ -57,7 +56,6 @@ struct JsepTransportDescription { bool rtcp_mux_enabled = true; std::vector encrypted_header_extension_ids; - int rtp_abs_sendtime_extn_id = -1; // TODO(zhihuang): Add the ICE and DTLS related variables and methods from // TransportDescription and remove this extra layer of abstraction. TransportDescription transport_desc; diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index 61dfe73de79..4d2126ebd34 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -421,8 +421,8 @@ RTCError JsepTransportController::RollbackTransports() { RTCError JsepTransportController::RollbackTransports_n() { bundles_.Rollback(); if (!transports_.RollbackTransports()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to roll back transport state."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to roll back transport state."); } return RTCError::OK(); } @@ -554,9 +554,6 @@ JsepTransportController::CreateDtlsSrtpTransport( RTC_DCHECK_RUN_ON(network_thread_); auto dtls_srtp_transport = std::make_unique( rtcp_dtls_transport == nullptr, env_.field_trials()); - if (config_.enable_external_auth) { - dtls_srtp_transport->EnableExternalAuth(); - } dtls_srtp_transport->SetDtlsTransportsOwned(std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport)); @@ -704,10 +701,9 @@ RTCError JsepTransportController::ApplyDescription_n( JsepTransport* transport = GetJsepTransportForMid(content_info.mid()); if (!transport) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Could not find transport for m= section with mid='" + - content_info.mid() + "'"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Could not find transport for m= section with mid='" + << content_info.mid() << "'"); } if (established_bundle_group && @@ -734,13 +730,10 @@ RTCError JsepTransportController::ApplyDescription_n( extension_ids = GetEncryptedHeaderExtensionIds(content_info); } - int rtp_abs_sendtime_extn_id = - GetRtpAbsSendTimeHeaderExtensionId(content_info); - SetIceRole_n(DetermineIceRole(transport, transport_info, type, local)); JsepTransportDescription jsep_description = CreateJsepTransportDescription( - content_info, transport_info, extension_ids, rtp_abs_sendtime_extn_id); + content_info, transport_info, extension_ids); if (local) { error = transport->SetLocalJsepTransportDescription(jsep_description, type); @@ -750,10 +743,10 @@ RTCError JsepTransportController::ApplyDescription_n( } if (!error.ok()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Failed to apply the description for m= section with mid='" + - content_info.mid() + "': " + error.message()); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "Failed to apply the description for m= section with mid='" + << content_info.mid() << "': " << error.message()); } } if (type == SdpType::kAnswer) { @@ -1006,8 +999,7 @@ JsepTransportDescription JsepTransportController::CreateJsepTransportDescription( const ContentInfo& content_info, const TransportInfo& transport_info, - const std::vector& encrypted_extension_ids, - int rtp_abs_sendtime_extn_id) { + const std::vector& encrypted_extension_ids) { TRACE_EVENT0("webrtc", "JsepTransportController::CreateJsepTransportDescription"); const MediaContentDescription* content_desc = @@ -1018,7 +1010,6 @@ JsepTransportController::CreateJsepTransportDescription( : content_desc->rtcp_mux(); return JsepTransportDescription(rtcp_mux_enabled, encrypted_extension_ids, - rtp_abs_sendtime_extn_id, transport_info.description); } @@ -1070,24 +1061,6 @@ JsepTransportController::MergeEncryptedHeaderExtensionIdsForBundles( return merged_encrypted_extension_ids_by_bundle; } -int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId( - const ContentInfo& content_info) { - if (!config_.enable_external_auth) { - return -1; - } - - const MediaContentDescription* content_desc = - content_info.media_description(); - - const RtpExtension* send_time_extension = - RtpExtension::FindHeaderExtensionByUri( - content_desc->rtp_header_extensions(), RtpExtension::kAbsSendTimeUri, - config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions - ? RtpExtension::kPreferEncryptedExtension - : RtpExtension::kDiscardEncryptedExtension); - return send_time_extension ? send_time_extension->id : -1; -} - const JsepTransport* JsepTransportController::GetJsepTransportForMid( absl::string_view mid) const { return transports_.GetTransportForMid(mid); diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index 260cc34ee9c..e2c95b35610 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -107,7 +107,6 @@ class JsepTransportController final { PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire; bool disable_encryption = false; - bool enable_external_auth = false; // Used to inject the ICE/DTLS/SRTP transports created externally. IceTransportFactory* ice_transport_factory = nullptr; DtlsTransportFactory* dtls_transport_factory = nullptr; @@ -317,8 +316,7 @@ class JsepTransportController final { JsepTransportDescription CreateJsepTransportDescription( const ContentInfo& content_info, const TransportInfo& transport_info, - const std::vector& encrypted_extension_ids, - int rtp_abs_sendtime_extn_id); + const std::vector& encrypted_extension_ids); std::map> MergeEncryptedHeaderExtensionIdsForBundles( @@ -326,8 +324,6 @@ class JsepTransportController final { std::vector GetEncryptedHeaderExtensionIds( const ContentInfo& content_info); - int GetRtpAbsSendTimeHeaderExtensionId(const ContentInfo& content_info); - // This method takes the BUNDLE group into account. If the JsepTransport is // destroyed because of BUNDLE, it would return the transport which other // transports are bundled on (In current implementation, it is the first diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc index e271753e248..136d2e4d720 100644 --- a/pc/jsep_transport_controller_unittest.cc +++ b/pc/jsep_transport_controller_unittest.cc @@ -67,6 +67,7 @@ #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -357,7 +358,7 @@ class JsepTransportControllerTest : public JsepTransportController::Observer, FieldTrials field_trials_ = CreateTestFieldTrials(); Environment env_; - AutoThread main_thread_; + test::RunLoop main_thread_; // Information received from signals from transport controller. IceConnectionState connection_state_ = kIceConnectionConnecting; PeerConnectionInterface::IceConnectionState ice_connection_state_ = diff --git a/pc/jsep_transport_unittest.cc b/pc/jsep_transport_unittest.cc index c585844f883..b72267d808d 100644 --- a/pc/jsep_transport_unittest.cc +++ b/pc/jsep_transport_unittest.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -52,9 +53,9 @@ #include "rtc_base/ssl_fingerprint.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/thread.h" #include "test/create_test_field_trials.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { namespace { @@ -185,7 +186,7 @@ class JsepTransport2Test : public ::testing::Test { void OnRtcpMuxActive() { signal_rtcp_mux_active_received_ = true; } - AutoThread main_thread_; + test::RunLoop main_thread_; std::unique_ptr jsep_transport_; bool signal_rtcp_mux_active_received_ = false; FieldTrials field_trials_ = CreateTestFieldTrials(); @@ -1172,7 +1173,7 @@ class JsepTransport2HeaderExtensionTest memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len); // In order to be able to run this test function multiple times we can not // use the same sequence number twice. Increase the sequence number by one. - SetBE16(reinterpret_cast(rtp_packet_data) + 2, + SetBE16(std::span(rtp_packet_buffer).subspan(2), ++sequence_number_); CopyOnWriteBuffer rtp_packet(rtp_packet_data, rtp_len, packet_size); diff --git a/pc/legacy_stats_collector.cc b/pc/legacy_stats_collector.cc index 61aad3471bc..6978f1b06b4 100644 --- a/pc/legacy_stats_collector.cc +++ b/pc/legacy_stats_collector.cc @@ -20,6 +20,8 @@ #include #include +#include "absl/base/nullability.h" +#include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -43,7 +45,6 @@ #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" #include "p2p/dtls/dtls_transport_internal.h" -#include "pc/channel.h" #include "pc/channel_interface.h" #include "pc/data_channel_utils.h" #include "pc/jsep_transport_controller.h" @@ -61,6 +62,7 @@ #include "rtc_base/socket_address.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/trace_event.h" #include "system_wrappers/include/clock.h" @@ -791,8 +793,13 @@ StatsReport* LegacyStatsCollector::AddCertificateReports( StatsReport* first_report = nullptr; StatsReport* prev_report = nullptr; + absl::flat_hash_set visited_fingerprints; for (SSLCertificateStats* stats = cert_stats.get(); stats; stats = stats->issuer.get()) { + if (!visited_fingerprints.insert(stats->fingerprint).second) { + break; + } + StatsReport::Id id(StatsReport::NewTypedId( StatsReport::kStatsReportTypeCertificate, stats->fingerprint)); @@ -1133,9 +1140,9 @@ void LegacyStatsCollector::ExtractBweInfo() { if (transceiver->media_type() != MediaType::VIDEO) { continue; } - auto* video_channel = transceiver->internal()->channel(); - if (video_channel) { - video_media_channels.push_back(video_channel->video_media_send_channel()); + if (transceiver->internal()->HasChannel()) { + video_media_channels.push_back( + transceiver->internal()->video_media_send_channel()); } } @@ -1156,6 +1163,10 @@ namespace { class ChannelStatsGatherer { public: + explicit ChannelStatsGatherer(RtpTransceiver* absl_nonnull transceiver) + : transceiver_(transceiver) { + RTC_DCHECK(transceiver_); + } virtual ~ChannelStatsGatherer() = default; virtual bool GetStatsOnWorkerThread() = 0; @@ -1183,21 +1194,25 @@ class ChannelStatsGatherer { ExtractStatsFromList(sender_data, transport_id, collector, StatsReport::kSend, sender_track_id_by_ssrc); } + RtpTransceiver* transceiver() { return transceiver_; } + + private: + RtpTransceiver* const transceiver_; }; class VoiceChannelStatsGatherer final : public ChannelStatsGatherer { public: - explicit VoiceChannelStatsGatherer(VoiceChannel* voice_channel) - : voice_channel_(voice_channel) { - RTC_DCHECK(voice_channel_); + explicit VoiceChannelStatsGatherer(RtpTransceiver* transceiver) + : ChannelStatsGatherer(transceiver) { + RTC_DCHECK_EQ(transceiver->media_type(), MediaType::AUDIO); } bool GetStatsOnWorkerThread() override { VoiceMediaSendInfo send_info; VoiceMediaReceiveInfo receive_info; bool success = - voice_channel_->voice_media_send_channel()->GetStats(&send_info); - success &= voice_channel_->voice_media_receive_channel()->GetStats( + transceiver()->voice_media_send_channel()->GetStats(&send_info); + success &= transceiver()->voice_media_receive_channel()->GetStats( &receive_info, /*get_and_clear_legacy_stats=*/true); if (success) { @@ -1223,24 +1238,23 @@ class VoiceChannelStatsGatherer final : public ChannelStatsGatherer { } private: - VoiceChannel* voice_channel_; VoiceMediaInfo voice_media_info; }; class VideoChannelStatsGatherer final : public ChannelStatsGatherer { public: - explicit VideoChannelStatsGatherer(VideoChannel* video_channel) - : video_channel_(video_channel) { - RTC_DCHECK(video_channel_); + explicit VideoChannelStatsGatherer(RtpTransceiver* transceiver) + : ChannelStatsGatherer(transceiver) { + RTC_DCHECK_EQ(transceiver->media_type(), MediaType::VIDEO); } bool GetStatsOnWorkerThread() override { VideoMediaSendInfo send_info; VideoMediaReceiveInfo receive_info; bool success = - video_channel_->video_media_send_channel()->GetStats(&send_info); + transceiver()->video_media_send_channel()->GetStats(&send_info); success &= - video_channel_->video_media_receive_channel()->GetStats(&receive_info); + transceiver()->video_media_receive_channel()->GetStats(&receive_info); if (success) { video_media_info = VideoMediaInfo(std::move(send_info), std::move(receive_info)); @@ -1256,20 +1270,18 @@ class VideoChannelStatsGatherer final : public ChannelStatsGatherer { bool HasRemoteAudio() const override { return false; } private: - VideoChannel* video_channel_; VideoMediaInfo video_media_info; }; std::unique_ptr CreateChannelStatsGatherer( - ChannelInterface* channel) { - RTC_DCHECK(channel); - if (channel->media_type() == MediaType::AUDIO) { - return std::make_unique( - channel->AsVoiceChannel()); + RtpTransceiver* transceiver) { + RTC_DCHECK(transceiver); + RTC_DCHECK(transceiver->HasChannel()); + if (transceiver->media_type() == MediaType::AUDIO) { + return std::make_unique(transceiver); } else { - RTC_DCHECK_EQ(channel->media_type(), MediaType::VIDEO); - return std::make_unique( - channel->AsVideoChannel()); + RTC_DCHECK_EQ(transceiver->media_type(), MediaType::VIDEO); + return std::make_unique(transceiver); } } @@ -1285,12 +1297,11 @@ void LegacyStatsCollector::ExtractMediaInfo( { Thread::ScopedDisallowBlockingCalls no_blocking_calls; for (const auto& transceiver : transceivers) { - ChannelInterface* channel = transceiver->internal()->channel(); - if (!channel) { + if (!transceiver->internal()->HasChannel()) { continue; } std::unique_ptr gatherer = - CreateChannelStatsGatherer(channel); + CreateChannelStatsGatherer(transceiver->internal()); if (transceiver->mid()) { gatherer->mid = *transceiver->mid(); } @@ -1298,12 +1309,14 @@ void LegacyStatsCollector::ExtractMediaInfo( if (it != transport_names_by_mid.end()) { gatherer->transport_name = it->second; } + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (const auto& sender : transceiver->internal()->senders()) { auto track = sender->track(); std::string track_id = (track ? track->id() : ""); gatherer->sender_track_id_by_ssrc.insert( std::make_pair(sender->ssrc(), track_id)); } + RTC_ALLOW_PLAN_B_DEPRECATION_END() // Populating `receiver_track_id_by_ssrc` will be done on the worker // thread as the `ssrc` property of the receiver needs to be accessed @@ -1318,14 +1331,15 @@ void LegacyStatsCollector::ExtractMediaInfo( // Populate `receiver_track_id_by_ssrc` for the gatherers. int i = 0; for (const auto& transceiver : transceivers) { - ChannelInterface* channel = transceiver->internal()->channel(); - if (!channel) + if (!transceiver->internal()->HasChannel()) continue; ChannelStatsGatherer* gatherer = gatherers[i++].get(); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (const auto& receiver : transceiver->internal()->receivers()) { gatherer->receiver_track_id_by_ssrc.insert(std::make_pair( receiver->internal()->ssrc().value_or(0), receiver->track()->id())); } + RTC_ALLOW_PLAN_B_DEPRECATION_END() } for (auto it = gatherers.begin(); it != gatherers.end(); diff --git a/pc/legacy_stats_collector.h b/pc/legacy_stats_collector.h index 25e61edfdda..f36e5096645 100644 --- a/pc/legacy_stats_collector.h +++ b/pc/legacy_stats_collector.h @@ -114,6 +114,11 @@ class LegacyStatsCollector : public LegacyStatsCollectorInterface { bool UseStandardBytesStats() const { return use_standard_bytes_stats_; } + StatsReport* AddCertificateReportsForTest( + std::unique_ptr cert_stats) { + return AddCertificateReports(std::move(cert_stats)); + } + private: // Struct that's populated on the network thread and carries the values to // the signaling thread where the stats are added to the stats reports. diff --git a/pc/legacy_stats_collector_unittest.cc b/pc/legacy_stats_collector_unittest.cc index 38eb191b461..94f7810a10a 100644 --- a/pc/legacy_stats_collector_unittest.cc +++ b/pc/legacy_stats_collector_unittest.cc @@ -28,7 +28,6 @@ #include "api/legacy_stats_types.h" #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" -#include "api/media_stream_track.h" #include "api/media_types.h" #include "api/peer_connection_interface.h" #include "api/rtp_sender_interface.h" @@ -42,6 +41,7 @@ #include "pc/media_stream.h" #include "pc/peer_connection_internal.h" #include "pc/sctp_data_channel.h" +#include "pc/test/fake_audio_track.h" #include "pc/test/fake_peer_connection_for_stats.h" #include "pc/test/fake_video_track_source.h" #include "pc/test/mock_rtp_receiver_internal.h" @@ -57,13 +57,17 @@ #include "rtc_base/null_socket_server.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/socket_address.h" +#include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/ntp_time.h" +#include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { @@ -83,82 +87,20 @@ constexpr char kLocalTrackId[] = "local_track_id"; constexpr char kRemoteTrackId[] = "remote_track_id"; constexpr uint32_t kSsrcOfTrack = 1234; -class FakeAudioProcessor : public AudioProcessorInterface { - public: - FakeAudioProcessor() {} - ~FakeAudioProcessor() override {} - - private: - AudioProcessorInterface::AudioProcessorStatistics GetStats( - bool has_recv_streams) override { - AudioProcessorStatistics stats; - if (has_recv_streams) { - stats.apm_statistics.echo_return_loss = 2.0; - stats.apm_statistics.echo_return_loss_enhancement = 3.0; - stats.apm_statistics.delay_median_ms = 4; - stats.apm_statistics.delay_standard_deviation_ms = 5; - } - return stats; - } -}; - -class FakeAudioTrack : public MediaStreamTrack { - public: - explicit FakeAudioTrack(const std::string& id) - : MediaStreamTrack(id), - processor_(make_ref_counted()) {} - std::string kind() const override { return "audio"; } - AudioSourceInterface* GetSource() const override { return nullptr; } - void AddSink(AudioTrackSinkInterface* sink) override {} - void RemoveSink(AudioTrackSinkInterface* sink) override {} - bool GetSignalLevel(int* level) override { - *level = 1; - return true; - } - scoped_refptr GetAudioProcessor() override { - return processor_; - } - - private: - scoped_refptr processor_; -}; - -// This fake audio processor is used to verify that the undesired initial values -// (-1) will be filtered out. -class FakeAudioProcessorWithInitValue : public AudioProcessorInterface { - public: - FakeAudioProcessorWithInitValue() {} - ~FakeAudioProcessorWithInitValue() override {} - - private: - AudioProcessorInterface::AudioProcessorStatistics GetStats( - bool /*has_recv_streams*/) override { - AudioProcessorStatistics stats; - return stats; +// Returns a FakeAudioTrack with the given ID and configuration. +scoped_refptr CreateFakeAudioTrack(const std::string& id, + bool return_stats) { + auto processor = make_ref_counted(); + processor->return_stats = return_stats; + if (return_stats) { + processor->stats.apm_statistics.echo_return_loss = 2.0; + processor->stats.apm_statistics.echo_return_loss_enhancement = 3.0; + processor->stats.apm_statistics.delay_median_ms = 4; + processor->stats.apm_statistics.delay_standard_deviation_ms = 5; } -}; - -class FakeAudioTrackWithInitValue - : public MediaStreamTrack { - public: - explicit FakeAudioTrackWithInitValue(const std::string& id) - : MediaStreamTrack(id), - processor_(make_ref_counted()) {} - std::string kind() const override { return "audio"; } - AudioSourceInterface* GetSource() const override { return nullptr; } - void AddSink(AudioTrackSinkInterface* sink) override {} - void RemoveSink(AudioTrackSinkInterface* sink) override {} - bool GetSignalLevel(int* level) override { - *level = 1; - return true; - } - scoped_refptr GetAudioProcessor() override { - return processor_; - } - - private: - scoped_refptr processor_; -}; + return FakeAudioTrack::Create(id, MediaStreamTrackInterface::kLive, + processor); +} bool GetValue(const StatsReport* report, StatsReport::StatsValueName name, @@ -585,12 +527,11 @@ void InitVoiceReceiverInfo(VoiceReceiverInfo* voice_receiver_info) { voice_receiver_info->decoding_codec_plc = 127; } - - class LegacyStatsCollectorTest : public ::testing::Test { protected: scoped_refptr CreatePeerConnection() { - return make_ref_counted(); + return make_ref_counted( + CreateTestEnvironment({.time = &clock_})); } std::unique_ptr CreateStatsCollector( @@ -599,7 +540,7 @@ class LegacyStatsCollectorTest : public ::testing::Test { pc, clock_, [this]() { return GetUtcTimeNowMs(); }); } - void VerifyAudioTrackStats(FakeAudioTrack* audio_track, + void VerifyAudioTrackStats(AudioTrackInterface* audio_track, LegacyStatsCollector* stats, const VoiceMediaInfo& voice_info, StatsReports* reports) { @@ -661,7 +602,9 @@ class LegacyStatsCollectorTest : public ::testing::Test { auto pc = CreatePeerConnection(); auto stats = CreateStatsCollector(pc.get()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVoiceChannel(/*mid=*/"audio", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // Fake stats to process. TransportChannelStats channel_stats; @@ -753,7 +696,7 @@ class LegacyStatsCollectorTest : public ::testing::Test { TimeDelta::Seconds(int64_t{365} * 50 * 86400)}; // 50 years offset. private: - AutoThread main_thread_; + test::RunLoop main_thread_; }; static scoped_refptr CreateMockSender( @@ -809,7 +752,9 @@ class StatsCollectorTrackTest : public LegacyStatsCollectorTest, } else { stats->AddTrack(video_track_.get()); } + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddSender(CreateMockSender(video_track_, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Adds a incoming video track with a given SSRC into the stats. @@ -824,7 +769,9 @@ class StatsCollectorTrackTest : public LegacyStatsCollectorTest, } else { stats->AddTrack(video_track_.get()); } + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddReceiver(CreateMockReceiver(video_track_, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Adds a outgoing audio track with a given SSRC into the stats, @@ -834,7 +781,7 @@ class StatsCollectorTrackTest : public LegacyStatsCollectorTest, scoped_refptr AddOutgoingAudioTrack( FakePeerConnectionForStats* pc, LegacyStatsCollector* stats) { - audio_track_ = make_ref_counted(kLocalTrackId); + audio_track_ = CreateFakeAudioTrack(kLocalTrackId, true); if (GetParam()) { if (!stream_) stream_ = MediaStream::Create("streamid"); @@ -843,13 +790,16 @@ class StatsCollectorTrackTest : public LegacyStatsCollectorTest, } else { stats->AddTrack(audio_track_.get()); } - return pc->AddSender(CreateMockSender(audio_track_, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); + auto sender = pc->AddSender(CreateMockSender(audio_track_, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); + return sender; } // Adds a incoming audio track with a given SSRC into the stats. void AddIncomingAudioTrack(FakePeerConnectionForStats* pc, LegacyStatsCollector* stats) { - audio_track_ = make_ref_counted(kRemoteTrackId); + audio_track_ = CreateFakeAudioTrack(kRemoteTrackId, true); if (GetParam()) { if (stream_ == nullptr) stream_ = MediaStream::Create("streamid"); @@ -858,7 +808,9 @@ class StatsCollectorTrackTest : public LegacyStatsCollectorTest, } else { stats->AddTrack(audio_track_.get()); } + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddReceiver(CreateMockReceiver(audio_track_, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } scoped_refptr audio_track() { return audio_track_; } @@ -866,7 +818,7 @@ class StatsCollectorTrackTest : public LegacyStatsCollectorTest, scoped_refptr stream_; scoped_refptr video_track_; - scoped_refptr audio_track_; + scoped_refptr audio_track_; }; TEST(StatsCollectionTest, DetachAndMerge) { @@ -995,7 +947,9 @@ TEST_P(StatsCollectorTrackTest, BytesCounterHandles64Bits) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddOutgoingVideoTrack(pc.get(), stats.get()); @@ -1027,7 +981,9 @@ TEST_P(StatsCollectorTrackTest, AudioBandwidthEstimationInfoIsReported) { VoiceMediaInfo voice_info; voice_info.senders.push_back(voice_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("audio", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1078,7 +1034,9 @@ TEST_P(StatsCollectorTrackTest, VideoBandwidthEstimationInfoIsReported) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddOutgoingVideoTrack(pc.get(), stats.get()); @@ -1144,7 +1102,9 @@ TEST_P(StatsCollectorTrackTest, TrackObjectExistsWithoutUpdateStats) { auto pc = CreatePeerConnection(); auto stats = CreateStatsCollector(pc.get()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddOutgoingVideoTrack(pc.get(), stats.get()); // Verfies the existence of the track report. @@ -1175,7 +1135,9 @@ TEST_P(StatsCollectorTrackTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddOutgoingVideoTrack(pc.get(), stats.get()); @@ -1229,7 +1191,9 @@ TEST_P(StatsCollectorTrackTest, TransportObjectLinkedFromSsrcObject) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddOutgoingVideoTrack(pc.get(), stats.get()); @@ -1264,7 +1228,9 @@ TEST_P(StatsCollectorTrackTest, RemoteSsrcInfoIsAbsent) { auto pc = CreatePeerConnection(); auto stats = CreateStatsCollector(pc.get()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddOutgoingVideoTrack(pc.get(), stats.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); @@ -1291,7 +1257,9 @@ TEST_P(StatsCollectorTrackTest, RemoteSsrcInfoIsPresent) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddOutgoingVideoTrack(pc.get(), stats.get()); @@ -1319,7 +1287,9 @@ TEST_P(StatsCollectorTrackTest, ReportsFromRemoteTrack) { VideoMediaInfo video_info; video_info.receivers.push_back(video_receiver_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); AddIncomingVideoTrack(pc.get(), stats.get()); @@ -1384,7 +1354,9 @@ TEST_F(LegacyStatsCollectorTest, IceCandidateReport) { TransportChannelStats channel_stats; channel_stats.ice_transport_stats.connection_infos.push_back(connection_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVoiceChannel("audio", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); pc->SetTransportStats(kTransportName, channel_stats); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); @@ -1476,6 +1448,21 @@ TEST_F(LegacyStatsCollectorTest, ChainedCertificateReportsCreated) { remote_ders); } +TEST_F(LegacyStatsCollectorTest, + AddCertificateReports_DuplicateFingerprintDoesNotCrash) { + auto pc = CreatePeerConnection(); + auto stats = CreateStatsCollector(pc.get()); + + // Create duplicate fingerprints chain + auto issuer_stats = std::make_unique( + "same_fingerprint", "sha-1", "cert_issuer", nullptr); + auto cert_stats = std::make_unique( + "same_fingerprint", "sha-1", "cert_leaf", std::move(issuer_stats)); + + // This should not crash (Use-After-Free) + stats->AddCertificateReportsForTest(std::move(cert_stats)); +} + // This test verifies that all certificates without chains are correctly // reported. TEST_F(LegacyStatsCollectorTest, ChainlessCertificateReportsCreated) { @@ -1500,7 +1487,9 @@ TEST_F(LegacyStatsCollectorTest, NoTransport) { // This will cause the fake PeerConnection to generate a TransportStats entry // but with only a single dummy TransportChannelStats. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVoiceChannel("audio", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1559,10 +1548,11 @@ TEST_P(StatsCollectorTrackTest, FilterOutNegativeInitialValues) { // Create a local stream with a local audio track and adds it to the stats. stream_ = MediaStream::Create("streamid"); - auto local_track = - make_ref_counted(kLocalTrackId); + auto local_track = CreateFakeAudioTrack(kLocalTrackId, false); stream_->AddTrack(scoped_refptr(local_track.get())); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddSender(CreateMockSender(local_track, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); if (GetParam()) { stats->AddStream(stream_.get()); } @@ -1572,9 +1562,11 @@ TEST_P(StatsCollectorTrackTest, FilterOutNegativeInitialValues) { scoped_refptr remote_stream( MediaStream::Create("remotestreamid")); scoped_refptr remote_track = - make_ref_counted(kRemoteTrackId); + CreateFakeAudioTrack(kRemoteTrackId, false); remote_stream->AddTrack(remote_track); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddReceiver(CreateMockReceiver(remote_track, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); if (GetParam()) { stats->AddStream(remote_stream.get()); } @@ -1602,7 +1594,9 @@ TEST_P(StatsCollectorTrackTest, FilterOutNegativeInitialValues) { voice_info.senders.push_back(voice_sender_info); voice_info.receivers.push_back(voice_receiver_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("voice", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1655,7 +1649,9 @@ TEST_P(StatsCollectorTrackTest, GetStatsFromLocalAudioTrack) { VoiceMediaInfo voice_info; voice_info.senders.push_back(voice_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("audio", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1683,7 +1679,9 @@ TEST_P(StatsCollectorTrackTest, GetStatsFromRemoteStream) { VoiceMediaInfo voice_info; voice_info.receivers.push_back(voice_receiver_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("audio", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1705,7 +1703,9 @@ TEST_P(StatsCollectorTrackTest, GetStatsAfterRemoveAudioStream) { VoiceMediaInfo voice_info; voice_info.senders.push_back(voice_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("audio", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1747,8 +1747,10 @@ TEST_P(StatsCollectorTrackTest, LocalAndRemoteTracksWithSameSsrc) { scoped_refptr remote_stream( MediaStream::Create("remotestreamid")); scoped_refptr remote_track = - make_ref_counted(kRemoteTrackId); + CreateFakeAudioTrack(kRemoteTrackId, true); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddReceiver(CreateMockReceiver(remote_track, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); remote_stream->AddTrack(remote_track); stats->AddStream(remote_stream.get()); @@ -1768,7 +1770,9 @@ TEST_P(StatsCollectorTrackTest, LocalAndRemoteTracksWithSameSsrc) { voice_info.receivers.push_back(voice_receiver_info); // Instruct the session to return stats containing the transport channel. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("audio", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1825,7 +1829,9 @@ TEST_P(StatsCollectorTrackTest, TwoLocalTracksWithSameSsrc) { VoiceMediaInfo voice_info; voice_info.senders.push_back(voice_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("voice", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1835,12 +1841,16 @@ TEST_P(StatsCollectorTrackTest, TwoLocalTracksWithSameSsrc) { // Remove the previous audio track from the stream. stream_->RemoveTrack(audio_track()); stats->RemoveLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->RemoveSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // Create a new audio track and adds it to the stream and stats. static const std::string kNewTrackId = "new_track_id"; - auto new_audio_track = make_ref_counted(kNewTrackId); + auto new_audio_track = CreateFakeAudioTrack(kNewTrackId, true); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddSender(CreateMockSender(new_audio_track, kSsrcOfTrack)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stream_->AddTrack(scoped_refptr(new_audio_track.get())); stats->AddLocalAudioTrack(new_audio_track.get(), kSsrcOfTrack); @@ -1870,11 +1880,12 @@ TEST_P(StatsCollectorTrackTest, TwoLocalSendersWithSameTrack) { auto pc = CreatePeerConnection(); auto stats = CreateStatsCollector(pc.get()); - auto local_track = - make_ref_counted(kLocalTrackId); + auto local_track = CreateFakeAudioTrack(kLocalTrackId, false); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddSender(CreateMockSender(local_track, kFirstSsrc)); stats->AddLocalAudioTrack(local_track.get(), kFirstSsrc); pc->AddSender(CreateMockSender(local_track, kSecondSsrc)); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats->AddLocalAudioTrack(local_track.get(), kSecondSsrc); VoiceSenderInfo first_sender_info; @@ -1891,7 +1902,9 @@ TEST_P(StatsCollectorTrackTest, TwoLocalSendersWithSameTrack) { voice_info.senders.push_back(first_sender_info); voice_info.senders.push_back(second_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc->AddVoiceChannel("voice", "transport"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); voice_media_channels.first->SetStats(voice_info); voice_media_channels.second->SetStats(voice_info); @@ -1940,7 +1953,9 @@ TEST_P(StatsCollectorTrackTest, VerifyVideoSendSsrcStats) { VideoMediaInfo video_info; video_info.aggregated_senders.push_back(video_sender_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1967,7 +1982,9 @@ TEST_P(StatsCollectorTrackTest, VerifyVideoReceiveSsrcStatsNew) { VideoMediaInfo video_info; video_info.receivers.push_back(video_receiver_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc->AddVideoChannel("video", "transport", video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; diff --git a/pc/media_options.cc b/pc/media_options.cc index 56afb8c8bdc..5d936f2e5f1 100644 --- a/pc/media_options.cc +++ b/pc/media_options.cc @@ -18,6 +18,7 @@ #include "media/base/rid_description.h" #include "pc/simulcast_description.h" #include "rtc_base/checks.h" +#include "rtc_base/system/plan_b_only.h" namespace webrtc { @@ -35,14 +36,14 @@ bool ValidateSimulcastLayers(const std::vector& rids, } // namespace -void MediaDescriptionOptions::AddAudioSender( +PLAN_B_ONLY void MediaDescriptionOptions::AddAudioSender( const std::string& track_id, const std::vector& stream_ids) { RTC_DCHECK(type == MediaType::AUDIO); AddSenderInternal(track_id, stream_ids, {}, SimulcastLayerList(), 1); } -void MediaDescriptionOptions::AddVideoSender( +PLAN_B_ONLY void MediaDescriptionOptions::AddVideoSender( const std::string& track_id, const std::vector& stream_ids, const std::vector& rids, @@ -56,7 +57,7 @@ void MediaDescriptionOptions::AddVideoSender( num_sim_layers); } -void MediaDescriptionOptions::AddSenderInternal( +PLAN_B_ONLY void MediaDescriptionOptions::AddSenderInternal( const std::string& track_id, const std::vector& stream_ids, const std::vector& rids, diff --git a/pc/media_options.h b/pc/media_options.h index a92590c8257..64d4b9ff195 100644 --- a/pc/media_options.h +++ b/pc/media_options.h @@ -25,6 +25,7 @@ #include "p2p/base/transport_description.h" #include "p2p/base/transport_description_factory.h" #include "pc/simulcast_description.h" +#include "rtc_base/system/plan_b_only.h" namespace webrtc { @@ -53,13 +54,13 @@ struct MediaDescriptionOptions { // TODO(deadbeef): When we don't support Plan B, there will only be one // sender per media description and this can be simplified. - void AddAudioSender(const std::string& track_id, - const std::vector& stream_ids); - void AddVideoSender(const std::string& track_id, - const std::vector& stream_ids, - const std::vector& rids, - const SimulcastLayerList& simulcast_layers, - int num_sim_layers); + PLAN_B_ONLY void AddAudioSender(const std::string& track_id, + const std::vector& stream_ids); + PLAN_B_ONLY void AddVideoSender(const std::string& track_id, + const std::vector& stream_ids, + const std::vector& rids, + const SimulcastLayerList& simulcast_layers, + int num_sim_layers); MediaType type; std::string mid; @@ -77,11 +78,11 @@ struct MediaDescriptionOptions { private: // Doesn't DCHECK on `type`. - void AddSenderInternal(const std::string& track_id, - const std::vector& stream_ids, - const std::vector& rids, - const SimulcastLayerList& simulcast_layers, - int num_sim_layers); + PLAN_B_ONLY void AddSenderInternal(const std::string& track_id, + const std::vector& stream_ids, + const std::vector& rids, + const SimulcastLayerList& simulcast_layers, + int num_sim_layers); }; // Provides a mechanism for describing how m= sections should be generated. diff --git a/pc/media_session.cc b/pc/media_session.cc index 3b34e6b7285..0417f537a1b 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -29,6 +29,7 @@ #include "api/rtp_transceiver_direction.h" #include "api/sctp_transport_interface.h" #include "api/transport/sctp_transport_factory_interface.h" +#include "call/payload_type.h" #include "media/base/codec.h" #include "media/base/media_constants.h" #include "media/base/media_engine.h" @@ -45,58 +46,53 @@ #include "pc/rtp_media_utils.h" #include "pc/session_description.h" #include "pc/simulcast_description.h" -#include "pc/used_ids.h" #include "rtc_base/checks.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" #include "rtc_base/thread.h" #include "rtc_base/unique_id_generator.h" -namespace { +namespace webrtc { -using webrtc::RTCError; -using webrtc::RTCErrorType; -using webrtc::RtpTransceiverDirection; -using webrtc::UniqueRandomIdGenerator; +namespace { -webrtc::RtpExtension RtpExtensionFromCapability( - const webrtc::RtpHeaderExtensionCapability& capability) { - return webrtc::RtpExtension(capability.uri, - capability.preferred_id.value_or(1), - capability.preferred_encrypt); +RtpExtension RtpExtensionFromCapability( + const RtpHeaderExtensionCapability& capability) { + return RtpExtension(capability.uri, capability.preferred_id.value_or(1), + capability.preferred_encrypt); } -webrtc::RtpHeaderExtensions RtpHeaderExtensionsFromCapabilities( - const std::vector& capabilities) { - webrtc::RtpHeaderExtensions exts; +RtpHeaderExtensions RtpHeaderExtensionsFromCapabilities( + const std::vector& capabilities) { + RtpHeaderExtensions exts; for (const auto& capability : capabilities) { exts.push_back(RtpExtensionFromCapability(capability)); } return exts; } -std::vector +std::vector UnstoppedRtpHeaderExtensionCapabilities( - std::vector capabilities) { + std::vector capabilities) { std::erase_if( - capabilities, [](const webrtc::RtpHeaderExtensionCapability& capability) { + capabilities, [](const RtpHeaderExtensionCapability& capability) { return capability.direction == RtpTransceiverDirection::kStopped; }); return capabilities; } -bool IsCapabilityPresent(const webrtc::RtpHeaderExtensionCapability& capability, - const webrtc::RtpHeaderExtensions& extensions) { +bool IsCapabilityPresent(const RtpHeaderExtensionCapability& capability, + const RtpHeaderExtensions& extensions) { return std::find_if(extensions.begin(), extensions.end(), - [&capability](const webrtc::RtpExtension& extension) { + [&capability](const RtpExtension& extension) { return capability.uri == extension.uri; }) != extensions.end(); } -webrtc::RtpHeaderExtensions UnstoppedOrPresentRtpHeaderExtensions( - const std::vector& capabilities, - const webrtc::RtpHeaderExtensions& all_encountered_extensions) { - webrtc::RtpHeaderExtensions extensions; +RtpHeaderExtensions UnstoppedOrPresentRtpHeaderExtensions( + const std::vector& capabilities, + const RtpHeaderExtensions& all_encountered_extensions) { + RtpHeaderExtensions extensions; for (const auto& capability : capabilities) { if (capability.direction != RtpTransceiverDirection::kStopped || IsCapabilityPresent(capability, all_encountered_extensions)) { @@ -106,12 +102,6 @@ webrtc::RtpHeaderExtensions UnstoppedOrPresentRtpHeaderExtensions( return extensions; } -} // namespace - -namespace webrtc { - -namespace { - bool ContainsRtxCodec(const std::vector& codecs) { return absl::c_find_if(codecs, [](const Codec& c) { return c.GetResiliencyType() == Codec::ResiliencyType::kRtx; @@ -410,8 +400,8 @@ RTCError CreateMediaContentOffer( if (!AddStreamParams(media_description_options.sender_options, session_options.rtcp_cname, ssrc_generator, current_streams, offer, field_trials)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to add stream parameters"); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to add stream parameters"); } return CreateContentOffer(media_description_options, session_options, @@ -430,7 +420,9 @@ void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions, bool enable_encrypted_rtp_header_extensions, RtpHeaderExtensions* offered_extensions, RtpHeaderExtensions* all_encountered_extensions, - UsedRtpHeaderExtensionIds* used_ids) { + PayloadTypeSuggester& suggester, + absl::string_view mid, + RtpTransceiverIdDomain id_domain) { for (auto reference_extension : reference_extensions) { if (!RtpExtension::FindHeaderExtensionByUriAndEncryption( *offered_extensions, reference_extension.uri, @@ -449,7 +441,15 @@ void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions, // audio and video. offered_extensions->push_back(*existing); } else { - used_ids->FindAndSetIdUsed(&reference_extension); + auto suggested_id = suggester.SuggestRtpHeaderExtensionId( + mid, reference_extension, id_domain); + if (suggested_id.ok()) { + reference_extension.id = suggested_id.value(); + } else { + RTC_LOG(LS_ERROR) + << "Failed to suggest RTP header extension ID for " + << reference_extension.uri << ", error " << suggested_id.error(); + } all_encountered_extensions->push_back(reference_extension); offered_extensions->push_back(reference_extension); } @@ -485,11 +485,26 @@ const RtpExtension* FindHeaderExtensionByUriDiscardUnsupported( void NegotiateRtpHeaderExtensions(const RtpHeaderExtensions& local_extensions, const RtpHeaderExtensions& offered_extensions, RtpExtension::Filter filter, - RtpHeaderExtensions* negotiated_extensions) { + RtpHeaderExtensions* negotiated_extensions, + PayloadTypeSuggester& suggester, + absl::string_view mid, + RtpTransceiverIdDomain id_domain) { bool frame_descriptor_in_local = false; bool dependency_descriptor_in_local = false; bool abs_capture_time_in_local = false; + auto negotiate_extension = [&](const RtpExtension& ours, + const RtpExtension* theirs) { + if (theirs && theirs->encrypt == ours.encrypt) { + // We MUST use their ID in the answer, and we should also record it + // in our suggester to ensure consistency and persistence. + RTCError error = + suggester.AddRtpHeaderExtensionMapping(mid, *theirs, /*local=*/false); + RTC_DCHECK(error.ok()); + negotiated_extensions->push_back(*theirs); + } + }; + for (const RtpExtension& ours : local_extensions) { if (ours.uri == RtpExtension::kGenericFrameDescriptorUri00) frame_descriptor_in_local = true; @@ -500,10 +515,7 @@ void NegotiateRtpHeaderExtensions(const RtpHeaderExtensions& local_extensions, const RtpExtension* theirs = FindHeaderExtensionByUriDiscardUnsupported( offered_extensions, ours.uri, filter); - if (theirs && theirs->encrypt == ours.encrypt) { - // We respond with their RTP header extension id. - negotiated_extensions->push_back(*theirs); - } + negotiate_extension(ours, theirs); } // Frame descriptors support. If the extension is not present locally, but is @@ -512,6 +524,9 @@ void NegotiateRtpHeaderExtensions(const RtpHeaderExtensions& local_extensions, const RtpExtension* theirs = FindHeaderExtensionByUriDiscardUnsupported( offered_extensions, RtpExtension::kDependencyDescriptorUri, filter); if (theirs) { + RTCError error = + suggester.AddRtpHeaderExtensionMapping(mid, *theirs, /*local=*/false); + RTC_DCHECK(error.ok()); negotiated_extensions->push_back(*theirs); } } @@ -519,6 +534,9 @@ void NegotiateRtpHeaderExtensions(const RtpHeaderExtensions& local_extensions, const RtpExtension* theirs = FindHeaderExtensionByUriDiscardUnsupported( offered_extensions, RtpExtension::kGenericFrameDescriptorUri00, filter); if (theirs) { + RTCError error = + suggester.AddRtpHeaderExtensionMapping(mid, *theirs, /*local=*/false); + RTC_DCHECK(error.ok()); negotiated_extensions->push_back(*theirs); } } @@ -529,6 +547,9 @@ void NegotiateRtpHeaderExtensions(const RtpHeaderExtensions& local_extensions, const RtpExtension* theirs = FindHeaderExtensionByUriDiscardUnsupported( offered_extensions, RtpExtension::kAbsoluteCaptureTimeUri, filter); if (theirs) { + RTCError error = + suggester.AddRtpHeaderExtensionMapping(mid, *theirs, /*local=*/false); + RTC_DCHECK(error.ok()); negotiated_extensions->push_back(*theirs); } } @@ -570,7 +591,9 @@ bool CreateMediaContentAnswer( bool enable_encrypted_rtp_header_extensions, StreamParamsVec* current_streams, bool bundle_enabled, - MediaContentDescription* answer) { + MediaContentDescription* answer, + PayloadTypeSuggester& suggester, + RtpTransceiverIdDomain id_domain) { answer->set_extmap_allow_mixed_enum(offer->extmap_allow_mixed_enum()); const RtpExtension::Filter extensions_filter = enable_encrypted_rtp_header_extensions @@ -595,9 +618,10 @@ bool CreateMediaContentAnswer( } } RtpHeaderExtensions negotiated_rtp_extensions; - NegotiateRtpHeaderExtensions(local_rtp_extensions_to_reply_with, - offer->rtp_header_extensions(), - extensions_filter, &negotiated_rtp_extensions); + NegotiateRtpHeaderExtensions( + local_rtp_extensions_to_reply_with, offer->rtp_header_extensions(), + extensions_filter, &negotiated_rtp_extensions, suggester, + media_description_options.mid, id_domain); answer->set_rtp_header_extensions(negotiated_rtp_extensions); answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux()); @@ -708,7 +732,7 @@ MediaSessionDescriptionFactory::filtered_rtp_header_extensions( RtpHeaderExtensions extensions) const { if (!is_unified_plan_) { // Remove extensions only supported with unified-plan. - std::erase_if(extensions, [](const webrtc::RtpExtension& extension) { + std::erase_if(extensions, [](const RtpExtension& extension) { return extension.uri == RtpExtension::kMidUri || extension.uri == RtpExtension::kRidUri || extension.uri == RtpExtension::kRepairedRidUri; @@ -807,9 +831,9 @@ MediaSessionDescriptionFactory::CreateOfferOrError( if (!offer_bundle.content_names().empty()) { offer->AddGroup(offer_bundle); if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "CreateOffer failed to UpdateTransportInfoForBundle"); + return LOG_ERROR( + RTCError(RTCErrorType::INTERNAL_ERROR) + << "CreateOffer failed to UpdateTransportInfoForBundle"); } } } @@ -840,7 +864,8 @@ MediaSessionDescriptionFactory::CreateAnswerOrError( const MediaSessionOptions& session_options, const SessionDescription* current_description) const { if (!offer) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, "Called without offer."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Called without offer."); } // Must have options for exactly as many sections as in the offer. @@ -999,9 +1024,9 @@ MediaSessionDescriptionFactory::CreateAnswerOrError( // Share the same ICE credentials and crypto params across all contents, // as BUNDLE requires. if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "CreateAnswer failed to UpdateTransportInfoForBundle."); + return LOG_ERROR( + RTCError(RTCErrorType::INTERNAL_ERROR) + << "CreateAnswer failed to UpdateTransportInfoForBundle."); } } } @@ -1064,34 +1089,37 @@ MediaSessionDescriptionFactory::GetOfferedRtpHeaderExtensionsWithIds( // receiver supports an RTP stream where one- and two-byte RTP header // extensions are mixed. For backwards compatibility reasons it's used in // WebRTC to signal that two-byte RTP header extensions are supported. - UsedRtpHeaderExtensionIds used_ids( - extmap_allow_mixed ? UsedRtpHeaderExtensionIds::IdDomain::kTwoByteAllowed - : UsedRtpHeaderExtensionIds::IdDomain::kOneByteOnly); + RtpTransceiverIdDomain id_domain = + extmap_allow_mixed ? RtpTransceiverIdDomain::kTwoByteAllowed + : RtpTransceiverIdDomain::kOneByteOnly; RtpHeaderExtensions all_encountered_extensions; AudioVideoRtpHeaderExtensions offered_extensions; // First - get all extensions from the current description if the media type // is used. - // Add them to `used_ids` so the local ids are not reused if a new media + // Add them to the suggester so the local ids are not reused if a new media // type is added. + RTC_DCHECK(codec_lookup_helper_->PayloadTypeSuggester()); for (const ContentInfo* content : current_active_contents) { if (IsMediaContentOfType(content, MediaType::AUDIO)) { MergeRtpHdrExts(content->media_description()->rtp_header_extensions(), enable_encrypted_rtp_header_extensions_, &offered_extensions.audio, &all_encountered_extensions, - &used_ids); + *codec_lookup_helper_->PayloadTypeSuggester(), + content->mid(), id_domain); } else if (IsMediaContentOfType(content, MediaType::VIDEO)) { MergeRtpHdrExts(content->media_description()->rtp_header_extensions(), enable_encrypted_rtp_header_extensions_, &offered_extensions.video, &all_encountered_extensions, - &used_ids); + *codec_lookup_helper_->PayloadTypeSuggester(), + content->mid(), id_domain); } } // Add all encountered header extensions in the media description options that // are not in the current description. - + RTC_DCHECK(codec_lookup_helper_->PayloadTypeSuggester()); for (const auto& entry : media_description_options) { RtpHeaderExtensions filtered_extensions = filtered_rtp_header_extensions(UnstoppedOrPresentRtpHeaderExtensions( @@ -1099,11 +1127,13 @@ MediaSessionDescriptionFactory::GetOfferedRtpHeaderExtensionsWithIds( if (entry.type == MediaType::AUDIO) MergeRtpHdrExts( filtered_extensions, enable_encrypted_rtp_header_extensions_, - &offered_extensions.audio, &all_encountered_extensions, &used_ids); + &offered_extensions.audio, &all_encountered_extensions, + *codec_lookup_helper_->PayloadTypeSuggester(), entry.mid, id_domain); else if (entry.type == MediaType::VIDEO) MergeRtpHdrExts( filtered_extensions, enable_encrypted_rtp_header_extensions_, - &offered_extensions.video, &all_encountered_extensions, &used_ids); + &offered_extensions.video, &all_encountered_extensions, + *codec_lookup_helper_->PayloadTypeSuggester(), entry.mid, id_domain); } return offered_extensions; } @@ -1342,9 +1372,9 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer( media_description_options.transport_options, current_description, !offer_content->rejected && bundle_transport == nullptr, ice_credentials); if (!transport) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "Failed to create transport answer, transport is missing"); + return LOG_ERROR( + RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to create transport answer, transport is missing"); } // Pick codecs based on the requested communications direction in the offer @@ -1397,16 +1427,21 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer( ssrc_generator(), current_streams, answer_content.get(), transport_desc_factory_->trials())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to set codecs in answer"); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to set codecs in answer"); } + RTC_DCHECK(codec_lookup_helper_->PayloadTypeSuggester()); if (!CreateMediaContentAnswer( offer_content_description, media_description_options, session_options, filtered_rtp_header_extensions(header_extensions), ssrc_generator(), enable_encrypted_rtp_header_extensions_, current_streams, - bundle_enabled, answer_content.get())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create answer"); + bundle_enabled, answer_content.get(), + *codec_lookup_helper_->PayloadTypeSuggester(), + offer_description->extmap_allow_mixed() + ? RtpTransceiverIdDomain::kTwoByteAllowed + : RtpTransceiverIdDomain::kOneByteOnly)) { + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to create answer"); } bool secure = bundle_transport ? bundle_transport->description.secure() @@ -1447,9 +1482,9 @@ RTCError MediaSessionDescriptionFactory::AddDataContentForAnswer( media_description_options.transport_options, current_description, !offer_content->rejected && bundle_transport == nullptr, ice_credentials); if (!data_transport) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "Failed to create transport answer, data transport is missing"); + return LOG_ERROR( + RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to create transport answer, data transport is missing"); } bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) && @@ -1474,13 +1509,18 @@ RTCError MediaSessionDescriptionFactory::AddDataContentForAnswer( data_answer->as_sctp()->set_max_message_size(std::min( offer_data_description->max_message_size(), kSctpSendBufferSize)); } + RTC_DCHECK(codec_lookup_helper_->PayloadTypeSuggester()); if (!CreateMediaContentAnswer( offer_data_description, media_description_options, session_options, RtpHeaderExtensions(), ssrc_generator(), enable_encrypted_rtp_header_extensions_, current_streams, - bundle_enabled, data_answer.get())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create answer"); + bundle_enabled, data_answer.get(), + *codec_lookup_helper_->PayloadTypeSuggester(), + offer_description->extmap_allow_mixed() + ? RtpTransceiverIdDomain::kTwoByteAllowed + : RtpTransceiverIdDomain::kOneByteOnly)) { + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to create answer"); } // Respond with sctpmap if the offer uses sctpmap. bool offer_uses_sctpmap = offer_data_description->use_sctpmap(); @@ -1540,9 +1580,9 @@ RTCError MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer( !offer_content->rejected && bundle_transport == nullptr, ice_credentials); if (!unsupported_transport) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "Failed to create transport answer, unsupported transport is missing"); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to create transport answer, unsupported " + "transport is missing"); } RTC_CHECK(IsMediaContentOfType(offer_content, MediaType::UNSUPPORTED)); diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc index 095912fae01..61ad0238fa0 100644 --- a/pc/media_session_unittest.cc +++ b/pc/media_session_unittest.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_codecs/audio_format.h" #include "api/candidate.h" #include "api/environment/environment.h" @@ -61,6 +61,7 @@ #include "rtc_base/ssl_identity.h" #include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/unique_id_generator.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" @@ -86,7 +87,6 @@ using ::testing::SizeIs; using ::testing::UnorderedElementsAreArray; using ::testing::Values; using ::testing::ValuesIn; -using ::webrtc::UniqueRandomIdGenerator; using Candidates = std::vector; @@ -134,22 +134,22 @@ class CodecLookupHelperForTesting : public CodecLookupHelper { SetVideoCodecs(codecs, codecs); } - void SetAudioCodecs(ArrayView codecs) { + void SetAudioCodecs(std::span codecs) { SetAudioCodecs(std::vector(codecs.begin(), codecs.end())); } - void SetVideoCodecs(ArrayView codecs) { + void SetVideoCodecs(std::span codecs) { SetVideoCodecs(std::vector(codecs.begin(), codecs.end())); } template void SetAudioCodecs(U (&array)[N]) { - SetAudioCodecs(ArrayView(&array[0], N)); + SetAudioCodecs(std::span(&array[0], N)); } template void SetVideoCodecs(U (&array)[N]) { - SetVideoCodecs(ArrayView(&array[0], N)); + SetVideoCodecs(std::span(&array[0], N)); } void ClearAudioCodecs() { SetAudioCodecs(std::vector()); } @@ -260,8 +260,8 @@ const Codec kVideoCodecsH265Level6[] = { CreateVideoCodec(96, kH265MainProfileLevel6Sdp)}; // Match two codec lists for content, but ignore the ID. -bool CodecListsMatch(ArrayView list1, - ArrayView list2) { +bool CodecListsMatch(std::span list1, + std::span list2) { if (list1.size() != list2.size()) { return false; } @@ -546,6 +546,7 @@ void AttachSenderToMediaDescriptionOptions( int num_sim_layer, MediaSessionOptions* session_options) { auto it = FindFirstMediaDescriptionByMid(mid, session_options); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); switch (type) { case webrtc::MediaType::AUDIO: it->AddAudioSender(track_id, stream_ids); @@ -557,6 +558,7 @@ void AttachSenderToMediaDescriptionOptions( default: RTC_DCHECK_NOTREACHED(); } + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } void AttachSenderToMediaDescriptionOptions( @@ -2477,6 +2479,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, PreferEncryptedRtpHeaderExtensionsWhenEncryptionEnabled) { MediaSessionOptions opts; + opts.offer_extmap_allow_mixed = true; AddAudioVideoSections(RtpTransceiverDirection::kRecvOnly, &opts); SetAudioVideoRtpHeaderExtensions( diff --git a/pc/media_stream_unittest.cc b/pc/media_stream_unittest.cc index de82b201e7e..8611ee465e3 100644 --- a/pc/media_stream_unittest.cc +++ b/pc/media_stream_unittest.cc @@ -20,6 +20,7 @@ #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" static const char kStreamId1[] = "local_stream_1"; static const char kVideoTrackId[] = "dummy_video_cam_1"; @@ -82,7 +83,7 @@ class MediaStreamTest : public ::testing::Test { EXPECT_FALSE(track->enabled()); } - AutoThread main_thread_; + test::RunLoop main_thread_; scoped_refptr stream_; scoped_refptr audio_track_; scoped_refptr video_track_; diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 49c51ca5f90..11e40ad5796 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -119,6 +119,7 @@ #include "rtc_base/socket_address.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/trace_event.h" #include "rtc_base/unique_id_generator.h" @@ -275,9 +276,9 @@ RTCError ValidateIceCandidatePoolSize( // in new candidates being gathered. if (previous_ice_candidate_pool_size.has_value() && ice_candidate_pool_size != previous_ice_candidate_pool_size.value()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change candidate pool size after calling " - "SetLocalDescription."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Can't change candidate pool size after calling " + "SetLocalDescription."); } return RTCError::OK(); @@ -330,8 +331,8 @@ RTCErrorOr ApplyConfiguration( configuration.stable_writable_connection_ping_interval_ms; if (configuration != modified_config) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Modifying the configuration in an unsupported way."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Modifying the configuration in an unsupported way."); } RTCError err = IceConfig(modified_config).IsValid(); @@ -696,8 +697,8 @@ PeerConnection::~PeerConnection() { sdp_handler_->GetMediaChannelTeardownTasks(network_tasks, worker_tasks); legacy_stats_.reset(nullptr); - network_tasks.push_back( - stats_collector_.CancelPendingRequestAndGetShutdownTask()); + stats_collector_.CancelPendingRequestAndGetShutdownTasks(network_tasks, + worker_tasks); CloseOnNetworkThread(network_tasks); @@ -745,9 +746,6 @@ JsepTransportController* PeerConnection::InitializeNetworkThread( config.transport_observer = this; config.rtcp_handler = InitializeRtcpCallback(); config.un_demuxable_packet_handler = InitializeUnDemuxablePacketHandler(); -#if defined(ENABLE_EXTERNAL_AUTH) - config.enable_external_auth = true; -#endif // DTLS has to be enabled to use SCTP. if (dtls_enabled_) { @@ -960,25 +958,26 @@ RTCErrorOr> PeerConnection::AddTrack( RTC_DCHECK_RUN_ON(signaling_thread()); TRACE_EVENT0("webrtc", "PeerConnection::AddTrack"); if (!ConfiguredForMedia()) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "Not configured for media"); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Not configured for media"); } if (!track) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Track is null."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Track is null."); } if (!(track->kind() == MediaStreamTrackInterface::kAudioKind || track->kind() == MediaStreamTrackInterface::kVideoKind)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Track has invalid kind: " + track->kind()); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Track has invalid kind: " << track->kind()); } if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "PeerConnection is closed."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_STATE) + << "PeerConnection is closed."); } if (rtp_manager()->FindSenderForTrack(track.get())) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Sender already exists for track " + track->id() + "."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Sender already exists for track " << track->id() + << "."); } RTCErrorOr> sender_or_error; if (IsUnifiedPlan()) { @@ -988,8 +987,10 @@ RTCErrorOr> PeerConnection::AddTrack( sdp_handler_->video_bitrate_allocator_factory(), track, stream_ids, init_send_encodings); } else { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); sender_or_error = rtp_manager()->AddTrackPlanB(track, stream_ids, init_send_encodings); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } if (sender_or_error.ok()) { sdp_handler_->UpdateNegotiationNeeded(); @@ -1002,15 +1003,16 @@ RTCError PeerConnection::RemoveTrackOrError( scoped_refptr sender) { RTC_DCHECK_RUN_ON(signaling_thread()); if (!ConfiguredForMedia()) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "Not configured for media"); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Not configured for media"); } if (!sender) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Sender is null."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Sender is null."); } if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "PeerConnection is closed."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_STATE) + << "PeerConnection is closed."); } if (IsUnifiedPlan()) { auto transceiver = FindTransceiverBySender(sender); @@ -1027,6 +1029,7 @@ RTCError PeerConnection::RemoveTrackOrError( } } else { bool removed; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); if (sender->media_type() == webrtc::MediaType::AUDIO) { removed = rtp_manager()->GetAudioTransceiver()->internal()->RemoveSenderPlanB( @@ -1037,10 +1040,11 @@ RTCError PeerConnection::RemoveTrackOrError( rtp_manager()->GetVideoTransceiver()->internal()->RemoveSenderPlanB( sender.get()); } + RTC_ALLOW_PLAN_B_DEPRECATION_END(); if (!removed) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Couldn't find sender " + sender->id() + " to remove."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Couldn't find sender " << sender->id() + << " to remove."); } } sdp_handler_->UpdateNegotiationNeeded(); @@ -1056,8 +1060,8 @@ PeerConnection::FindTransceiverBySender( RTCErrorOr> PeerConnection::AddTransceiver(scoped_refptr track) { if (!ConfiguredForMedia()) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "Not configured for media"); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Not configured for media"); } return AddTransceiver(track, RtpTransceiverInit()); @@ -1068,13 +1072,14 @@ PeerConnection::AddTransceiver(scoped_refptr track, const RtpTransceiverInit& init) { RTC_DCHECK_RUN_ON(signaling_thread()); if (!ConfiguredForMedia()) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "Not configured for media"); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Not configured for media"); } RTC_CHECK(IsUnifiedPlan()) << "AddTransceiver is only available with Unified Plan SdpSemantics"; if (!track) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "track is null"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "track is null"); } MediaType media_type; if (track->kind() == MediaStreamTrackInterface::kAudioKind) { @@ -1082,8 +1087,8 @@ PeerConnection::AddTransceiver(scoped_refptr track, } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) { media_type = MediaType::VIDEO; } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Track kind is not audio or video"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Track kind is not audio or video"); } return AddTransceiver(media_type, track, init); } @@ -1098,14 +1103,14 @@ PeerConnection::AddTransceiver(MediaType media_type, const RtpTransceiverInit& init) { RTC_DCHECK_RUN_ON(signaling_thread()); if (!ConfiguredForMedia()) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "Not configured for media"); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Not configured for media"); } RTC_CHECK(IsUnifiedPlan()) << "AddTransceiver is only available with Unified Plan SdpSemantics"; if (!(media_type == MediaType::AUDIO || media_type == MediaType::VIDEO)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "media type is not audio or video"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "media type is not audio or video"); } return AddTransceiver(media_type, nullptr, init); } @@ -1117,8 +1122,8 @@ PeerConnection::AddTransceiver(MediaType media_type, bool update_negotiation_needed) { RTC_DCHECK_RUN_ON(signaling_thread()); if (!ConfiguredForMedia()) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "Not configured for media"); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_OPERATION) + << "Not configured for media"); } RTC_DCHECK( (media_type == MediaType::AUDIO || media_type == MediaType::VIDEO)); @@ -1134,26 +1139,26 @@ PeerConnection::AddTransceiver(MediaType media_type, return !encoding.rid.empty(); }); if (num_rids > 0 && num_rids != init.send_encodings.size()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "RIDs must be provided for either all or none of the send encodings."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "RIDs must be provided for either all or none of the " + "send encodings."); } if (num_rids > 0 && absl::c_any_of(init.send_encodings, [](const RtpEncodingParameters& encoding) { return !IsLegalRsidName(encoding.rid); })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Invalid RID value provided."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Invalid RID value provided."); } if (absl::c_any_of(init.send_encodings, [](const RtpEncodingParameters& encoding) { return encoding.ssrc.has_value(); })) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); + return LOG_ERROR( + RTCError(RTCErrorType::UNSUPPORTED_PARAMETER) + << "Attempted to set an unimplemented parameter of RtpParameters."); } RtpParameters parameters; @@ -1189,9 +1194,9 @@ PeerConnection::AddTransceiver(MediaType media_type, } if (UnimplementedRtpParameterHasValue(parameters)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); + return LOG_ERROR( + RTCError(RTCErrorType::UNSUPPORTED_PARAMETER) + << "Attempted to set an unimplemented parameter of RtpParameters."); } std::vector codecs; @@ -1210,7 +1215,7 @@ PeerConnection::AddTransceiver(MediaType media_type, if (result.type() == RTCErrorType::INVALID_MODIFICATION) { result.set_type(RTCErrorType::UNSUPPORTED_OPERATION); } - LOG_AND_RETURN_ERROR(result.type(), result.message()); + return LOG_ERROR(RTCError(result.type()) << result.message()); } RTC_LOG(LS_INFO) << "Adding " << MediaTypeToString(media_type) @@ -1235,11 +1240,6 @@ PeerConnection::AddTransceiver(MediaType media_type, return scoped_refptr(transceiver); } -void PeerConnection::OnNegotiationNeeded() { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(!IsClosed()); - sdp_handler_->UpdateNegotiationNeeded(); -} scoped_refptr PeerConnection::CreateSender( const std::string& kind, @@ -1271,16 +1271,17 @@ scoped_refptr PeerConnection::CreateSender( scoped_refptr> new_sender; if (kind == MediaStreamTrackInterface::kAudioKind) { - auto audio_sender = AudioRtpSender::Create( - env_, worker_thread(), CreateRandomUuid(), legacy_stats_.get(), nullptr, - rtp_manager()->voice_media_send_channel()); + auto audio_sender = + AudioRtpSender::Create(env_, signaling_thread(), worker_thread(), + CreateRandomUuid(), legacy_stats_.get(), nullptr, + rtp_manager()->voice_media_send_channel()); new_sender = RtpSenderProxyWithInternal::Create( signaling_thread(), audio_sender); rtp_manager()->GetAudioTransceiver()->internal()->AddSenderPlanB( new_sender); } else if (kind == MediaStreamTrackInterface::kVideoKind) { auto video_sender = VideoRtpSender::Create( - env_, worker_thread(), CreateRandomUuid(), nullptr, + env_, signaling_thread(), worker_thread(), CreateRandomUuid(), nullptr, rtp_manager()->video_media_send_channel()); new_sender = RtpSenderProxyWithInternal::Create( signaling_thread(), video_sender); @@ -1288,6 +1289,9 @@ scoped_refptr PeerConnection::CreateSender( new_sender); } else { RTC_LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind; + } + + if (!new_sender) { return nullptr; } new_sender->internal()->set_stream_ids(stream_ids); @@ -1376,11 +1380,12 @@ void PeerConnection::GetStats( TRACE_EVENT0("webrtc", "PeerConnection::GetStats"); RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(callback); - RTC_LOG_THREAD_BLOCK_COUNT(); + RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); scoped_refptr internal_sender; if (selector) { for (const auto& proxy_transceiver : rtp_manager()->transceivers()->List()) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (const auto& proxy_sender : proxy_transceiver->internal()->senders()) { if (proxy_sender == selector) { @@ -1388,6 +1393,7 @@ void PeerConnection::GetStats( break; } } + RTC_ALLOW_PLAN_B_DEPRECATION_END() if (internal_sender) break; } @@ -1398,7 +1404,6 @@ void PeerConnection::GetStats( // selector" is an empty set. Invoking GetStatsReport() with a null selector // produces an empty stats report. stats_collector_.GetStatsReport(internal_sender, callback); - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); } void PeerConnection::GetStats( @@ -1407,11 +1412,12 @@ void PeerConnection::GetStats( TRACE_EVENT0("webrtc", "PeerConnection::GetStats"); RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(callback); - RTC_LOG_THREAD_BLOCK_COUNT(); + RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); scoped_refptr internal_receiver; if (selector) { for (const auto& proxy_transceiver : rtp_manager()->transceivers()->List()) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (const auto& proxy_receiver : proxy_transceiver->internal()->receivers()) { if (proxy_receiver == selector) { @@ -1419,6 +1425,7 @@ void PeerConnection::GetStats( break; } } + RTC_ALLOW_PLAN_B_DEPRECATION_END() if (internal_receiver) break; } @@ -1429,7 +1436,6 @@ void PeerConnection::GetStats( // the selector" is an empty set. Invoking GetStatsReport() with a null // selector produces an empty stats report. stats_collector_.GetStatsReport(internal_receiver, callback); - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); } PeerConnectionInterface::SignalingState PeerConnection::signaling_state() { @@ -1485,8 +1491,8 @@ PeerConnection::CreateDataChannelOrError(const std::string& label, TRACE_EVENT0("webrtc", "PeerConnection::CreateDataChannel"); if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "CreateDataChannelOrError: PeerConnection is closed."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_STATE) + << "CreateDataChannelOrError: PeerConnection is closed."); } bool first_datachannel = !data_channel_controller_.HasUsedDataChannels(); @@ -1537,12 +1543,15 @@ void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer, void PeerConnection::SetLocalDescription( SetSessionDescriptionObserver* observer, SessionDescriptionInterface* desc) { - if (!CheckValidSetDescription(observer, desc, signaling_thread())) + auto desc_ptr = std::unique_ptr(desc); + if (!CheckValidSetDescription(observer, desc_ptr, signaling_thread())) return; - RunOnSignalingThread([this, desc, observer]() mutable { + RunOnSignalingThread([this, desc = std::move(desc_ptr), + observer = scoped_refptr( + observer)]() mutable { RTC_DCHECK_RUN_ON(signaling_thread()); - sdp_handler_->SetLocalDescription(observer, desc); + sdp_handler_->SetLocalDescription(std::move(observer), std::move(desc)); }); } @@ -1563,7 +1572,8 @@ void PeerConnection::SetLocalDescription( void PeerConnection::SetLocalDescription( SetSessionDescriptionObserver* observer) { RTC_DCHECK_RUN_ON(signaling_thread()); - sdp_handler_->SetLocalDescription(observer); + sdp_handler_->SetLocalDescription( + scoped_refptr(observer)); } void PeerConnection::SetLocalDescription( @@ -1575,12 +1585,15 @@ void PeerConnection::SetLocalDescription( void PeerConnection::SetRemoteDescription( SetSessionDescriptionObserver* observer, SessionDescriptionInterface* desc) { - if (!CheckValidSetDescription(observer, desc, signaling_thread())) + auto desc_ptr = std::unique_ptr(desc); + if (!CheckValidSetDescription(observer, desc_ptr, signaling_thread())) return; - RunOnSignalingThread([this, desc, observer]() mutable { + RunOnSignalingThread([this, desc = std::move(desc_ptr), + observer = scoped_refptr( + observer)]() mutable { RTC_DCHECK_RUN_ON(signaling_thread()); - sdp_handler_->SetRemoteDescription(observer, desc); + sdp_handler_->SetRemoteDescription(std::move(observer), std::move(desc)); }); } @@ -1607,8 +1620,8 @@ RTCError PeerConnection::SetConfiguration( RTC_DCHECK_RUN_ON(signaling_thread()); TRACE_EVENT0("webrtc", "PeerConnection::SetConfiguration"); if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "SetConfiguration: PeerConnection is closed."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_STATE) + << "SetConfiguration: PeerConnection is closed."); } const bool has_local_description = local_description() != nullptr; @@ -1624,9 +1637,9 @@ RTCError PeerConnection::SetConfiguration( if (has_local_description && configuration.crypto_options != configuration_.crypto_options) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change crypto_options after calling " - "SetLocalDescription."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Can't change crypto_options after calling " + "SetLocalDescription."); } // Create a new, configuration object whose peerconnection config @@ -1680,8 +1693,8 @@ RTCError PeerConnection::SetConfiguration( modified_config.stun_candidate_keepalive_interval, has_local_description); })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to apply configuration to PortAllocator."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to apply configuration to PortAllocator."); } configuration_ = modified_config; @@ -1717,36 +1730,36 @@ RTCError PeerConnection::SetBitrate(const BitrateSettings& bitrate) { RTC_DCHECK_RUN_ON(worker_thread()); if (!call_) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "PeerConnection is closed."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_STATE) + << "PeerConnection is closed."); } const bool has_min = bitrate.min_bitrate_bps.has_value(); const bool has_start = bitrate.start_bitrate_bps.has_value(); const bool has_max = bitrate.max_bitrate_bps.has_value(); if (has_min && *bitrate.min_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "min_bitrate_bps <= 0"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "min_bitrate_bps <= 0"); } if (has_start) { if (has_min && *bitrate.start_bitrate_bps < *bitrate.min_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "start_bitrate_bps < min_bitrate_bps"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "start_bitrate_bps < min_bitrate_bps"); } else if (*bitrate.start_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "curent_bitrate_bps < 0"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "curent_bitrate_bps < 0"); } } if (has_max) { if (has_start && *bitrate.max_bitrate_bps < *bitrate.start_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < start_bitrate_bps"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "max_bitrate_bps < start_bitrate_bps"); } else if (has_min && *bitrate.max_bitrate_bps < *bitrate.min_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < min_bitrate_bps"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "max_bitrate_bps < min_bitrate_bps"); } else if (*bitrate.max_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < 0"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "max_bitrate_bps < 0"); } } @@ -1923,9 +1936,6 @@ void PeerConnection::Close() { transceiver->StopInternal(); } } - // Ensure that all asynchronous stats requests are completed before destroying - // the transport controller below. - stats_collector_.WaitForPendingRequest(); // Don't destroy BaseChannels until after stats has been cleaned up so that // the last stats request can still read from the channels. @@ -2436,21 +2446,6 @@ bool PeerConnection::GetSslRole(const std::string& content_name, return false; } -bool PeerConnection::GetTransportDescription( - const SessionDescription* description, - const std::string& content_name, - TransportDescription* tdesc) { - if (!description || !tdesc) { - return false; - } - const TransportInfo* transport_info = - description->GetTransportInfoByName(content_name); - if (!transport_info) { - return false; - } - *tdesc = transport_info->description; - return true; -} std::vector PeerConnection::GetDataChannelStats() const { RTC_DCHECK_RUN_ON(network_thread()); @@ -2568,7 +2563,7 @@ void PeerConnection::OnTransportControllerConnectionState( std::vector> transceiver_info; if (ConfiguredForMedia()) { for (const auto& t : rtp_manager()->transceivers()->List()) { - if (t->internal()->channel()) { + if (t->internal()->HasChannel()) { std::optional mid = t->mid(); if (mid) { transceiver_info.emplace_back(*mid, t->media_type()); @@ -3091,9 +3086,9 @@ bool PeerConnection::OnTransportChanged( if (ConfiguredForMedia()) { for (const auto& transceiver : rtp_manager()->transceivers()->UnsafeList()) { - ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && channel->mid() == mid) { - ret = channel->SetRtpTransport(rtp_transport); + auto internal = transceiver->internal(); + if (internal->HasChannel() && internal->mid() == mid) { + ret = internal->SetChannelRtpTransport(rtp_transport); } } } @@ -3131,14 +3126,16 @@ RTCError PeerConnection::StartSctpTransport(const SctpOptions& options) { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(sctp_mid_s_); - network_thread()->PostTask( - SafeTask(network_thread_safety_, [this, mid = *sctp_mid_s_, options] { - scoped_refptr sctp_transport = - transport_controller_n()->GetSctpTransport(mid); - if (sctp_transport) - sctp_transport->Start(options); - })); - return RTCError::OK(); + return network_thread()->BlockingCall([this, mid = *sctp_mid_s_, options] { + scoped_refptr sctp_transport = + transport_controller_n()->GetSctpTransport(mid); + RTC_DCHECK(sctp_transport); + if (!sctp_transport || !sctp_transport->Start(options)) { + return LOG_ERROR(RTCError(RTCErrorType::INVALID_MODIFICATION) + << "Failed to start SCTP transport."); + } + return RTCError::OK(); + }); } CryptoOptions PeerConnection::GetCryptoOptions() { @@ -3196,16 +3193,14 @@ PeerConnection::InitializeRtcpCallback() { absl::AnyInvocable PeerConnection::InitializeUnDemuxablePacketHandler() { return [this](const RtpPacketReceived& parsed_packet) { - worker_thread()->PostTask( - SafeTask(worker_thread_safety_, [this, parsed_packet]() { - // Deliver the packet anyway to Call to allow Call to do BWE. - // Even if there is no media receiver, the packet has still - // been received on the network and has been correcly parsed. - call_ptr_->Receiver()->DeliverRtpPacket( - MediaType::ANY, parsed_packet, - /*undemuxable_packet_handler=*/ - [](const RtpPacketReceived& packet) { return false; }); - })); + RTC_DCHECK_RUN_ON(network_thread()); + // Deliver the packet anyway to Call to allow Call to do BWE. + // Even if there is no media receiver, the packet has still + // been received on the network and has been correctly parsed. + call_ptr_->Receiver()->DeliverRtpPacket( + MediaType::ANY, parsed_packet, + /*undemuxable_packet_handler=*/ + [](const RtpPacketReceived& packet) { return false; }); }; } @@ -3218,12 +3213,8 @@ void PeerConnection::RunOnSignalingThread(absl::AnyInvocable task) { if (signaling_thread()->IsCurrent()) { std::move(task)(); } else { - // Consider if we can use PostTask instead in some situations: - // signaling_thread()->PostTask( - // SafeTask(signaling_thread_safety_.flag(), std::move(task))); - // Currently we use BlockingCall() to be compatible with how the - // api proxies work by default. - signaling_thread()->BlockingCall([&] { std::move(task)(); }); + signaling_thread()->PostTask( + SafeTask(signaling_thread_safety_.flag(), std::move(task))); } } diff --git a/pc/peer_connection.h b/pc/peer_connection.h index 8d2e592b87f..15743e26dc5 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -67,7 +67,6 @@ #include "p2p/base/ice_transport_internal.h" #include "p2p/base/port.h" #include "p2p/base/port_allocator.h" -#include "p2p/base/transport_description.h" #include "p2p/dtls/dtls_transport_factory.h" #include "pc/channel_interface.h" #include "pc/codec_vendor.h" @@ -94,6 +93,7 @@ #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" #include "rtc_base/weak_ptr.h" @@ -133,10 +133,11 @@ class PeerConnection : public PeerConnectionInternal, const ServerAddresses& stun_servers, const std::vector& turn_servers); - scoped_refptr local_streams() override; - scoped_refptr remote_streams() override; - bool AddStream(MediaStreamInterface* local_stream) override; - void RemoveStream(MediaStreamInterface* local_stream) override; + PLAN_B_ONLY scoped_refptr local_streams() override; + PLAN_B_ONLY scoped_refptr remote_streams() + override; + PLAN_B_ONLY bool AddStream(MediaStreamInterface* local_stream) override; + PLAN_B_ONLY void RemoveStream(MediaStreamInterface* local_stream) override; RTCErrorOr> AddTrack( scoped_refptr track, @@ -163,7 +164,7 @@ class PeerConnection : public PeerConnectionInternal, webrtc::MediaType media_type, const RtpTransceiverInit& init) override; - scoped_refptr CreateSender( + PLAN_B_ONLY scoped_refptr CreateSender( const std::string& kind, const std::string& stream_id) override; @@ -177,9 +178,9 @@ class PeerConnection : public PeerConnectionInternal, const std::string& label, const DataChannelInit* config) override; // WARNING: LEGACY. See peerconnectioninterface.h - bool GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track, - StatsOutputLevel level) override; + [[deprecated]] bool GetStats(StatsObserver* observer, + MediaStreamTrackInterface* track, + StatsOutputLevel level) override; // Spec-complaint GetStats(). See peerconnectioninterface.h void GetStats(RTCStatsCollectorCallback* callback) override; void GetStats(scoped_refptr selector, @@ -416,8 +417,9 @@ class PeerConnection : public PeerConnectionInternal, bool CreateDataChannelTransport(absl::string_view mid) override; void DestroyDataChannelTransport(RTCError error) override; - // Asynchronously calls SctpTransport::Start() on the network thread for - // `sctp_mid()` if set. Called as part of setting the local description. + // Synchronously calls SctpTransport::Start() on the network thread for + // `sctp_mid()` if set. Called as part of pushing down the media descriptions + // after a complete offer/answer. RTCError StartSctpTransport(const SctpOptions& options) override; // Returns the CryptoOptions set as RTCConfiguration.crypto_options for this @@ -526,8 +528,6 @@ class PeerConnection : public PeerConnectionInternal, void OnSelectedCandidatePairChanged(const CandidatePairChangeEvent& event) RTC_RUN_ON(signaling_thread()); - void OnNegotiationNeeded(); - const JsepTransportController* transport_controller_s() const RTC_RUN_ON(signaling_thread()) { return transport_controller_copy_; @@ -562,12 +562,6 @@ class PeerConnection : public PeerConnectionInternal, // This function should only be called from the worker thread. void StopRtcEventLog_w(); - // Returns true and the TransportInfo of the given `content_name` - // from `description`. Returns false if it's not available. - static bool GetTransportDescription(const SessionDescription* description, - const std::string& content_name, - TransportDescription* info); - // Returns the media index for a local ice candidate given the content name. // Returns false if the local session description does not have a media // content called `content_name`. diff --git a/pc/peer_connection_audio_options_unittest.cc b/pc/peer_connection_audio_options_unittest.cc index 6cd46243c9c..193b95b3a92 100644 --- a/pc/peer_connection_audio_options_unittest.cc +++ b/pc/peer_connection_audio_options_unittest.cc @@ -141,7 +141,7 @@ TEST_F(PeerConnectionAudioOptionsTest, AudioOptionsAppliedOnCreateChannel) { auto transceivers = pc()->GetTransceiversInternal(); ASSERT_EQ(transceivers.size(), 1u); auto* transceiver_impl = transceivers[0]->internal(); - ASSERT_THAT(transceiver_impl->channel(), IsNull()); + ASSERT_FALSE(transceiver_impl->HasChannel()); // Create offer and set local description to trigger CreateChannel. auto offer_observer = make_ref_counted(loop_); @@ -157,9 +157,9 @@ TEST_F(PeerConnectionAudioOptionsTest, AudioOptionsAppliedOnCreateChannel) { // Verify that now the channel() exists and that the options were applied to // the voice engine. - ASSERT_THAT(transceiver_impl->channel(), NotNull()); + ASSERT_TRUE(transceiver_impl->HasChannel()); - auto* media_channel = transceiver_impl->channel()->media_receive_channel(); + auto* media_channel = transceiver_impl->media_receive_channel(); ASSERT_TRUE(media_channel); auto* voice_channel = static_cast(media_channel); diff --git a/pc/peer_connection_bundle_unittest.cc b/pc/peer_connection_bundle_unittest.cc index d75a22edfbc..6d4dc983cd6 100644 --- a/pc/peer_connection_bundle_unittest.cc +++ b/pc/peer_connection_bundle_unittest.cc @@ -43,7 +43,7 @@ #include "p2p/base/p2p_constants.h" #include "p2p/base/port_allocator.h" #include "p2p/base/transport_info.h" -#include "pc/channel.h" +#include "pc/jsep_transport_controller.h" #include "pc/peer_connection.h" #include "pc/peer_connection_wrapper.h" #include "pc/rtp_transceiver.h" @@ -63,6 +63,7 @@ #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" #ifdef WEBRTC_ANDROID @@ -121,28 +122,24 @@ class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper { } RtpTransportInternal* voice_rtp_transport() { - return (voice_channel() ? voice_channel()->rtp_transport() : nullptr); - } - - VoiceChannel* voice_channel() { auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal(); for (const auto& transceiver : transceivers) { - if (transceiver->media_type() == MediaType::AUDIO) { - return static_cast(transceiver->internal()->channel()); + if (transceiver->media_type() == MediaType::AUDIO && transceiver->mid()) { + return GetInternalPeerConnection() + ->transport_controller_n() + ->GetRtpTransport(*transceiver->mid()); } } return nullptr; } RtpTransportInternal* video_rtp_transport() { - return (video_channel() ? video_channel()->rtp_transport() : nullptr); - } - - VideoChannel* video_channel() { auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal(); for (const auto& transceiver : transceivers) { - if (transceiver->media_type() == MediaType::VIDEO) { - return static_cast(transceiver->internal()->channel()); + if (transceiver->media_type() == MediaType::VIDEO && transceiver->mid()) { + return GetInternalPeerConnection() + ->transport_controller_n() + ->GetRtpTransport(*transceiver->mid()); } } return nullptr; @@ -268,7 +265,7 @@ class PeerConnectionBundleBaseTest : public ::testing::Test { } VirtualSocketServer vss_; - AutoSocketServerThread main_; + test::RunLoop main_; const SdpSemantics sdp_semantics_; }; diff --git a/pc/peer_connection_crypto_unittest.cc b/pc/peer_connection_crypto_unittest.cc index 20d85539903..f7fee87ed5f 100644 --- a/pc/peer_connection_crypto_unittest.cc +++ b/pc/peer_connection_crypto_unittest.cc @@ -52,6 +52,7 @@ #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -156,7 +157,7 @@ class PeerConnectionCryptoBaseTest : public ::testing::Test { } std::unique_ptr vss_; - AutoSocketServerThread main_; + test::RunLoop main_; scoped_refptr pc_factory_; const SdpSemantics sdp_semantics_; }; diff --git a/pc/peer_connection_data_channel_unittest.cc b/pc/peer_connection_data_channel_unittest.cc index 694f20702a7..e086ce01a5c 100644 --- a/pc/peer_connection_data_channel_unittest.cc +++ b/pc/peer_connection_data_channel_unittest.cc @@ -34,6 +34,7 @@ #include "test/gmock.h" #include "test/gtest.h" #include "test/pc/sctp/fake_sctp_transport.h" +#include "test/run_loop.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -158,7 +159,7 @@ class PeerConnectionDataChannelBaseTest : public ::testing::Test { } std::unique_ptr vss_; - AutoSocketServerThread main_; + test::RunLoop main_; const SdpSemantics sdp_semantics_; }; diff --git a/pc/peer_connection_encodings_integrationtest.cc b/pc/peer_connection_encodings_integrationtest.cc index 6b84d6bf933..9b9ee258c15 100644 --- a/pc/peer_connection_encodings_integrationtest.cc +++ b/pc/peer_connection_encodings_integrationtest.cc @@ -2129,6 +2129,64 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, EXPECT_EQ(parameters.codecs[0].name, opus->name); } +TEST_F(PeerConnectionEncodingsIntegrationTest, + EncodingParametersRedEnabledBeforeNegotiationAudioWithFieldTrial) { + scoped_refptr local_pc_wrapper = + CreatePc("WebRTC-PayloadTypesInTransport/Enabled/"); + scoped_refptr remote_pc_wrapper = + CreatePc("WebRTC-PayloadTypesInTransport/Enabled/"); + ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); + + std::vector send_codecs = + local_pc_wrapper->pc_factory() + ->GetRtpSenderCapabilities(MediaType::AUDIO) + .codecs; + + std::optional opus = + local_pc_wrapper->FindFirstSendCodecWithName(MediaType::AUDIO, "opus"); + ASSERT_TRUE(opus); + + std::optional red = + local_pc_wrapper->FindFirstSendCodecWithName(MediaType::AUDIO, "red"); + ASSERT_TRUE(red); + + RtpTransceiverInit init; + init.direction = RtpTransceiverDirection::kSendOnly; + RtpEncodingParameters encoding_parameters; + encoding_parameters.codec = opus; + init.send_encodings.push_back(encoding_parameters); + + auto transceiver_or_error = + local_pc_wrapper->pc()->AddTransceiver(MediaType::AUDIO, init); + ASSERT_TRUE(transceiver_or_error.ok()); + scoped_refptr audio_transceiver = + transceiver_or_error.MoveValue(); + + // Preferring RED over Opus should enable RED with Opus encoding. + send_codecs[0] = red.value(); + send_codecs[1] = opus.value(); + + ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok()); + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + local_pc_wrapper->WaitForConnection(); + remote_pc_wrapper->WaitForConnection(); + + RtpParameters parameters = audio_transceiver->sender()->GetParameters(); + EXPECT_EQ(parameters.encodings[0].codec, opus); + EXPECT_EQ(parameters.codecs[0].name, red->name); + + // Check that it's possible to switch back to Opus without RED. + send_codecs[0] = opus.value(); + send_codecs[1] = red.value(); + + ASSERT_TRUE(audio_transceiver->SetCodecPreferences(send_codecs).ok()); + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + + parameters = audio_transceiver->sender()->GetParameters(); + EXPECT_EQ(parameters.encodings[0].codec, opus); + EXPECT_EQ(parameters.codecs[0].name, opus->name); +} + TEST_F(PeerConnectionEncodingsIntegrationTest, SetParametersRejectsScalabilityModeForSelectedCodec) { scoped_refptr local_pc_wrapper = CreatePc(); diff --git a/pc/peer_connection_end_to_end_unittest.cc b/pc/peer_connection_end_to_end_unittest.cc index 763b70e7d06..ee6799cb3f3 100644 --- a/pc/peer_connection_end_to_end_unittest.cc +++ b/pc/peer_connection_end_to_end_unittest.cc @@ -48,6 +48,7 @@ #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" #ifdef WEBRTC_ANDROID @@ -65,6 +66,8 @@ using ::testing::AtLeast; using ::testing::Eq; using ::testing::Ge; using ::testing::Gt; +using ::testing::IsTrue; +using ::testing::Ne; using ::testing::SizeIs; using ::testing::StrictMock; using ::testing::Values; @@ -244,7 +247,7 @@ class PeerConnectionEndToEndBaseTest : public ::testing::Test { } protected: - AutoThread main_thread_; + test::RunLoop main_thread_; PhysicalSocketServer pss_; Environment env_; std::unique_ptr network_thread_; @@ -285,13 +288,13 @@ std::unique_ptr CreateForwardingMockDecoder( .WillRepeatedly([dec] { return dec->Channels(); }); EXPECT_CALL(*mock_decoder, DecodeInternal(_, _, _, _, _)) .Times(AtLeast(1)) - .WillRepeatedly( - [dec](const uint8_t* encoded, size_t encoded_len, int sample_rate_hz, - int16_t* decoded, AudioDecoder::SpeechType* speech_type) { - return dec->Decode(encoded, encoded_len, sample_rate_hz, - std::numeric_limits::max(), decoded, - speech_type); - }); + .WillRepeatedly([dec](const uint8_t* encoded, size_t encoded_len, + int sample_rate_hz, int16_t* decoded, + AudioDecoder::SpeechType* speech_type) { + return dec->Decode(encoded, encoded_len, sample_rate_hz, + std::numeric_limits::max(), decoded, + speech_type); + }); EXPECT_CALL(*mock_decoder, Die()); EXPECT_CALL(*mock_decoder, HasDecodePlc()).WillRepeatedly([dec] { return dec->HasDecodePlc(); @@ -324,15 +327,14 @@ scoped_refptr CreateForwardingMockDecoderFactory( }); EXPECT_CALL(*mock_decoder_factory, Create) .Times(AtLeast(2)) - .WillRepeatedly( - [real_decoder_factory](const Environment& env, - const SdpAudioFormat& format, - std::optional /* pair */) { - auto real_decoder = real_decoder_factory->Create(env, format); - return real_decoder - ? CreateForwardingMockDecoder(std::move(real_decoder)) - : nullptr; - }); + .WillRepeatedly([real_decoder_factory]( + const Environment& env, const SdpAudioFormat& format, + std::optional /* pair */) { + auto real_decoder = real_decoder_factory->Create(env, format); + return real_decoder + ? CreateForwardingMockDecoder(std::move(real_decoder)) + : nullptr; + }); return mock_decoder_factory; } @@ -538,9 +540,21 @@ TEST_P(PeerConnectionEndToEndTest, DataChannelIdAssignment) { scoped_refptr callee_dc_1( callee_->CreateDataChannel("data", init)); + MockDataChannelObserver caller_dc_1_observer(caller_dc_1.get()); + MockDataChannelObserver callee_dc_1_observer(callee_dc_1.get()); + Negotiate(); WaitForConnection(); + EXPECT_THAT( + WaitUntil([&] { return caller_dc_1_observer.IsOpen(); }, IsTrue()), + IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return callee_dc_1_observer.IsOpen(); }, IsTrue()), + IsRtcOk()); + + EXPECT_NE(-1, caller_dc_1->id()); + EXPECT_NE(-1, callee_dc_1->id()); EXPECT_EQ(1, caller_dc_1->id() % 2); EXPECT_EQ(0, callee_dc_1->id() % 2); @@ -549,6 +563,18 @@ TEST_P(PeerConnectionEndToEndTest, DataChannelIdAssignment) { scoped_refptr callee_dc_2( callee_->CreateDataChannel("data", init)); + MockDataChannelObserver caller_dc_2_observer(caller_dc_2.get()); + MockDataChannelObserver callee_dc_2_observer(callee_dc_2.get()); + + EXPECT_THAT( + WaitUntil([&] { return caller_dc_2_observer.IsOpen(); }, IsTrue()), + IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return callee_dc_2_observer.IsOpen(); }, IsTrue()), + IsRtcOk()); + + EXPECT_NE(-1, caller_dc_2->id()); + EXPECT_NE(-1, callee_dc_2->id()); EXPECT_EQ(1, caller_dc_2->id() % 2); EXPECT_EQ(0, callee_dc_2->id() % 2); } @@ -664,7 +690,7 @@ TEST_P(PeerConnectionEndToEndTest, CloseDataChannelRemotelyWhileNotReferenced) { // Wait for a bit longer so the remote data channel will receive the // close message and be destroyed. - Thread::Current()->ProcessMessages(100); + main_thread_.RunFor(webrtc::TimeDelta::Millis(100)); } // Test behavior of creating too many datachannels. diff --git a/pc/peer_connection_factory_unittest.cc b/pc/peer_connection_factory_unittest.cc index ed93e64c752..346125c2870 100644 --- a/pc/peer_connection_factory_unittest.cc +++ b/pc/peer_connection_factory_unittest.cc @@ -68,6 +68,7 @@ #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -262,7 +263,7 @@ class PeerConnectionFactoryTest : public ::testing::Test { } std::unique_ptr socket_server_; - AutoSocketServerThread main_thread_; + test::RunLoop main_thread_; scoped_refptr factory_; NullPeerConnectionObserver observer_; std::unique_ptr port_allocator_; diff --git a/pc/peer_connection_field_trial_tests.cc b/pc/peer_connection_field_trial_tests.cc index 4977bdd07f1..fa323677b04 100644 --- a/pc/peer_connection_field_trial_tests.cc +++ b/pc/peer_connection_field_trial_tests.cc @@ -36,6 +36,7 @@ #include "system_wrappers/include/clock.h" #include "test/create_test_field_trials.h" #include "test/gtest.h" +#include "test/run_loop.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -93,7 +94,7 @@ class PeerConnectionFieldTrialTest : public ::testing::Test { Clock* const clock_; std::unique_ptr socket_server_; - AutoSocketServerThread main_thread_; + test::RunLoop main_thread_; scoped_refptr pc_factory_ = nullptr; PeerConnectionInterface::RTCConfiguration config_; }; diff --git a/pc/peer_connection_header_extension_unittest.cc b/pc/peer_connection_header_extension_unittest.cc index c2b8fa7b03a..a81cd9b2c76 100644 --- a/pc/peer_connection_header_extension_unittest.cc +++ b/pc/peer_connection_header_extension_unittest.cc @@ -40,6 +40,7 @@ #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { @@ -116,7 +117,7 @@ class PeerConnectionHeaderExtensionTest } std::unique_ptr socket_server_; - AutoSocketServerThread main_thread_; + test::RunLoop main_thread_; std::vector extensions_; }; @@ -739,7 +740,7 @@ TEST_P(PeerConnectionHeaderExtensionUnifiedPlanTest, } TEST_P(PeerConnectionHeaderExtensionUnifiedPlanTest, - TransceiversAddedAfterFirstTransceiverCopyExtensions) { + TransceiversAddedAfterFirstTransceiverDoNotCopyExtensionsFromStopped) { MediaType media_type; SdpSemantics semantics; std::tie(media_type, semantics) = GetParam(); @@ -749,22 +750,29 @@ TEST_P(PeerConnectionHeaderExtensionUnifiedPlanTest, auto modified_extensions = transceiver1->GetHeaderExtensionsToNegotiate(); modified_extensions[3].direction = RtpTransceiverDirection::kStopped; transceiver1->SetHeaderExtensionsToNegotiate(modified_extensions); + // Create a description with two sections, and set it as local. + auto session_description = pc1->CreateOffer(); + pc1->SetLocalDescription(std::move(session_description)); + // Stop the transceiver. That should make it ignored for copying purposes. + transceiver1->StopStandard(); auto transceiver2 = pc1->AddTransceiver(media_type); + session_description = pc1->CreateOffer(); - auto session_description = pc1->CreateOffer(); + ASSERT_THAT(session_description->description()->contents().size(), Eq(2)); EXPECT_THAT(session_description->description() ->contents()[0] .media_description() ->rtp_header_extensions(), ElementsAre(Field(&RtpExtension::uri, "uri2"), Field(&RtpExtension::uri, "uri3"))); - // the uri4 extension is disabled in the newly added transceiver too + // the uri4 extension is enabled in the newly added transceiver EXPECT_THAT(session_description->description() ->contents()[1] .media_description() ->rtp_header_extensions(), ElementsAre(Field(&RtpExtension::uri, "uri2"), - Field(&RtpExtension::uri, "uri3"))); + Field(&RtpExtension::uri, "uri3"), + Field(&RtpExtension::uri, "uri4"))); } TEST_P(PeerConnectionHeaderExtensionUnifiedPlanTest, diff --git a/pc/peer_connection_histogram_unittest.cc b/pc/peer_connection_histogram_unittest.cc index 89a323259ed..251d46df52f 100644 --- a/pc/peer_connection_histogram_unittest.cc +++ b/pc/peer_connection_histogram_unittest.cc @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include #include @@ -43,6 +44,7 @@ #include "system_wrappers/include/metrics.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -74,8 +76,8 @@ a=rtpmap:101 fake_audio_codec/8000 constexpr char kUsagePatternMetric[] = "WebRTC.PeerConnection.UsagePattern"; constexpr TimeDelta kDefaultTimeout = TimeDelta::Millis(10000); -const SocketAddress kLocalAddrs[2] = {SocketAddress("1.1.1.1", 0), - SocketAddress("2.2.2.2", 0)}; +const std::array kLocalAddrs{SocketAddress("1.1.1.1", 0), + SocketAddress("2.2.2.2", 0)}; const SocketAddress kPrivateLocalAddress("10.1.1.1", 0); const SocketAddress kPrivateIpv6LocalAddress("fd12:3456:789a:1::1", 0); @@ -343,7 +345,7 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test { int next_local_address_ = 0; VirtualSocketServer vss_; - AutoSocketServerThread main_; + test::RunLoop main_; }; TEST_F(PeerConnectionUsageHistogramTest, UsageFingerprintHistogramFromTimeout) { diff --git a/pc/peer_connection_ice_unittest.cc b/pc/peer_connection_ice_unittest.cc index fb77c733e26..49d59ec6e81 100644 --- a/pc/peer_connection_ice_unittest.cc +++ b/pc/peer_connection_ice_unittest.cc @@ -28,7 +28,6 @@ #include "api/peer_connection_interface.h" #include "api/rtc_error.h" #include "api/scoped_refptr.h" -#include "api/test/rtc_error_matchers.h" #include "api/units/time_delta.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" @@ -36,7 +35,6 @@ #include "p2p/base/transport_description.h" #include "p2p/base/transport_info.h" #include "p2p/test/fake_port_allocator.h" -#include "pc/channel_interface.h" #include "pc/dtls_transport.h" #include "pc/media_session.h" #include "pc/peer_connection.h" @@ -52,7 +50,7 @@ #include "rtc_base/socket_server.h" #include "rtc_base/thread.h" #include "test/gtest.h" -#include "test/wait_until.h" +#include "test/run_loop.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -97,7 +95,15 @@ constexpr int64_t kWaitTimeout = 10000; class PeerConnectionWrapperForIceTest : public PeerConnectionWrapper { public: - using PeerConnectionWrapper::PeerConnectionWrapper; + PeerConnectionWrapperForIceTest( + scoped_refptr pc_factory, + scoped_refptr pc, + std::unique_ptr observer, + FakeNetworkManager* network = nullptr) + : PeerConnectionWrapper(std::move(pc_factory), + std::move(pc), + std::move(observer)), + network_(network) {} std::unique_ptr CreateJsepCandidateForFirstTransport( Candidate* candidate) { @@ -129,12 +135,21 @@ class PeerConnectionWrapperForIceTest : public PeerConnectionWrapper { return candidates; } - FakeNetworkManager* network() { return network_; } + void AddInterface(const SocketAddress& iface) { + network_thread()->BlockingCall([&] { network_->AddInterface(iface); }); + } - void set_network(FakeNetworkManager* network) { network_ = network; } + void RemoveInterface(const SocketAddress& iface) { + network_thread()->BlockingCall([&] { network_->RemoveInterface(iface); }); + } + + Thread* network_thread() { + return GetInternalPeerConnection()->network_thread(); + } + FakeNetworkManager* network() { return network_; } private: - FakeNetworkManager* network_; + FakeNetworkManager* const network_; }; class PeerConnectionIceBaseTest : public ::testing::Test { @@ -142,7 +157,11 @@ class PeerConnectionIceBaseTest : public ::testing::Test { typedef std::unique_ptr WrapperPtr; explicit PeerConnectionIceBaseTest(SdpSemantics sdp_semantics) - : main_(&vss_), sdp_semantics_(sdp_semantics) { + : network_thread_(new Thread(&vss_)), + worker_thread_(Thread::Create()), + sdp_semantics_(sdp_semantics) { + RTC_CHECK(network_thread_->Start()); + RTC_CHECK(worker_thread_->Start()); #ifdef WEBRTC_ANDROID InitializeAndroidObjects(); #endif @@ -154,8 +173,8 @@ class PeerConnectionIceBaseTest : public ::testing::Test { WrapperPtr CreatePeerConnection(const RTCConfiguration& config) { PeerConnectionFactoryDependencies pcf_deps; - pcf_deps.network_thread = Thread::Current(); - pcf_deps.worker_thread = Thread::Current(); + pcf_deps.network_thread = network_thread_.get(); + pcf_deps.worker_thread = worker_thread_.get(); pcf_deps.signaling_thread = Thread::Current(); pcf_deps.socket_factory = &vss_; auto network_manager = @@ -188,10 +207,9 @@ class PeerConnectionIceBaseTest : public ::testing::Test { } observer->SetPeerConnectionInterface(result.value().get()); - auto wrapper = std::make_unique( - std::move(pc_factory), result.MoveValue(), std::move(observer)); - wrapper->set_network(fake_network); - return wrapper; + return std::make_unique( + std::move(pc_factory), result.MoveValue(), std::move(observer), + fake_network); } // Accepts the same arguments as CreatePeerConnection and adds default audio @@ -273,8 +291,10 @@ class PeerConnectionIceBaseTest : public ::testing::Test { if (transceiver->media_type() == MediaType::AUDIO) { auto dtls_transport = pc->transport_controller_s()->LookupDtlsTransportByMid( - transceiver->internal()->channel()->mid()); - return dtls_transport->ice_transport()->internal()->GetIceRole(); + *transceiver->internal()->mid()); + return network_thread_->BlockingCall([&dtls_transport]() { + return dtls_transport->ice_transport()->internal()->GetIceRole(); + }); } } RTC_DCHECK_NOTREACHED(); @@ -311,8 +331,10 @@ class PeerConnectionIceBaseTest : public ::testing::Test { return sdesc->AddCandidate(jsep_candidate.get()); } + test::RunLoop main_; VirtualSocketServer vss_; - AutoSocketServerThread main_; + std::unique_ptr network_thread_; + std::unique_ptr worker_thread_; const SdpSemantics sdp_semantics_; }; @@ -357,15 +379,12 @@ TEST_P(PeerConnectionIceTest, OfferContainsGatheredCandidates) { const SocketAddress kLocalAddress("1.1.1.1", 0); auto caller = CreatePeerConnectionWithAudioVideo(); - caller->network()->AddInterface(kLocalAddress); + caller->AddInterface(kLocalAddress); // Start ICE candidate gathering by setting the local offer. ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); - EXPECT_THAT(WaitUntil([&] { return caller->IsIceGatheringDone(); }, - ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kIceCandidatesTimeout)}), - IsRtcOk()); + EXPECT_TRUE(caller->RunUntilIceGatheringDone(main_)); std::unique_ptr offer = caller->CreateOffer(); EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size()); @@ -381,15 +400,12 @@ TEST_P(PeerConnectionIceTest, AnswerContainsGatheredCandidates) { auto caller = CreatePeerConnectionWithAudioVideo(); auto callee = CreatePeerConnectionWithAudioVideo(); - caller->network()->AddInterface(kCallerAddress); + caller->AddInterface(kCallerAddress); ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer())); - EXPECT_THAT(WaitUntil([&] { return callee->IsIceGatheringDone(); }, - ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kIceCandidatesTimeout)}), - IsRtcOk()); + EXPECT_TRUE(callee->RunUntilIceGatheringDone(main_)); auto* answer = callee->pc()->local_description(); EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size()); @@ -461,10 +477,10 @@ TEST_P(PeerConnectionIceTest, NoIceCandidatesBeforeSetLocalDescription) { const SocketAddress kLocalAddress("1.1.1.1", 0); auto caller = CreatePeerConnectionWithAudioVideo(); - caller->network()->AddInterface(kLocalAddress); + caller->AddInterface(kLocalAddress); // Pump for 1 second and verify that no candidates are generated. - Thread::Current()->ProcessMessages(1000); + main_.RunFor(TimeDelta::Millis(1000)); EXPECT_EQ(0u, caller->observer()->candidates_.size()); } @@ -474,7 +490,7 @@ TEST_P(PeerConnectionIceTest, auto caller = CreatePeerConnectionWithAudioVideo(); auto callee = CreatePeerConnectionWithAudioVideo(); - caller->network()->AddInterface(kCallerAddress); + caller->AddInterface(kCallerAddress); std::unique_ptr offer = caller->CreateOfferAndSetAsLocal(); @@ -483,7 +499,7 @@ TEST_P(PeerConnectionIceTest, ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer))); // Pump for 1 second and verify that no candidates are generated. - Thread::Current()->ProcessMessages(1000); + main_.RunFor(TimeDelta::Millis(1000)); EXPECT_EQ(0u, callee->observer()->candidates_.size()); } @@ -721,14 +737,11 @@ TEST_P(PeerConnectionIceTest, CandidatesGeneratedForEachLocalInterface) { const SocketAddress kLocalAddress2("2.2.2.2", 0); auto caller = CreatePeerConnectionWithAudioVideo(); - caller->network()->AddInterface(kLocalAddress1); - caller->network()->AddInterface(kLocalAddress2); + caller->AddInterface(kLocalAddress1); + caller->AddInterface(kLocalAddress2); caller->CreateOfferAndSetAsLocal(); - EXPECT_THAT(WaitUntil([&] { return caller->IsIceGatheringDone(); }, - ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kIceCandidatesTimeout)}), - IsRtcOk()); + EXPECT_TRUE(caller->RunUntilIceGatheringDone(main_)); auto candidates = caller->observer()->GetCandidatesByMline(0); EXPECT_PRED_FORMAT2(AssertIpInCandidates, kLocalAddress1, candidates); @@ -787,15 +800,15 @@ TEST_P(PeerConnectionIceTest, AsyncAddIceCandidateIsAddedToRemoteDescription) { auto jsep_candidate = callee->CreateJsepCandidateForFirstTransport(&candidate); bool operation_completed = false; - callee->pc()->AddIceCandidate(std::move(jsep_candidate), - [&operation_completed](RTCError result) { - EXPECT_TRUE(result.ok()); - operation_completed = true; - }); - EXPECT_THAT( - WaitUntil([&] { return operation_completed; }, ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kWaitTimeout)}), - IsRtcOk()); + callee->pc()->AddIceCandidate( + std::move(jsep_candidate), + [&operation_completed, &run_loop = main_](RTCError result) { + EXPECT_TRUE(result.ok()); + operation_completed = true; + run_loop.Quit(); + }); + main_.RunFor(TimeDelta::Millis(kWaitTimeout)); + EXPECT_TRUE(operation_completed); auto candidates = callee->GetIceCandidatesFromRemoteDescription(); ASSERT_EQ(1u, candidates.size()); @@ -815,9 +828,11 @@ TEST_P(PeerConnectionIceTest, auto jsep_candidate = callee->CreateJsepCandidateForFirstTransport(&candidate); bool operation_completed = false; - callee->pc()->AddIceCandidate( - std::move(jsep_candidate), - [&operation_completed](RTCError result) { operation_completed = true; }); + callee->pc()->AddIceCandidate(std::move(jsep_candidate), + [&operation_completed](RTCError result) { + EXPECT_TRUE(result.ok()); + operation_completed = true; + }); EXPECT_TRUE(operation_completed); } @@ -831,8 +846,8 @@ TEST_P(PeerConnectionIceTest, ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); // Chain an operation that will block AddIceCandidate() from executing. - auto answer_observer = - make_ref_counted(); + auto answer_observer = make_ref_counted( + main_.QuitClosure()); callee->pc()->CreateAnswer(answer_observer.get(), RTCOfferAnswerOptions()); auto jsep_candidate = @@ -841,13 +856,11 @@ TEST_P(PeerConnectionIceTest, callee->pc()->AddIceCandidate( std::move(jsep_candidate), [&operation_completed](RTCError result) { operation_completed = true; }); - // The operation will not be able to complete until we EXPECT_TRUE_WAIT() - // allowing CreateAnswer() to complete. + // The operation will not be able to complete until we let the run loop + // process CreateAnswer(). EXPECT_FALSE(operation_completed); - EXPECT_THAT( - WaitUntil([&] { return answer_observer->called(); }, ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kWaitTimeout)}), - IsRtcOk()); + main_.Run(); + EXPECT_TRUE(answer_observer->called()); // As soon as it does, AddIceCandidate() will execute without delay, so it // must also have completed. EXPECT_TRUE(operation_completed); @@ -866,16 +879,16 @@ TEST_P(PeerConnectionIceTest, bool operation_completed = false; caller->pc()->AddIceCandidate( - std::move(jsep_candidate), [&operation_completed](RTCError result) { + std::move(jsep_candidate), + [&operation_completed, &run_loop = main_](RTCError result) { EXPECT_FALSE(result.ok()); EXPECT_EQ(result.message(), std::string("The remote description was null")); operation_completed = true; + run_loop.Quit(); }); - EXPECT_THAT( - WaitUntil([&] { return operation_completed; }, ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kWaitTimeout)}), - IsRtcOk()); + main_.RunFor(TimeDelta::Millis(kWaitTimeout)); + EXPECT_TRUE(operation_completed); } TEST_P(PeerConnectionIceTest, @@ -888,8 +901,8 @@ TEST_P(PeerConnectionIceTest, ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); // Chain an operation that will block AddIceCandidate() from executing. - auto answer_observer = - make_ref_counted(); + auto answer_observer = make_ref_counted( + main_.QuitClosure()); callee->pc()->CreateAnswer(answer_observer.get(), RTCOfferAnswerOptions()); auto jsep_candidate = @@ -904,15 +917,15 @@ TEST_P(PeerConnectionIceTest, "AddIceCandidate failed because the session was shut down")); operation_completed = true; }); - // The operation will not be able to run until EXPECT_TRUE_WAIT(), giving us - // time to remove all references to the PeerConnection. + // The operation will not be able to run until we let the run loop + // process CreateAnswer(), giving us time to remove all references to the + // PeerConnection. EXPECT_FALSE(operation_completed); // This should delete the callee PC. callee = nullptr; - EXPECT_THAT( - WaitUntil([&] { return operation_completed; }, ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kWaitTimeout)}), - IsRtcOk()); + main_.RunFor(TimeDelta::Millis(kWaitTimeout)); + EXPECT_TRUE(answer_observer->called()); + EXPECT_TRUE(operation_completed); } TEST_P(PeerConnectionIceTest, LocalDescriptionUpdatedWhenContinualGathering) { @@ -923,20 +936,16 @@ TEST_P(PeerConnectionIceTest, LocalDescriptionUpdatedWhenContinualGathering) { config.continual_gathering_policy = PeerConnectionInterface::GATHER_CONTINUALLY; auto caller = CreatePeerConnectionWithAudioVideo(config); - caller->network()->AddInterface(kLocalAddress); + caller->AddInterface(kLocalAddress); // Start ICE candidate gathering by setting the local offer. + caller->observer()->SetOnIceCandidateCallback(main_.QuitClosure()); ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); // Since we're using continual gathering, we won't get "gathering done". - EXPECT_THAT( - WaitUntil( - [&] { - return caller->pc()->local_description()->candidates(0)->count(); - }, - ::testing::Gt(0), - {.timeout = TimeDelta::Millis(kIceCandidatesTimeout)}), - IsRtcOk()); + main_.RunFor(TimeDelta::Millis(kIceCandidatesTimeout)); + caller->observer()->SetOnIceCandidateCallback(nullptr); + EXPECT_GT(caller->pc()->local_description()->candidates(0)->count(), 0u); } // Test that when continual gathering is enabled, and a network interface goes @@ -951,32 +960,25 @@ TEST_P(PeerConnectionIceTest, config.continual_gathering_policy = PeerConnectionInterface::GATHER_CONTINUALLY; auto caller = CreatePeerConnectionWithAudioVideo(config); - caller->network()->AddInterface(kLocalAddress); + caller->AddInterface(kLocalAddress); // Start ICE candidate gathering by setting the local offer. + caller->observer()->SetOnIceCandidateCallback(main_.QuitClosure()); ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); - - EXPECT_THAT( - WaitUntil( - [&] { - return caller->pc()->local_description()->candidates(0)->count(); - }, - ::testing::Gt(0), - {.timeout = TimeDelta::Millis(kIceCandidatesTimeout)}), - IsRtcOk()); + main_.RunFor(TimeDelta::Millis(kIceCandidatesTimeout)); + caller->observer()->SetOnIceCandidateCallback(nullptr); + EXPECT_GT(caller->pc()->local_description()->candidates(0)->count(), 0u); // Remove the only network interface, causing the PeerConnection to signal // the removal of all candidates derived from this interface. - caller->network()->RemoveInterface(kLocalAddress); - - EXPECT_THAT( - WaitUntil( - [&] { - return caller->pc()->local_description()->candidates(0)->count(); - }, - ::testing::Eq(0u), - {.timeout = TimeDelta::Millis(kIceCandidatesTimeout)}), - IsRtcOk()); + caller->RemoveInterface(kLocalAddress); + + if (caller->pc()->local_description()->candidates(0)->count() != 0) { + caller->observer()->SetOnIceCandidateRemovedCallback(main_.QuitClosure()); + main_.RunFor(TimeDelta::Millis(kIceCandidatesTimeout)); + caller->observer()->SetOnIceCandidateRemovedCallback(nullptr); + } + EXPECT_EQ(caller->pc()->local_description()->candidates(0)->count(), 0u); EXPECT_LT(0, caller->observer()->num_candidates_removed_); } @@ -988,20 +990,17 @@ TEST_P(PeerConnectionIceTest, config.sdp_semantics = SdpSemantics::kUnifiedPlan; config.continual_gathering_policy = PeerConnectionInterface::GATHER_ONCE; auto caller = CreatePeerConnectionWithAudioVideo(config); - caller->network()->AddInterface(kLocalAddress); + caller->AddInterface(kLocalAddress); // Start ICE candidate gathering by setting the local offer. ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer())); - EXPECT_THAT(WaitUntil([&] { return caller->IsIceGatheringDone(); }, - ::testing::IsTrue(), - {.timeout = TimeDelta::Millis(kIceCandidatesTimeout)}), - IsRtcOk()); + EXPECT_TRUE(caller->RunUntilIceGatheringDone(main_)); - caller->network()->RemoveInterface(kLocalAddress); + caller->RemoveInterface(kLocalAddress); // Verify that the local candidates are not removed; - Thread::Current()->ProcessMessages(1000); + main_.RunFor(TimeDelta::Millis(1000)); EXPECT_EQ(0, caller->observer()->num_candidates_removed_); } @@ -1459,13 +1458,17 @@ INSTANTIATE_TEST_SUITE_P(PeerConnectionIceTest, class PeerConnectionIceConfigTest : public ::testing::Test { public: PeerConnectionIceConfigTest() - : socket_server_(CreateDefaultSocketServer()), - main_thread_(socket_server_.get()) {} + : worker_thread_(Thread::Create()), + socket_server_(CreateDefaultSocketServer()), + network_thread_(new Thread(socket_server_.get())) { + RTC_CHECK(network_thread_->Start()); + RTC_CHECK(worker_thread_->Start()); + } protected: void SetUp() override { pc_factory_ = CreatePeerConnectionFactory( - Thread::Current(), Thread::Current(), Thread::Current(), + network_thread_.get(), worker_thread_.get(), Thread::Current(), FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(), std::make_unique( - CreateEnvironment(), socket_server_.get()); + auto port_allocator = network_thread_->BlockingCall([&] { + return std::make_unique(CreateEnvironment(), + socket_server_.get()); + }); port_allocator_ = port_allocator.get(); PeerConnectionDependencies pc_dependencies(&observer_); pc_dependencies.allocator = std::move(port_allocator); @@ -1488,8 +1493,10 @@ class PeerConnectionIceConfigTest : public ::testing::Test { pc_ = result.MoveValue(); } + test::RunLoop main_thread_; + std::unique_ptr worker_thread_; std::unique_ptr socket_server_; - AutoSocketServerThread main_thread_; + std::unique_ptr network_thread_; scoped_refptr pc_factory_ = nullptr; scoped_refptr pc_ = nullptr; FakePortAllocator* port_allocator_ = nullptr; @@ -1504,11 +1511,15 @@ TEST_F(PeerConnectionIceConfigTest, SetStunCandidateKeepaliveInterval) { config.ice_candidate_pool_size = 1; CreatePeerConnection(config); ASSERT_NE(port_allocator_, nullptr); - EXPECT_EQ(port_allocator_->stun_candidate_keepalive_interval(), + EXPECT_EQ(network_thread_->BlockingCall([this] { + return port_allocator_->stun_candidate_keepalive_interval(); + }), TimeDelta::Millis(123)); config.stun_candidate_keepalive_interval = 321; ASSERT_TRUE(pc_->SetConfiguration(config).ok()); - EXPECT_EQ(port_allocator_->stun_candidate_keepalive_interval(), + EXPECT_EQ(network_thread_->BlockingCall([this] { + return port_allocator_->stun_candidate_keepalive_interval(); + }), TimeDelta::Millis(321)); } @@ -1552,9 +1563,11 @@ TEST_P(PeerConnectionIceTest, IceCredentialsCreateOffer) { auto pc = CreatePeerConnectionWithAudioVideo(config); ASSERT_NE(pc->GetInternalPeerConnection()->port_allocator(), nullptr); std::unique_ptr offer = pc->CreateOffer(); - auto credentials = pc->GetInternalPeerConnection() - ->port_allocator() - ->GetPooledIceCredentials(); + auto credentials = pc->network_thread()->BlockingCall([&] { + return pc->GetInternalPeerConnection() + ->port_allocator() + ->GetPooledIceCredentials(); + }); ASSERT_EQ(1u, credentials.size()); auto* desc = offer->description(); @@ -1575,9 +1588,11 @@ TEST_P(PeerConnectionIceTest, IceCredentialsCreateAnswer) { ASSERT_TRUE(pc->SetRemoteDescription(std::move(offer))); std::unique_ptr answer = pc->CreateAnswer(); - auto credentials = pc->GetInternalPeerConnection() - ->port_allocator() - ->GetPooledIceCredentials(); + auto credentials = pc->network_thread()->BlockingCall([&] { + return pc->GetInternalPeerConnection() + ->port_allocator() + ->GetPooledIceCredentials(); + }); ASSERT_EQ(1u, credentials.size()); auto* desc = answer->description(); diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc index effbec73392..579e34d4a3c 100644 --- a/pc/peer_connection_integrationtest.cc +++ b/pc/peer_connection_integrationtest.cc @@ -86,6 +86,7 @@ #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/string_encode.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/task_queue_for_test.h" #include "rtc_base/test_certificate_verifier.h" #include "rtc_base/virtual_socket_server.h" @@ -1615,7 +1616,8 @@ TEST_P(PeerConnectionIntegrationTest, NewGetStatsManyAudioAndManyVideoStreams) { audio_sender_1->track()->id(), video_sender_1->track()->id(), audio_sender_2->track()->id(), video_sender_2->track()->id()}; - scoped_refptr caller_report = caller()->NewGetStats(); + scoped_refptr caller_report = + caller()->NewGetStats(run_loop()); ASSERT_TRUE(caller_report); auto outbound_stream_stats = caller_report->GetStatsOfType(); @@ -1639,7 +1641,8 @@ TEST_P(PeerConnectionIntegrationTest, NewGetStatsManyAudioAndManyVideoStreams) { } EXPECT_THAT(outbound_track_ids, UnorderedElementsAreArray(track_ids)); - scoped_refptr callee_report = callee()->NewGetStats(); + scoped_refptr callee_report = + callee()->NewGetStats(run_loop()); ASSERT_TRUE(callee_report); auto inbound_stream_stats = callee_report->GetStatsOfType(); @@ -1678,7 +1681,8 @@ TEST_P(PeerConnectionIntegrationTest, // We received a frame, so we should have nonzero "bytes received" stats for // the unsignaled stream, if stats are working for it. - scoped_refptr report = callee()->NewGetStats(); + scoped_refptr report = + callee()->NewGetStats(run_loop()); ASSERT_NE(nullptr, report); auto inbound_stream_stats = report->GetStatsOfType(); @@ -1728,7 +1732,8 @@ TEST_P(PeerConnectionIntegrationTest, media_expectations.CalleeExpectsSomeVideo(1); ASSERT_TRUE(ExpectNewFrames(media_expectations)); - scoped_refptr report = callee()->NewGetStats(); + scoped_refptr report = + callee()->NewGetStats(run_loop()); ASSERT_NE(nullptr, report); auto inbound_rtps = report->GetStatsOfType(); @@ -1770,11 +1775,11 @@ TEST_P(PeerConnectionIntegrationTest, Dtls10CipherStatsAndUmaMetrics) { EXPECT_THAT(WaitUntil( [&] { return SSLStreamAdapter::IsAcceptableCipher( - caller()->OldGetStats()->DtlsCipher(), KT_DEFAULT); + caller()->DtlsCipher(), KT_DEFAULT); }, IsTrue()), IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return caller()->OldGetStats()->SrtpCipher(); }, + EXPECT_THAT(WaitUntil([&] { return caller()->SrtpCipher(); }, Eq(SrtpCryptoSuiteToName(kDefaultSrtpCryptoSuite))), IsRtcOk()); } @@ -1793,11 +1798,11 @@ TEST_P(PeerConnectionIntegrationTest, Dtls12CipherStatsAndUmaMetrics) { EXPECT_THAT(WaitUntil( [&] { return SSLStreamAdapter::IsAcceptableCipher( - caller()->OldGetStats()->DtlsCipher(), KT_DEFAULT); + caller()->DtlsCipher(), KT_DEFAULT); }, IsTrue()), IsRtcOk()); - EXPECT_THAT(WaitUntil([&] { return caller()->OldGetStats()->SrtpCipher(); }, + EXPECT_THAT(WaitUntil([&] { return caller()->SrtpCipher(); }, Eq(SrtpCryptoSuiteToName(kDefaultSrtpCryptoSuite))), IsRtcOk()); } @@ -2447,6 +2452,7 @@ TEST_P(PeerConnectionIntegrationTest, // received end-to-end. TEST_F(PeerConnectionIntegrationTestPlanB, MediaFlowsAfterEarlyWarmupWithCreateSender) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); auto caller_audio_sender = @@ -2457,6 +2463,7 @@ TEST_F(PeerConnectionIntegrationTestPlanB, callee()->pc()->CreateSender("audio", "callee_stream"); auto callee_video_sender = callee()->pc()->CreateSender("video", "callee_stream"); + RTC_ALLOW_PLAN_B_DEPRECATION_END() caller()->CreateAndSetAndSignalOffer(); ASSERT_THAT(WaitUntil([&] { return SignalingStateStable(); }, IsTrue(), {.timeout = kMaxWaitForActivation}), @@ -2538,6 +2545,7 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, // from the caller to the callee, rather than being forwarded to a third // PeerConnection. TEST_F(PeerConnectionIntegrationTestPlanB, CanSendRemoteVideoTrack) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Just send a video track from the caller. @@ -2551,6 +2559,7 @@ TEST_F(PeerConnectionIntegrationTestPlanB, CanSendRemoteVideoTrack) { // Echo the stream back, and do a new offer/anwer (initiated by callee this // time). callee()->pc()->AddStream(callee()->remote_streams()->at(0)); + RTC_ALLOW_PLAN_B_DEPRECATION_END() callee()->CreateAndSetAndSignalOffer(); ASSERT_THAT(WaitUntil([&] { return SignalingStateStable(); }, IsTrue(), {.timeout = kMaxWaitForActivation}), @@ -2659,7 +2668,8 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock, caller()->AddAudioTrack(); // Call getStats, assert there are no candidates. - scoped_refptr first_report = caller()->NewGetStats(); + scoped_refptr first_report = + caller()->NewGetStats(run_loop()); ASSERT_TRUE(first_report); auto first_candidate_stats = first_report->GetStatsOfType(); @@ -2669,7 +2679,8 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock, // callee. caller()->CreateAndSetAndSignalOffer(); // Call getStats again, assert there are candidates now. - scoped_refptr second_report = caller()->NewGetStats(); + scoped_refptr second_report = + caller()->NewGetStats(run_loop()); ASSERT_TRUE(second_report); auto second_candidate_stats = second_report->GetStatsOfType(); @@ -2694,7 +2705,8 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock, IsRtcOk()); // Call getStats, assert there are no candidates. - scoped_refptr first_report = caller()->NewGetStats(); + scoped_refptr first_report = + caller()->NewGetStats(run_loop()); ASSERT_TRUE(first_report); auto first_candidate_stats = first_report->GetStatsOfType(); @@ -2713,7 +2725,8 @@ TEST_P(PeerConnectionIntegrationTestWithFakeClock, ASSERT_TRUE(result.value().ok()); // Call getStats again, assert there is a remote candidate now. - scoped_refptr second_report = caller()->NewGetStats(); + scoped_refptr second_report = + caller()->NewGetStats(run_loop()); ASSERT_TRUE(second_report); auto second_candidate_stats = second_report->GetStatsOfType(); diff --git a/pc/peer_connection_interface_unittest.cc b/pc/peer_connection_interface_unittest.cc index 302e62b3a3a..7135acb1064 100644 --- a/pc/peer_connection_interface_unittest.cc +++ b/pc/peer_connection_interface_unittest.cc @@ -86,11 +86,13 @@ #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/socket_address.h" #include "rtc_base/socket_server.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "rtc_base/weak_ptr.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" #ifdef WEBRTC_ANDROID @@ -826,7 +828,9 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { scoped_refptr stream( pc_factory_->CreateLocalMediaStream(label)); stream->AddTrack(CreateVideoTrack(label + "v0")); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); ASSERT_TRUE(pc_->AddStream(stream.get())); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } scoped_refptr CreateAudioTrack( @@ -845,7 +849,9 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { scoped_refptr stream( pc_factory_->CreateLocalMediaStream(label)); stream->AddTrack(CreateAudioTrack(label + "a0")); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); ASSERT_TRUE(pc_->AddStream(stream.get())); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } void AddAudioVideoStream(const std::string& stream_id, @@ -856,7 +862,9 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { pc_factory_->CreateLocalMediaStream(stream_id)); stream->AddTrack(CreateAudioTrack(audio_track_label)); stream->AddTrack(CreateVideoTrack(video_track_label)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); ASSERT_TRUE(pc_->AddStream(stream.get())); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } scoped_refptr GetFirstReceiverOfType( @@ -929,8 +937,12 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { // be required. bool DoGetStats(MediaStreamTrackInterface* track) { auto observer = make_ref_counted(); - if (!pc_->GetStats(observer.get(), track, - PeerConnectionInterface::kStatsOutputLevelStandard)) + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); + bool result = + pc_->GetStats(observer.get(), track, + PeerConnectionInterface::kStatsOutputLevelStandard); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); + if (!result) return false; EXPECT_THAT(WaitUntil([&] { return observer->called(); }, IsTrue(), {.timeout = TimeDelta::Millis(kTimeout)}), @@ -1088,6 +1100,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { // Waits until a remote stream with the given id is signaled. This helper // function will verify both OnAddTrack and OnAddStream (Plan B only) are // called with the given stream id and expected number of tracks. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() void WaitAndVerifyOnAddStream(const std::string& stream_id, int expected_num_tracks) { // Verify that both OnAddStream and OnAddTrack are called. @@ -1101,6 +1114,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { Eq(expected_num_tracks), {.timeout = TimeDelta::Millis(kTimeout)}), IsRtcOk()); } + RTC_ALLOW_PLAN_B_DEPRECATION_END() // Creates an offer and applies it as a local session description. // Creates an answer with the same SDP an the offer but removes all lines @@ -1277,7 +1291,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { SocketServer* socket_server() const { return vss_.get(); } std::unique_ptr vss_; - AutoSocketServerThread main_; + test::RunLoop main_; std::unique_ptr network_thread_; std::unique_ptr worker_thread_; scoped_refptr fake_audio_capture_module_; @@ -1297,12 +1311,17 @@ class PeerConnectionInterfaceTest PeerConnectionInterfaceTest() : PeerConnectionInterfaceBaseTest(GetParam()) {} }; -class PeerConnectionInterfaceTestPlanB +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() +// Plan B specific tests. +// Note - since the class is PLAN_B_ONLY, instances must be bracketed by +// RTC_ALLOW_PLAN_B_DEPRECATION macros. +class PLAN_B_ONLY PeerConnectionInterfaceTestPlanB : public PeerConnectionInterfaceBaseTest { protected: PeerConnectionInterfaceTestPlanB() : PeerConnectionInterfaceBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Generate different CNAMEs when PeerConnections are created. // The CNAMEs are expected to be generated randomly. It is possible @@ -1473,6 +1492,7 @@ TEST_P(PeerConnectionInterfaceTest, SetConfigurationFailsAfterClose) { pc_->SetConfiguration(PeerConnectionInterface::RTCConfiguration()).ok()); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() TEST_F(PeerConnectionInterfaceTestPlanB, AddStreams) { CreatePeerConnectionWithoutDtls(); AddVideoStream(kStreamId1); @@ -1648,6 +1668,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, AddTrackWithSendEncodings) { EXPECT_TRUE(pc_->RemoveTrackOrError(audio_sender).ok()); EXPECT_TRUE(pc_->RemoveTrackOrError(video_sender).ok()); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Test creating senders without a stream specified, // expecting a random stream ID to be generated. @@ -1687,7 +1708,7 @@ TEST_P(PeerConnectionInterfaceTest, AddTrackBeforeConnecting) { CreateVideoTrack("video_track")); auto audio_sender = pc_->AddTrack(audio_track, std::vector()); auto video_sender = pc_->AddTrack(video_track, std::vector()); - EXPECT_TRUE(DoGetStats(nullptr)); + EXPECT_TRUE(DoGetRTCStats()); } TEST_P(PeerConnectionInterfaceTest, AttachmentIdIsSetOnAddTrack) { @@ -1711,6 +1732,7 @@ TEST_P(PeerConnectionInterfaceTest, AttachmentIdIsSetOnAddTrack) { EXPECT_NE(0, video_sender_proxy->internal()->AttachmentId()); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // Don't run under Unified Plan since the stream API is not available. TEST_F(PeerConnectionInterfaceTestPlanB, AttachmentIdIsSetOnAddStream) { CreatePeerConnection(); @@ -1722,6 +1744,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, AttachmentIdIsSetOnAddStream) { senders[0].get()); EXPECT_NE(0, sender_proxy->internal()->AttachmentId()); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() TEST_P(PeerConnectionInterfaceTest, CreateOfferReceiveAnswer) { InitiateCall(); @@ -1760,6 +1783,7 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveOfferCreatePrAnswerAndAnswer) { WaitAndVerifyOnAddStream(kStreamId1, 1); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // Don't run under Unified Plan since the stream API is not available. TEST_F(PeerConnectionInterfaceTestPlanB, Renegotiate) { InitiateCall(); @@ -1784,6 +1808,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, RenegotiateAudioOnly) { CreateOfferReceiveAnswer(); EXPECT_EQ(0u, pc_->remote_streams()->count()); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Test that candidates are generated and that we can parse our own candidates. TEST_P(PeerConnectionInterfaceTest, IceCandidates) { @@ -1812,6 +1837,7 @@ TEST_P(PeerConnectionInterfaceTest, IceCandidates) { EXPECT_TRUE(pc_->AddIceCandidate(observer_.last_candidate())); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // Test that CreateOffer and CreateAnswer will fail if the track labels are // not unique. TEST_F(PeerConnectionInterfaceTestPlanB, CreateOfferAnswerWithInvalidStream) { @@ -1833,6 +1859,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CreateOfferAnswerWithInvalidStream) { std::unique_ptr answer; EXPECT_FALSE(DoCreateAnswer(&answer, nullptr)); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Test that we will get different SSRCs for each tracks in the offer and answer // we created. @@ -1866,6 +1893,7 @@ TEST_P(PeerConnectionInterfaceTest, SsrcInOfferAnswer) { EXPECT_NE(audio_ssrc, video_ssrc); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // Test that it's possible to call AddTrack on a MediaStream after adding // the stream to a PeerConnection. // TODO(deadbeef): Remove this test once this behavior is no longer supported. @@ -1926,6 +1954,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CreateSenderWithStream) { ASSERT_EQ(1u, video_desc->streams().size()); EXPECT_EQ(kStreamId1, video_desc->streams()[0].first_stream_id()); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Test that we can specify a certain track that we want statistics about. TEST_P(PeerConnectionInterfaceTest, GetStatsForSpecificTrack) { @@ -1934,7 +1963,7 @@ TEST_P(PeerConnectionInterfaceTest, GetStatsForSpecificTrack) { ASSERT_LT(0u, pc_->GetReceivers().size()); scoped_refptr remote_audio = pc_->GetReceivers()[0]->track(); - EXPECT_TRUE(DoGetStats(remote_audio.get())); + EXPECT_TRUE(DoGetRTCStats()); // Remove the stream. Since we are sending to our selves the local // and the remote stream is the same. @@ -1944,7 +1973,7 @@ TEST_P(PeerConnectionInterfaceTest, GetStatsForSpecificTrack) { // Test that we still can get statistics for the old track. Even if it is not // sent any longer. - EXPECT_TRUE(DoGetStats(remote_audio.get())); + EXPECT_TRUE(DoGetRTCStats()); } // Test that we can get stats on a video track. @@ -1952,7 +1981,7 @@ TEST_P(PeerConnectionInterfaceTest, GetStatsForVideoTrack) { InitiateCall(); auto video_receiver = GetFirstReceiverOfType(MediaType::VIDEO); ASSERT_TRUE(video_receiver); - EXPECT_TRUE(DoGetStats(video_receiver->track().get())); + EXPECT_TRUE(DoGetRTCStats()); } // Test that we don't get statistics for an invalid track. @@ -2588,8 +2617,10 @@ TEST_P(PeerConnectionInterfaceTest, CloseAndTestStreamsAndStates) { // With Plan B, verify the stream count. The analog with Unified Plan is the // RtpTransceiver count. if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); ASSERT_EQ(1u, pc_->local_streams()->count()); ASSERT_EQ(1u, pc_->remote_streams()->count()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } else { ASSERT_EQ(2u, pc_->GetTransceivers().size()); } @@ -2603,8 +2634,10 @@ TEST_P(PeerConnectionInterfaceTest, CloseAndTestStreamsAndStates) { pc_->ice_gathering_state()); if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); EXPECT_EQ(1u, pc_->local_streams()->count()); EXPECT_EQ(1u, pc_->remote_streams()->count()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } else { // Verify that the RtpTransceivers are still returned. EXPECT_EQ(2u, pc_->GetTransceivers().size()); @@ -2630,6 +2663,7 @@ TEST_P(PeerConnectionInterfaceTest, CloseAndTestStreamsAndStates) { } } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // Test that PeerConnection methods fails gracefully after // PeerConnection::Close has been called. // Don't run under Unified Plan since the stream API is not available. @@ -2668,12 +2702,13 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CloseAndTestMethods) { CreateSessionDescription(SdpType::kOffer, sdp)); EXPECT_FALSE(DoSetLocalDescription(std::move(local_offer))); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Test that GetStats can still be called after PeerConnection::Close. TEST_P(PeerConnectionInterfaceTest, CloseAndGetStats) { InitiateCall(); pc_->Close(); - DoGetStats(nullptr); + DoGetRTCStats(); } // NOTE: The series of tests below come from what used to be @@ -2690,9 +2725,11 @@ TEST_P(PeerConnectionInterfaceTest, UpdateRemoteStreams) { scoped_refptr reference( CreateStreamCollection(1, 1, worker_thread_.get())); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() EXPECT_TRUE( CompareStreamCollections(observer_.remote_streams(), reference.get())); MediaStreamInterface* remote_stream = observer_.remote_streams()->at(0); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); EXPECT_TRUE(remote_stream->GetVideoTracks()[0]->GetSource() != nullptr); // Create a session description based on another SDP with another @@ -2701,10 +2738,13 @@ TEST_P(PeerConnectionInterfaceTest, UpdateRemoteStreams) { scoped_refptr reference2( CreateStreamCollection(2, 1, worker_thread_.get())); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); EXPECT_TRUE( CompareStreamCollections(observer_.remote_streams(), reference2.get())); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // This test verifies that when remote tracks are added/removed from SDP, the // created remote streams are updated appropriately. // Don't run under Unified Plan since this test uses Plan B SDP to test Plan B @@ -2753,6 +2793,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, {.timeout = TimeDelta::Millis(kTimeout)}), IsRtcOk()); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // This tests that remote tracks are ended if a local session description is set // that rejects the media content type. @@ -2805,6 +2846,7 @@ TEST_P(PeerConnectionInterfaceTest, RejectMediaContent) { IsRtcOk()); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // This tests that we won't crash if the remote track has been removed outside // of PeerConnection and then PeerConnection tries to reject the track. // Don't run under Unified Plan since the stream API is not available. @@ -2829,6 +2871,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, RemoveTrackThenRejectMediaContent) { // No crash is a pass. } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // This tests that if a recvonly remote description is set, no remote streams // will be created, even if the description contains SSRCs/MSIDs. @@ -2841,9 +2884,12 @@ TEST_P(PeerConnectionInterfaceTest, RecvonlyDescriptionDoesntCreateStream) { absl::StrReplaceAll({{kSendrecv, kRecvonly}}, &recvonly_offer); CreateAndSetRemoteOffer(recvonly_offer); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); EXPECT_EQ(0u, observer_.remote_streams()->count()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // This tests that a default MediaStream is created if a remote session // description doesn't contain any streams and no MSID support. // It also tests that the default stream is updated if a video m-line is added @@ -3094,6 +3140,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, EXPECT_TRUE(ContainsSender(senders, kAudioTracks[1])); EXPECT_TRUE(ContainsSender(senders, kVideoTracks[1])); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // This tests that the expected behavior occurs if the SSRC on a local track is // changed when SetLocalDescription is called. @@ -3145,6 +3192,7 @@ TEST_P(PeerConnectionInterfaceTest, // changed. } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // This tests that the expected behavior occurs if a new session description is // set with the same tracks, but on a different MediaStream. // Don't run under Unified Plan since the stream API is not available. @@ -3183,6 +3231,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, EXPECT_TRUE(ContainsSender(new_senders, kAudioTracks[0], kStreams[1])); EXPECT_TRUE(ContainsSender(new_senders, kVideoTracks[0], kStreams[1])); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // This tests that PeerConnectionObserver::OnAddTrack is correctly called. TEST_P(PeerConnectionInterfaceTest, OnAddTrackCallback) { @@ -3499,6 +3548,7 @@ TEST_P(PeerConnectionInterfaceTest, CreateOfferWithOfferToReceiveConstraints) { EXPECT_FALSE(video->rejected); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // Test that if CreateAnswer is called with the deprecated "offer to receive // audio/video" constraints, they're processed and can be used to reject an // offered m= section just as can be done with RTCOfferAnswerOptions; @@ -3530,6 +3580,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, EXPECT_TRUE(audio->rejected); EXPECT_TRUE(video->rejected); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Test that negotiation can succeed with a data channel only, and with the max // bundle policy. Previously there was a bug that prevented this. @@ -3782,6 +3833,7 @@ TEST_P(PeerConnectionInterfaceTest, CreateOfferWithRtpMux) { EXPECT_FALSE(offer->description()->HasGroup(GROUP_TYPE_BUNDLE)); } +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() // This test ensures OnRenegotiationNeeded is called when we add track with // MediaStream -> AddTrack in the same way it is called when we add track with // PeerConnection -> AddTrack. @@ -3825,6 +3877,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, IsRtcOk()); observer_.renegotiation_needed_ = false; } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Tests that an error is returned if a description is applied that has fewer // media sections than the existing description. @@ -3926,6 +3979,7 @@ class PeerConnectionMediaConfigTest : public ::testing::Test { std::unique_ptr network_thread_; std::unique_ptr worker_thread_; + test::RunLoop signaling_thread_; scoped_refptr pcf_; MockPeerConnectionObserver observer_; }; diff --git a/pc/peer_connection_internal.h b/pc/peer_connection_internal.h index 4bf70dcf355..f31f9992d03 100644 --- a/pc/peer_connection_internal.h +++ b/pc/peer_connection_internal.h @@ -127,17 +127,10 @@ class PeerConnectionSdpMethods { scoped_refptr track, const RtpTransceiverInit& init, bool fire_callback = true) = 0; - // Asynchronously calls SctpTransport::Start() on the network thread for - // `sctp_mid()` if set. Called as part of setting the local description. + // Synchronously calls SctpTransport::Start() on the network thread for + // `sctp_mid()` if set. Called as part of pushing down the media descriptions + // after a complete offer/answer. virtual RTCError StartSctpTransport(const SctpOptions& options) = 0; - [[deprecated("Call with SctpOptions")]] - virtual void StartSctpTransport(int local_port, - int remote_port, - int max_message_size) { - StartSctpTransport({.local_port = local_port, - .remote_port = remote_port, - .max_message_size = max_message_size}); - } // Asynchronously adds a remote candidate on the network thread. virtual void AddRemoteCandidate(absl::string_view mid, diff --git a/pc/peer_connection_jsep_unittest.cc b/pc/peer_connection_jsep_unittest.cc index 72e54c0b826..104591b0a83 100644 --- a/pc/peer_connection_jsep_unittest.cc +++ b/pc/peer_connection_jsep_unittest.cc @@ -31,11 +31,13 @@ #include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" +#include "api/stats/rtcstats_objects.h" #include "api/test/rtc_error_matchers.h" #include "media/base/stream_params.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/transport_info.h" #include "pc/media_session.h" +#include "pc/peer_connection.h" #include "pc/peer_connection_wrapper.h" #include "pc/session_description.h" #include "pc/test/fake_audio_capture_module.h" @@ -61,6 +63,8 @@ namespace webrtc { using RTCConfiguration = PeerConnectionInterface::RTCConfiguration; using ::testing::Combine; using ::testing::ElementsAre; +using ::testing::NotNull; +using ::testing::SizeIs; using ::testing::UnorderedElementsAre; using ::testing::Values; @@ -2379,6 +2383,37 @@ TEST_F(PeerConnectionJsepTest, BundleOnlySectionDoesNotNeedRtcpMux) { EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer))); } +TEST_F(PeerConnectionJsepTest, RtcpMuxNotNegotiated) { + RTCConfiguration config; + // Non-standard as `require` is the only standardized value but we can not + // remove support for non-mux. + config.rtcp_mux_policy = + PeerConnection::RtcpMuxPolicy::kRtcpMuxPolicyNegotiate; + auto caller = CreatePeerConnection(config); + auto callee = CreatePeerConnection(config); + caller->AddTransceiver(MediaType::AUDIO); + std::unique_ptr offer = + caller->CreateOfferAndSetAsLocal(); + ASSERT_THAT(offer, NotNull()); + ASSERT_THAT(offer->description()->contents(), SizeIs(1)); + // Remove BUNDLE and rtcp-mux. + offer->description()->RemoveGroupByName(GROUP_TYPE_BUNDLE); + offer->description()->contents()[0].media_description()->set_rtcp_mux(false); + + EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer))); + std::unique_ptr answer = + callee->CreateAnswerAndSetAsLocal(); + EXPECT_THAT(answer, NotNull()); + EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer))); + + auto report = caller->GetStats(); + ASSERT_THAT(report, NotNull()); + + std::vector transports = + report->GetStatsOfType(); + EXPECT_THAT(transports, SizeIs(2)); +} + // This test is a regression test for crbug.com/410960672 TEST_F(PeerConnectionJsepTest, OfferRollbackRemoveReoffer) { auto caller = CreatePeerConnection(); diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc index 7f8b37a85ca..ea2a077d414 100644 --- a/pc/peer_connection_media_unittest.cc +++ b/pc/peer_connection_media_unittest.cc @@ -44,7 +44,6 @@ #include "p2p/base/p2p_constants.h" #include "p2p/base/transport_info.h" #include "p2p/test/fake_port_allocator.h" -#include "pc/channel_interface.h" #include "pc/media_session.h" #include "pc/peer_connection_wrapper.h" #include "pc/rtp_media_utils.h" @@ -56,6 +55,7 @@ #include "rtc_base/ref_counted_object.h" #include "rtc_base/thread.h" #include "test/gtest.h" +#include "test/run_loop.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -88,13 +88,13 @@ RtpTransceiver* RtpTransceiverInternal( MediaSendChannelInterface* SendChannelInternal( scoped_refptr transceiver) { auto transceiver_internal = RtpTransceiverInternal(transceiver); - return transceiver_internal->channel()->media_send_channel(); + return transceiver_internal->media_send_channel(); } MediaReceiveChannelInterface* ReceiveChannelInternal( scoped_refptr transceiver) { auto transceiver_internal = RtpTransceiverInternal(transceiver); - return transceiver_internal->channel()->media_receive_channel(); + return transceiver_internal->media_receive_channel(); } FakeVideoMediaSendChannel* VideoMediaSendChannel( @@ -246,7 +246,7 @@ class PeerConnectionMediaBaseTest : public ::testing::Test { } std::unique_ptr vss_; - AutoSocketServerThread main_; + test::RunLoop main_; const SdpSemantics sdp_semantics_; }; @@ -1377,10 +1377,10 @@ TEST_P(PeerConnectionMediaTest, RedFmtpPayloadTypeMustMatchBaseCodecs) { CreateAudioCodec(120, "foo", kDefaultAudioClockRateHz, 1)); callee_fake_codecs.push_back( CreateAudioCodec(121, kRedCodecName, kDefaultAudioClockRateHz, 1)); - callee_fake_codecs.push_back( - CreateAudioCodec(122, "bar", kDefaultAudioClockRateHz, 1)); callee_fake_codecs.back().SetParam(kCodecParamNotInNameValueFormat, "122/122"); + callee_fake_codecs.push_back( + CreateAudioCodec(122, "bar", kDefaultAudioClockRateHz, 1)); auto callee_fake_engine = std::make_unique(); callee_fake_engine->SetAudioCodecs(callee_fake_codecs); auto callee = CreatePeerConnectionWithAudio(std::move(callee_fake_engine)); @@ -1577,11 +1577,10 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, auto codecs = caller->pc_factory()->GetRtpSenderCapabilities(MediaType::AUDIO).codecs; auto codecs_only_rtx_red_fec = codecs; - std::erase_if( - codecs_only_rtx_red_fec, [](const RtpCodecCapability& codec) { - return !(codec.name == kRtxCodecName || codec.name == kRedCodecName || - codec.name == kUlpfecCodecName); - }); + std::erase_if(codecs_only_rtx_red_fec, [](const RtpCodecCapability& codec) { + return !(codec.name == kRtxCodecName || codec.name == kRedCodecName || + codec.name == kUlpfecCodecName); + }); ASSERT_THAT(codecs_only_rtx_red_fec.size(), Gt(0)); auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec); EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type()); @@ -1653,11 +1652,10 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, auto codecs = caller->pc_factory()->GetRtpSenderCapabilities(MediaType::VIDEO).codecs; auto codecs_only_rtx_red_fec = codecs; - std::erase_if( - codecs_only_rtx_red_fec, [](const RtpCodecCapability& codec) { - return !(codec.name == kRtxCodecName || codec.name == kRedCodecName || - codec.name == kUlpfecCodecName); - }); + std::erase_if(codecs_only_rtx_red_fec, [](const RtpCodecCapability& codec) { + return !(codec.name == kRtxCodecName || codec.name == kRedCodecName || + codec.name == kUlpfecCodecName); + }); auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec); EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type()); diff --git a/pc/peer_connection_proxy.h b/pc/peer_connection_proxy.h index fcd943bafb6..0f87b0447e1 100644 --- a/pc/peer_connection_proxy.h +++ b/pc/peer_connection_proxy.h @@ -40,6 +40,7 @@ #include "api/transport/bandwidth_estimation_settings.h" #include "api/transport/bitrate_settings.h" #include "pc/proxy.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" namespace webrtc { @@ -49,12 +50,17 @@ namespace webrtc { // and support for a secondary thread is provided via 'SECONDARY' macros. // TODO(deadbeef): Move this to .cc file. What threads methods are called on is // an implementation detail. + +// The use of multiple macros here means that clang-format does badly. +// clang-format off BEGIN_PROXY_MAP(PeerConnection) PROXY_PRIMARY_THREAD_DESTRUCTOR() -PROXY_METHOD0(scoped_refptr, local_streams) -PROXY_METHOD0(scoped_refptr, remote_streams) -PROXY_METHOD1(bool, AddStream, MediaStreamInterface*) -PROXY_METHOD1(void, RemoveStream, MediaStreamInterface*) +PLAN_B_ONLY PROXY_METHOD0(scoped_refptr, + local_streams) +PLAN_B_ONLY PROXY_METHOD0(scoped_refptr, + remote_streams) +PLAN_B_ONLY PROXY_METHOD1(bool, AddStream, MediaStreamInterface*) +PLAN_B_ONLY PROXY_METHOD1(void, RemoveStream, MediaStreamInterface*) PROXY_METHOD2(RTCErrorOr>, AddTrack, scoped_refptr, @@ -79,20 +85,23 @@ PROXY_METHOD2(RTCErrorOr>, AddTransceiver, webrtc::MediaType, const RtpTransceiverInit&) -PROXY_METHOD2(scoped_refptr, - CreateSender, - const std::string&, - const std::string&) +PLAN_B_ONLY PROXY_METHOD2(scoped_refptr, + CreateSender, + const std::string&, + const std::string&) PROXY_CONSTMETHOD0(std::vector>, GetSenders) PROXY_CONSTMETHOD0(std::vector>, GetReceivers) PROXY_CONSTMETHOD0(std::vector>, GetTransceivers) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" PROXY_METHOD3(bool, GetStats, StatsObserver*, MediaStreamTrackInterface*, StatsOutputLevel) +#pragma clang diagnostic pop PROXY_METHOD1(void, GetStats, RTCStatsCollectorCallback*) PROXY_METHOD2(void, GetStats, @@ -193,6 +202,7 @@ PROXY_METHOD0(void, Close) BYPASS_PROXY_CONSTMETHOD0(Thread*, signaling_thread) END_PROXY_MAP(PeerConnection) +// clang-format on } // namespace webrtc diff --git a/pc/peer_connection_rampup_tests.cc b/pc/peer_connection_rampup_tests.cc index e967536357a..b0c76c25765 100644 --- a/pc/peer_connection_rampup_tests.cc +++ b/pc/peer_connection_rampup_tests.cc @@ -40,7 +40,6 @@ #include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h" #include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h" #include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h" -#include "p2p/base/port_interface.h" #include "p2p/test/test_turn_server.h" #include "pc/peer_connection.h" #include "pc/peer_connection_wrapper.h" @@ -51,6 +50,7 @@ #include "rtc_base/crypto_random.h" #include "rtc_base/fake_network.h" #include "rtc_base/firewall_socket_server.h" +#include "rtc_base/net_helper.h" #include "rtc_base/socket_address.h" #include "rtc_base/socket_factory.h" #include "rtc_base/task_queue_for_test.h" diff --git a/pc/peer_connection_rtp_unittest.cc b/pc/peer_connection_rtp_unittest.cc index d235e504187..f2fcba417af 100644 --- a/pc/peer_connection_rtp_unittest.cc +++ b/pc/peer_connection_rtp_unittest.cc @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include #include @@ -33,6 +34,8 @@ #include "api/scoped_refptr.h" #include "api/set_remote_description_observer_interface.h" #include "api/test/rtc_error_matchers.h" +#include "api/units/data_rate.h" +#include "api/video/render_resolution.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_decoder_factory_template.h" #include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h" @@ -45,18 +48,23 @@ #include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h" #include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h" #include "media/base/codec.h" +#include "media/base/media_channel.h" #include "media/base/stream_params.h" +#include "media/engine/webrtc_video_engine.h" #include "pc/media_session.h" #include "pc/peer_connection_wrapper.h" +#include "pc/rtp_transceiver.h" #include "pc/session_description.h" #include "pc/test/fake_audio_capture_module.h" #include "pc/test/integration_test_helpers.h" #include "pc/test/mock_peer_connection_observers.h" #include "rtc_base/checks.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "system_wrappers/include/metrics.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" // This file contains tests for RTP Media API-related behavior of @@ -136,6 +144,7 @@ class PeerConnectionRtpBaseTest : public ::testing::Test { protected: const SdpSemantics sdp_semantics_; + test::RunLoop run_loop_; scoped_refptr pc_factory_; private: @@ -151,8 +160,6 @@ class PeerConnectionRtpBaseTest : public ::testing::Test { return std::make_unique( pc_factory_, result.MoveValue(), std::move(observer)); } - - AutoThread main_thread_; }; class PeerConnectionRtpTest @@ -162,11 +169,13 @@ class PeerConnectionRtpTest PeerConnectionRtpTest() : PeerConnectionRtpBaseTest(GetParam()) {} }; +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() class PeerConnectionRtpTestPlanB : public PeerConnectionRtpBaseTest { protected: PeerConnectionRtpTestPlanB() : PeerConnectionRtpBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; +RTC_ALLOW_PLAN_B_DEPRECATION_END() class PeerConnectionRtpTestUnifiedPlan : public PeerConnectionRtpBaseTest { protected: @@ -269,7 +278,9 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithStreamFiresOnRemoveTrack) { ASSERT_EQ(callee->observer()->add_track_events_.size(), 1u); EXPECT_EQ(callee->observer()->GetAddTrackReceivers(), callee->observer()->remove_track_events_); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); EXPECT_EQ(0u, callee->observer()->remote_streams()->count()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } TEST_P(PeerConnectionRtpTest, RemoveTrackWithSharedStreamFiresOnRemoveTrack) { @@ -292,7 +303,9 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithSharedStreamFiresOnRemoveTrack) { std::vector>{ callee->observer()->add_track_events_[0].receiver}, callee->observer()->remove_track_events_); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); ASSERT_EQ(1u, callee->observer()->remote_streams()->count()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); ASSERT_TRUE( caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); @@ -302,11 +315,14 @@ TEST_P(PeerConnectionRtpTest, RemoveTrackWithSharedStreamFiresOnRemoveTrack) { ASSERT_EQ(callee->observer()->add_track_events_.size(), 2u); EXPECT_EQ(callee->observer()->GetAddTrackReceivers(), callee->observer()->remove_track_events_); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); EXPECT_EQ(0u, callee->observer()->remote_streams()->count()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Tests the edge case that if a stream ID changes for a given track that both // OnRemoveTrack and OnAddTrack is fired. +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() TEST_F(PeerConnectionRtpTestPlanB, RemoteStreamIdChangesFiresOnRemoveAndOnAddTrack) { auto caller = CreatePeerConnection(); @@ -333,6 +349,7 @@ TEST_F(PeerConnectionRtpTestPlanB, EXPECT_EQ(callee->observer()->remove_track_events_[0]->streams()[0]->id(), kStreamId1); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Tests that setting a remote description with sending transceivers will fire // the OnTrack callback for each transceiver and setting a remote description @@ -705,6 +722,7 @@ TEST_P(PeerConnectionRtpTest, VideoGetParametersHasHeaderExtensions) { // calls and examine the state of the peer connection inside the callbacks to // ensure that the second call does not occur prematurely, contaminating the // state of the peer connection of the first callback. +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() TEST_F(PeerConnectionRtpTestPlanB, StatesCorrelateWithSetRemoteDescriptionCall) { auto caller = CreatePeerConnection(); @@ -759,6 +777,7 @@ TEST_F(PeerConnectionRtpTestPlanB, WaitUntil([&] { return srd2_callback_called; }, ::testing::IsTrue()), IsRtcOk()); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Tests that a remote track is created with the signaled MSIDs when they are // communicated with a=msid and no SSRCs are signaled at all (i.e., no a=ssrc @@ -883,6 +902,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, // remote audio senders, FindSenderInfo didn't find them as unique, because // it looked up by StreamParam.id, which none had. This meant only one // AudioRtpReceiver was created, as opposed to one for each remote sender. +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() TEST_F(PeerConnectionRtpTestPlanB, MultipleRemoteSendersWithoutStreamParamIdAddsMultipleReceivers) { auto caller = CreatePeerConnection(); @@ -910,6 +930,7 @@ TEST_F(PeerConnectionRtpTestPlanB, ASSERT_EQ(receivers[1]->streams().size(), 1u); EXPECT_EQ(kStreamId2, receivers[1]->streams()[0]->id()); } +RTC_ALLOW_PLAN_B_DEPRECATION_END() // Tests for the legacy SetRemoteDescription() function signature. @@ -938,7 +959,7 @@ TEST_P(PeerConnectionRtpTest, caller->CreateOfferAndSetAsLocal(); callee->pc()->SetRemoteDescription(observer.get(), offer.release()); callee = nullptr; - Thread::Current()->ProcessMessages(0); + run_loop_.Flush(); EXPECT_FALSE(observer->called()); } @@ -1982,9 +2003,207 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, EXPECT_EQ("stream5", callee_streams[2]->id()); } +TEST_F(PeerConnectionRtpTestUnifiedPlan, + SendParamsCorrectWhenSendCodecChanges) { + auto caller = CreatePeerConnection(); + auto callee = CreatePeerConnection(); + + auto sender = caller->AddVideoTrack("video_track"); + ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); + + // Call GetParameters() to populate the cache. + auto parameters1 = sender->GetParameters(); + ASSERT_GT(parameters1.codecs.size(), 0u); + + // Force renegotiation to change the negotiated codec. + auto transceiver = callee->pc()->GetTransceivers()[0]; + auto capabilities = + caller->pc_factory()->GetRtpReceiverCapabilities(MediaType::VIDEO); + std::vector codecs = capabilities.codecs; + // Remove the currently negotiated codec from the receiver preferences + codecs.erase(std::remove_if(codecs.begin(), codecs.end(), + [&](const RtpCodecCapability& c) { + return c.name == parameters1.codecs[0].name; + }), + codecs.end()); + + auto error = transceiver->SetCodecPreferences(codecs); + EXPECT_TRUE(error.ok()); + + // Exchange offer/answer again. This forces WebRtcVideoSendChannel to + // change the send codec without notifying RtpSender! + ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); + + // Call GetParameters(). The DCHECK evaluates cached_parameters_ against + // what's actually on the worker thread. Since we bypassed SetParameters, + // this should crash if the bug is present, or return the right values. + auto parameters2 = sender->GetParameters(); + ASSERT_GT(parameters2.codecs.size(), 0u); + EXPECT_NE(parameters1.codecs[0].name, parameters2.codecs[0].name); +} + +TEST_F(PeerConnectionRtpTestUnifiedPlan, + SendParamsCorrectWhenContentHintChanges) { + auto caller = CreatePeerConnection(); + auto callee = CreatePeerConnection(); + + auto track = caller->CreateVideoTrack("video"); + + RtpTransceiverInit init; + init.direction = RtpTransceiverDirection::kSendRecv; + RtpEncodingParameters encoding; + encoding.active = true; + // Set an unsupported scalability mode so that it gets mutated + // by FallbackToDefaultScalabilityModeIfNotSupported later. + encoding.scalability_mode = "L3T3_KEY"; + init.send_encodings.push_back(encoding); + + auto transceiver = caller->AddTransceiver(track, init); + ASSERT_TRUE(transceiver != nullptr); + auto sender = transceiver->sender(); + + ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); + + // Get initial parameters to populate the signaling thread cache. + auto parameters = sender->GetParameters(); + + // Change content hint, which triggers VideoRtpSender::OnChanged() -> + // SetVideoSend() -> WebRtcVideoSendStream::SetOptions() -> + // ReconfigureEncoder() -> FallbackToDefaultScalabilityModeIfNotSupported() + // on the worker thread. + track->set_content_hint(VideoTrackInterface::ContentHint::kDetailed); + + // Calling GetParameters will fetch the new parameters from the worker thread + // and compare them to the signaling thread cache, triggering the DCHECK if + // the cache is stale. + auto parameters_after = sender->GetParameters(); + + // If the DCHECK is not fatal, we manually verify the mismatch happened. + EXPECT_EQ(parameters_after.encodings.size(), 1u); +} + +TEST_F(PeerConnectionRtpTestUnifiedPlan, + CacheMismatchWhenEncoderSelectorChanges) { + auto caller = CreatePeerConnection(); + auto callee = CreatePeerConnection(); + + auto track = caller->CreateVideoTrack("video"); + + auto transceiver = caller->AddTransceiver(track); + ASSERT_TRUE(transceiver != nullptr); + auto sender = transceiver->sender(); + + ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); + + // Get initial parameters to populate the signaling thread cache. + auto parameters = sender->GetParameters(); + + class MockEncoderSelector + : public VideoEncoderFactory::EncoderSelectorInterface { + public: + void OnCurrentEncoder(const SdpVideoFormat& format) override {} + std::optional OnAvailableBitrate( + const DataRate& rate) override { + return SdpVideoFormat("VP8"); + } + std::optional OnEncoderBroken() override { + return std::nullopt; + } + std::optional OnResolutionChange( + const RenderResolution& resolution) override { + return std::nullopt; + } + }; + + auto encoder_selector = std::make_unique(); + auto format_to_switch_to = + *encoder_selector->OnAvailableBitrate(DataRate::Zero()); + + // Change encoder selector + sender->SetEncoderSelector(std::move(encoder_selector)); + + auto transceivers = + caller->GetInternalPeerConnection()->GetTransceiversInternal(); + ASSERT_FALSE(transceivers.empty()); + auto* transceiver_internal = transceivers[0]->internal(); + ASSERT_TRUE(transceiver_internal != nullptr); + + auto* media_channel = transceiver_internal->media_send_channel(); + ASSERT_TRUE(media_channel != nullptr); + + auto* worker_thread = caller->GetInternalPeerConnection()->worker_thread(); + ASSERT_TRUE(worker_thread != nullptr); + + worker_thread->BlockingCall([&] { + // Simulate VideoStreamEncoder calling RequestEncoderSwitch after getting + // the format from the EncoderSelector. + auto* video_send_channel = static_cast( + media_channel->AsVideoSendChannel()); + video_send_channel->RequestEncoderSwitch(std::move(format_to_switch_to), + /*allow_default_fallback=*/false); + }); + + // Allow the signaling thread to process the cache invalidation task posted + // by RequestEncoderSwitch. + run_loop_.Flush(); + + // Calling GetParameters will fetch the new parameters from the worker thread + // and compare them to the signaling thread cache, triggering the DCHECK if + // the cache is stale. + auto parameters_after = sender->GetParameters(); + + EXPECT_EQ(parameters_after.encodings.size(), 1u); +} + +TEST_F(PeerConnectionRtpTestUnifiedPlan, SendParamsCorrectWhenEncoderFallback) { + auto caller = CreatePeerConnection(); + auto callee = CreatePeerConnection(); + + auto track = caller->CreateVideoTrack("video"); + + auto transceiver = caller->AddTransceiver(track); + ASSERT_TRUE(transceiver != nullptr); + auto sender = transceiver->sender(); + + ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); + + // Get initial parameters to populate the signaling thread cache. + auto parameters = sender->GetParameters(); + + auto transceivers = + caller->GetInternalPeerConnection()->GetTransceiversInternal(); + ASSERT_FALSE(transceivers.empty()); + auto* transceiver_internal = transceivers[0]->internal(); + ASSERT_TRUE(transceiver_internal != nullptr); + + auto* media_channel = transceiver_internal->media_send_channel(); + ASSERT_TRUE(media_channel != nullptr); + + auto* worker_thread = caller->GetInternalPeerConnection()->worker_thread(); + ASSERT_TRUE(worker_thread != nullptr); + + worker_thread->BlockingCall([&] { + // Simulate an encoder fallback on the worker thread. + auto* video_send_channel = static_cast( + media_channel->AsVideoSendChannel()); + video_send_channel->RequestEncoderSwitch(std::nullopt, true); + }); + + // Allow the signaling thread to process the cache invalidation task posted + // by RequestEncoderSwitch(std::nullopt, true). + run_loop_.Flush(); + + // Call GetParameters. This triggers an internal consistency check in the + // `GetParameters()` implementation. + auto parameters_after = sender->GetParameters(); + EXPECT_EQ(parameters_after.codecs.size(), parameters.codecs.size()); +} + +RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() INSTANTIATE_TEST_SUITE_P(PeerConnectionRtpTest, PeerConnectionRtpTest, Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); +RTC_ALLOW_PLAN_B_DEPRECATION_END() } // namespace webrtc diff --git a/pc/peer_connection_signaling_unittest.cc b/pc/peer_connection_signaling_unittest.cc index 368ef4a440c..6bc9c294f53 100644 --- a/pc/peer_connection_signaling_unittest.cc +++ b/pc/peer_connection_signaling_unittest.cc @@ -65,6 +65,7 @@ #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" #ifdef WEBRTC_ANDROID @@ -198,7 +199,7 @@ class PeerConnectionSignalingBaseTest : public ::testing::Test { } std::unique_ptr vss_; - AutoSocketServerThread main_; + test::RunLoop main_; scoped_refptr pc_factory_; const SdpSemantics sdp_semantics_; }; diff --git a/pc/peer_connection_simulcast_unittest.cc b/pc/peer_connection_simulcast_unittest.cc index 2cd0df043bf..e8cd73f95d4 100644 --- a/pc/peer_connection_simulcast_unittest.cc +++ b/pc/peer_connection_simulcast_unittest.cc @@ -556,4 +556,59 @@ TEST_F(PeerConnectionSimulcastTests, SimulcastSldModificationRejected) { EXPECT_TRUE(modified_offer); EXPECT_TRUE(local->SetLocalDescription(std::move(modified_offer))); } + +// Reproduces the bug reported by @ibc where RTP extension IDs are reassigned +// to different URIs in subsequent offers, causing SetLocalDescription to fail. +TEST_F(PeerConnectionSimulcastTests, + NoRtpExtensionIdReassignmentWhenAddingTransceiver) { + auto local = CreatePeerConnectionWrapper(); + auto layers = CreateLayers({"f", "h", "q"}, true); + + // Add video transceiver with simulcast. + AddTransceiver(local.get(), layers); + ASSERT_TRUE(local->CreateOfferAndSetAsLocal()); + + // Set remote answer without header extensions. + std::string remote_answer_sdp = + "v=0\r\n" + "o=- 8403615332048243445 2 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "a=group:BUNDLE 0\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 96\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=mid:0\r\n" + "a=ice-ufrag:IZeV\r\n" + "a=ice-pwd:uaZhQD4rYM/Tta2qWBT1Bbt4\r\n" + "a=fingerprint:sha-256 " + "D8:6C:3D:FA:23:E2:2C:63:11:2D:D0:86:BE:C4:D0:65:F9:42:F7:1C:06:04:27:E6:" + "1C:2C:74:01:8D:50:67:23\r\n" + "a=setup:active\r\n" + "a=rtcp-mux\r\n" + "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n" + "a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" + "a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\n" + "a=extmap:2 " + "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n" + "a=extmap:4 " + "http://www.ietf.org/id/" + "draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n" + "a=extmap:3 urn:3gpp:video-orientation\r\n" + "a=extmap:1 urn:ietf:params:rtp-hdrext:toffset\r\n" + "a=extmap:5 " + "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n" + "a=rtpmap:96 VP8/90000\r\n"; + // Answer recvonly. + ASSERT_TRUE(local->SetRemoteDescription(CreateSessionDescription( + SdpType::kAnswer, remote_answer_sdp + "a=recvonly\r\n"))); + + ASSERT_TRUE(local->CreateOfferAndSetAsLocal()); + // Answer inactive. + ASSERT_TRUE(local->SetRemoteDescription(CreateSessionDescription( + SdpType::kAnswer, remote_answer_sdp + "a=inactive\r\n"))); + + // Add an audio transceiver. + local->AddAudioTrack("audio"); + EXPECT_TRUE(local->CreateOfferAndSetAsLocal()); +} } // namespace webrtc diff --git a/pc/peer_connection_stability_integrationtest.cc b/pc/peer_connection_stability_integrationtest.cc index 8d3052215b2..eb8948b5df4 100644 --- a/pc/peer_connection_stability_integrationtest.cc +++ b/pc/peer_connection_stability_integrationtest.cc @@ -48,6 +48,7 @@ namespace { using ::testing::ElementsAreArray; using ::testing::Eq; using ::testing::Not; +using ::testing::UnorderedElementsAreArray; class FactorySignature { public: @@ -61,6 +62,28 @@ class FactorySignature { kWebRtcAndroid, kGoogleInternal, }; + + template + friend void AbslStringify(Sink& sink, Id id) { + switch (id) { + case Id::kNotRecognized: + sink.Append("kNotRecognized"); + break; + case Id::kWebRtcTipOfTree: + sink.Append("kWebRtcTipOfTree"); + break; + case Id::kWebRtcMoreConfigs1: + sink.Append("kWebRtcMoreConfigs1"); + break; + case Id::kWebRtcAndroid: + sink.Append("kWebRtcAndroid"); + break; + case Id::kGoogleInternal: + sink.Append("kGoogleInternal"); + break; + } + } + Id id() { return id_; } FactorySignature() { ExtractSignatureStrings(); @@ -377,10 +400,7 @@ class PeerConnectionIntegrationTest : public PeerConnectionIntegrationBaseTest { std::vector callee_local, std::vector callee_remote) { StringBuilder sb; - // TODO: issues.webrtc.org/397895867 - change kChangeThis to the name of - // the value. Requires adding an AbslStringifier to the enum. - sb << "\n{" << ".factory_id = FactorySignature::Id::kChangeThis" - << static_cast(id) << ",\n" + sb << "\n{" << ".factory_id = FactorySignature::Id::" << id << ",\n" << ".caller_local = {"; for (const std::string& str : caller_local) { sb << "\"" << str << "\",\n"; @@ -940,13 +960,54 @@ TEST_F(PeerConnectionIntegrationTest, BasicOfferAnswerPayloadTypesStable) { const ResultingCodecList& this_golden = *this_golden_it; EXPECT_THAT(CodecList(*caller()->pc()->local_description()), - ElementsAreArray(this_golden.caller_local)); + UnorderedElementsAreArray(this_golden.caller_local)) + << "Factory ID: " << factory_signature.id(); EXPECT_THAT(CodecList(*caller()->pc()->remote_description()), - ElementsAreArray(this_golden.caller_remote)); + UnorderedElementsAreArray(this_golden.caller_remote)) + << "Factory ID: " << factory_signature.id(); EXPECT_THAT(CodecList(*callee()->pc()->local_description()), - ElementsAreArray(this_golden.callee_local)); + UnorderedElementsAreArray(this_golden.callee_local)) + << "Factory ID: " << factory_signature.id(); EXPECT_THAT(CodecList(*callee()->pc()->remote_description()), - ElementsAreArray(this_golden.callee_remote)); + UnorderedElementsAreArray(this_golden.callee_remote)) + << "Factory ID: " << factory_signature.id(); + + if (HasFailure()) { + return; + } + + EXPECT_THAT(CodecList(*caller()->pc()->local_description()), + ElementsAreArray(this_golden.caller_local)) + << "Factory ID: " << factory_signature.id(); + EXPECT_THAT(CodecList(*caller()->pc()->remote_description()), + ElementsAreArray(this_golden.caller_remote)) + << "Factory ID: " << factory_signature.id(); + EXPECT_THAT(CodecList(*callee()->pc()->local_description()), + ElementsAreArray(this_golden.callee_local)) + << "Factory ID: " << factory_signature.id(); + EXPECT_THAT(CodecList(*callee()->pc()->remote_description()), + ElementsAreArray(this_golden.callee_remote)) + << "Factory ID: " << factory_signature.id(); +} + +TEST_F(PeerConnectionIntegrationTest, + DumpAsResultingCodecListProducesExpectedOutput) { + std::vector caller_local = {"cl1"}; + std::vector caller_remote = {"cr1"}; + std::vector callee_local = {"ce_l1"}; + std::vector callee_remote = {"ce_r1"}; + + std::string output = DumpAsResultingCodecList( + FactorySignature::Id::kWebRtcTipOfTree, caller_local, caller_remote, + callee_local, callee_remote); + + EXPECT_THAT(output, + Eq("\n{.factory_id = FactorySignature::Id::kWebRtcTipOfTree,\n" + ".caller_local = {\"cl1\",\n},\n" + " .caller_remote = {\"cr1\",\n},\n" + " .callee_local = {\"ce_l1\",\n},\n" + " .callee_remote = {\"ce_r1\",\n" + "}}\n")); } } // namespace diff --git a/pc/peer_connection_wrapper.cc b/pc/peer_connection_wrapper.cc index 3c212088445..cc87cc64812 100644 --- a/pc/peer_connection_wrapper.cc +++ b/pc/peer_connection_wrapper.cc @@ -30,14 +30,17 @@ #include "api/scoped_refptr.h" #include "api/stats/rtc_stats_report.h" #include "api/test/rtc_error_matchers.h" +#include "api/units/time_delta.h" #include "pc/peer_connection.h" #include "pc/peer_connection_proxy.h" #include "pc/test/fake_video_track_source.h" #include "pc/test/mock_peer_connection_observers.h" #include "rtc_base/checks.h" +#include "rtc_base/event.h" #include "rtc_base/logging.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -153,11 +156,21 @@ PeerConnectionWrapper::CreateRollback() { std::unique_ptr PeerConnectionWrapper::CreateSdp( FunctionView fn, std::string* error_out) { - auto observer = make_ref_counted(); + // Tests in SdpMungingTest call this method from outside the signaling thread. + const bool signaling_is_current = + GetInternalPeerConnection()->signaling_thread()->IsCurrent(); + Event done; + auto observer = make_ref_counted( + [&]() { done.Set(); }); fn(observer.get()); - EXPECT_THAT( - WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()), - IsRtcOk()); + if (signaling_is_current) { + EXPECT_THAT( + WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()), + IsRtcOk()); + } else { + done.Wait(Event::kForever); + EXPECT_TRUE(observer->called()); + } if (error_out && !observer->result()) { *error_out = observer->error(); } @@ -384,4 +397,18 @@ scoped_refptr PeerConnectionWrapper::GetStats() { return callback->report(); } +bool PeerConnectionWrapper::RunUntilIceGatheringDone(test::RunLoop& run_loop, + TimeDelta timeout) { + if (observer()->ice_gathering_complete()) { + return true; + } + observer()->SetIceGatheringCompleteCallback(run_loop.QuitClosure()); + run_loop.RunFor(timeout); + + // Clear the callback just in case the timeout happened before it fired. + observer()->SetIceGatheringCompleteCallback(nullptr); + + return observer()->ice_gathering_complete(); +} + } // namespace webrtc diff --git a/pc/peer_connection_wrapper.h b/pc/peer_connection_wrapper.h index e9996cbec80..a18752a66eb 100644 --- a/pc/peer_connection_wrapper.h +++ b/pc/peer_connection_wrapper.h @@ -28,8 +28,10 @@ #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" #include "api/stats/rtc_stats_report.h" +#include "api/units/time_delta.h" #include "pc/peer_connection.h" #include "pc/test/mock_peer_connection_observers.h" +#include "test/run_loop.h" namespace webrtc { @@ -87,6 +89,11 @@ class PeerConnectionWrapper { std::string* error_out = nullptr); // Calls CreateAnswer with the default options. std::unique_ptr CreateAnswer(); + + // Returns true if ICE gathering is complete. Re-evaluates based on the + // observer callback. + bool RunUntilIceGatheringDone(test::RunLoop& run_loop, + TimeDelta timeout = TimeDelta::Seconds(10)); // Calls CreateAnswer and sets a copy of the offer as the local description. std::unique_ptr CreateAnswerAndSetAsLocal( const PeerConnectionInterface::RTCOfferAnswerOptions& options); diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index aa68be4628d..ce8c2b3b9e0 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -24,7 +24,6 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio/audio_device.h" #include "api/audio/audio_processing_statistics.h" #include "api/candidate.h" @@ -67,7 +66,6 @@ #include "pc/track_media_info_map.h" #include "pc/transport_stats.h" #include "rtc_base/checks.h" -#include "rtc_base/event.h" #include "rtc_base/ip_address.h" #include "rtc_base/logging.h" #include "rtc_base/network_constants.h" @@ -76,8 +74,8 @@ #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" -#include "rtc_base/thread_annotations.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" @@ -1105,6 +1103,14 @@ void SetAudioProcessingStats(StatsType* stats, } } +// Helper function for inserting into voice/video send/receive std::map types +// while also DCHECKing for uniqueness. +template +void AddChannelStats(StatsMap& stats_map, typename StatsMap::key_type channel) { + RTC_DCHECK(!stats_map.contains(channel)); + stats_map.insert(std::make_pair(channel, typename StatsMap::mapped_type())); +} + } // namespace scoped_refptr RTCStatsCollector::CreateReportFilteredBySelector( @@ -1117,7 +1123,10 @@ scoped_refptr RTCStatsCollector::CreateReportFilteredBySelector( // Filter mode: RTCStatsCollector::RequestInfo::kSenderSelector if (sender_selector) { // Find outbound-rtp(s) of the sender using ssrc lookup. - auto encodings = sender_selector->GetParametersInternal().encodings; + RtpParameters parameters = + sender_selector->GetParametersInternal(/*may_use_cache=*/true, + /*with_all_layers=*/false); + std::vector& encodings = parameters.encodings; for (const auto* outbound_rtp : report->GetStatsOfType()) { RTC_DCHECK(outbound_rtp->ssrc.has_value()); @@ -1194,6 +1203,20 @@ RTCStatsCollector::RequestInfo::RequestInfo( RTC_DCHECK(!sender_selector_ || !receiver_selector_); } +struct RTCStatsCollector::CollectionContext { + CollectionContext(scoped_refptr partial_report, + int64_t partial_report_timestamp_us) + : partial_report_timestamp_us(partial_report_timestamp_us), + partial_report(std::move(partial_report)) {} + + int64_t partial_report_timestamp_us = 0; + + // Reports that are produced on the signaling thread or the network thread are + // merged into this report. It is only touched on the signaling thread. Once + // all partial reports are merged this is the result of a request. + scoped_refptr partial_report; +}; + RTCStatsCollector::RTCStatsCollector(PeerConnectionInternal* pc, const Environment& env, int64_t cache_lifetime_us) @@ -1205,15 +1228,14 @@ RTCStatsCollector::RTCStatsCollector(PeerConnectionInternal* pc, signaling_thread_(pc->signaling_thread()), worker_thread_(pc->worker_thread()), network_thread_(pc->network_thread()), - num_pending_partial_reports_(0), - partial_report_timestamp_us_(0), - network_report_event_(true /* manual_reset */, - true /* initially_signaled */), cache_timestamp_us_(0), cache_lifetime_us_(cache_lifetime_us), signaling_safety_( PendingTaskSafetyFlag::CreateAttachedToTaskQueue(/*alive=*/true, signaling_thread_)), + worker_safety_( + PendingTaskSafetyFlag::CreateAttachedToTaskQueue(/*alive=*/true, + worker_thread_)), network_safety_( PendingTaskSafetyFlag::CreateAttachedToTaskQueue(/*alive=*/true, network_thread_)) { @@ -1243,10 +1265,50 @@ void RTCStatsCollector::GetStatsReport( GetStatsReportInternal(RequestInfo(std::move(selector), std::move(callback))); } +/* +[Signaling Thread] [Worker Thread] [Network Thread] + | | | +GetStatsReport | | + | | | +GetStatsReportInternal | | + | | | +PrepareTransceiverStatsInfos- | | + AndCallStats_s_w | | + | | | + |----- (Post Task) ------>| | + | | | + | Call::GetStats | + | MediaChannel::GetStats | + | | | + |<---- (Post Task) -------| | + | | | +ProducePartialResultsOnSig | | + | | | + |----------------------------- (Post Task) ------->| + | | | + | | ProducePartialResultsOnNetworkThread + | | GetTransportStatsByNames + | | GetDataChannelStats + | | PrepareTransportCertStats + | | | + |<---------------------------- (Post Task) --------| + | | | +ProcessResultsFromNetworkThread | | + (ProduceCertificateStats_s) | | + (ProduceTransportStats_s) | | + (ProduceRTPStreamStats_s) | | + | | | +OnNetworkReportReady | | + (Deliver Callback) | | + v v v +*/ void RTCStatsCollector::GetStatsReportInternal( RTCStatsCollector::RequestInfo request) { RTC_DCHECK_RUN_ON(signaling_thread_); - requests_.push_back(std::move(request)); + if (!signaling_safety_->alive()) { + RTC_LOG(LS_WARNING) << "GetStats called after shutdown."; + return; + } // "Now" using a monotonically increasing timer. int64_t cache_now_us = env_.clock().TimeInMicroseconds(); @@ -1255,65 +1317,86 @@ void RTCStatsCollector::GetStatsReportInternal( // We have a fresh cached report to deliver. Deliver asynchronously, since // the caller may not be expecting a synchronous callback, and it avoids // reentrancy problems. - signaling_thread_->PostTask(SafeTask( - signaling_safety_, [this, report = cached_report_, - requests = std::move(requests_)]() mutable { - DeliverCachedReport(std::move(report), std::move(requests)); + signaling_thread_->PostTask( + SafeTask(signaling_safety_, [this, request = std::move(request), + report = cached_report_]() mutable { + DeliverReport(request, report); })); - } else if (!num_pending_partial_reports_) { - // Only start gathering stats if we're not already gathering stats. In the - // case of already gathering stats, `callback_` will be invoked when there - // are no more pending partial reports. - - // Initialize common variables for the stats gather operation. - // As a future improvement, these could be owned by a dedicated stats - // gathering object that is used across the async steps. This would include - // moving variables such as partial_report_, network_report_, - // transceiver_stats_infos_, etc to that object rather than keep it as - // unguarded member variables. - Timestamp timestamp = - stats_timestamp_with_environment_clock_ - ? - // "Now" using a monotonically increasing timer. - env_.clock().CurrentTime() - : - // "Now" using a system clock, relative to the UNIX epoch (Jan 1, - // 1970, UTC), in microseconds. The system clock could be modified - // and is not necessarily monotonically increasing. - Timestamp::Micros(TimeUTCMicros()); - num_pending_partial_reports_ = 2; - partial_report_timestamp_us_ = cache_now_us; - network_report_event_.Reset(); - - // Prepare `transceiver_stats_infos_` and `call_stats_` for use in - // `ProducePartialResultsOnNetworkThread` and - // `ProducePartialResultsOnSignalingThread`. - PrepareTransceiverStatsInfosAndCallStats_s_w_n(); + return; + } + + requests_.push_back(std::move(request)); + if (collection_context_) { + // A stats gathering operation is already in progress. + return; + } + // Only start gathering stats if we're not already gathering stats. In the + // case of already gathering stats, `callback_` will be invoked when there + // are no more pending partial reports. + + // Initialize common variables for the stats gather operation. + Timestamp timestamp = + stats_timestamp_with_environment_clock_ + ? + // "Now" using a monotonically increasing timer. + env_.clock().CurrentTime() + : + // "Now" using a system clock, relative to the UNIX epoch (Jan 1, + // 1970, UTC), in microseconds. The system clock could be modified + // and is not necessarily monotonically increasing. + Timestamp::Micros(TimeUTCMicros()); + + collection_context_ = std::make_unique( + RTCStatsReport::Create(timestamp), cache_now_us); + + // Prepare `transceiver_stats_infos` and `call_stats` for use in + // `ProducePartialResultsOnNetworkThread` and + // `ProducePartialResultsOnSignalingThread`. + auto worker_task = PrepareTransceiverStatsInfosAndCallStats_s_w(); + + auto signaling_task = [this](WorkerThreadResult worker_result) mutable { + RTC_DCHECK_RUN_ON(signaling_thread_); // Create the initial `partial_report_` for the gathering operation. - ProducePartialResultsOnSignalingThread(timestamp); + ProducePartialResultsOnSignalingThread( + worker_result.results.transceiver_stats_infos, + worker_result.transceiver_references, + worker_result.results.audio_device_stats); + Timestamp timestamp = collection_context_->partial_report->timestamp(); std::set transport_names; auto sctp_transport_name = pc_->sctp_transport_name(); if (sctp_transport_name) { transport_names.emplace(std::move(*sctp_transport_name)); } - for (const auto& info : transceiver_stats_infos_) { + for (const auto& info : worker_result.results.transceiver_stats_infos) { if (info.transport_name) transport_names.insert(*info.transport_name); } - std::vector* cheating = &transceiver_stats_infos_; network_thread_->PostTask(SafeTask( network_safety_, [this, transport_names = std::move(transport_names), timestamp, - signaling_flag = signaling_safety_, cheating = cheating]() mutable { + signaling_flag = signaling_safety_, + results = std::move(worker_result.results)]() mutable { ProducePartialResultsOnNetworkThread( std::move(signaling_flag), timestamp, std::move(transport_names), - *cheating); + std::move(results)); })); - } + }; + + worker_thread_->PostTask(SafeTask( + worker_safety_, [this, worker_task = std::move(worker_task), + signaling_task = std::move(signaling_task)]() mutable { + auto worker_result = std::move(worker_task)(); + signaling_thread_->PostTask( + SafeTask(signaling_safety_, + [signaling_task = std::move(signaling_task), + worker_result = std::move(worker_result)]() mutable { + signaling_task(std::move(worker_result)); + })); + })); } void RTCStatsCollector::ClearCachedStatsReport() { @@ -1328,172 +1411,159 @@ void RTCStatsCollector::ClearCachedStatsReport() { } } -void RTCStatsCollector::WaitForPendingRequest() { - RTC_DCHECK_RUN_ON(signaling_thread_); - // If a request is pending, blocks until the `network_report_event_` is - // signaled and then delivers the result. Otherwise this is a NO-OP. - MergeNetworkReport_s(); -} - -absl::AnyInvocable -RTCStatsCollector::CancelPendingRequestAndGetShutdownTask() { +void RTCStatsCollector::CancelPendingRequestAndGetShutdownTasks( + std::vector>& network_tasks, + std::vector>& worker_tasks) { RTC_DCHECK_RUN_ON(signaling_thread_); signaling_safety_->SetNotAlive(); - return [flag = network_safety_]() { flag->SetNotAlive(); }; + worker_tasks.push_back([flag = worker_safety_]() { flag->SetNotAlive(); }); + network_tasks.push_back([flag = network_safety_]() { flag->SetNotAlive(); }); } void RTCStatsCollector::ProducePartialResultsOnSignalingThread( - Timestamp timestamp) { + const std::vector& transceiver_stats_infos, + const std::vector& transceiver_references, + const std::optional& audio_device_stats) { RTC_DCHECK_RUN_ON(signaling_thread_); + RTC_DCHECK(collection_context_); + RTC_DCHECK(collection_context_->partial_report.get()); Thread::ScopedDisallowBlockingCalls no_blocking_calls; - partial_report_ = RTCStatsReport::Create(timestamp); - - ProducePartialResultsOnSignalingThreadImpl(timestamp, partial_report_.get()); - - // ProducePartialResultsOnSignalingThread() runs synchronously on the - // signaling thread. So it is always the first partial result delivered on the - // signaling thread. The request is not complete until MergeNetworkReport_s() - // runs. We don't have to do anything here. - RTC_DCHECK_GT(num_pending_partial_reports_, 1); - --num_pending_partial_reports_; + ProducePartialResultsOnSignalingThreadImpl( + collection_context_->partial_report->timestamp(), transceiver_stats_infos, + transceiver_references, audio_device_stats, + collection_context_->partial_report.get()); } void RTCStatsCollector::ProducePartialResultsOnSignalingThreadImpl( Timestamp timestamp, + const std::vector& transceiver_stats_infos, + const std::vector& transceiver_references, + const std::optional& audio_device_stats, RTCStatsReport* partial_report) { RTC_DCHECK_RUN_ON(signaling_thread_); - ProduceMediaSourceStats_s(timestamp, partial_report); + ProduceMediaSourceStats_s(timestamp, transceiver_stats_infos, + transceiver_references, partial_report); ProducePeerConnectionStats_s(timestamp, partial_report); - ProduceAudioPlayoutStats_s(timestamp, partial_report); + ProduceAudioPlayoutStats_s(timestamp, audio_device_stats, partial_report); } void RTCStatsCollector::ProducePartialResultsOnNetworkThread( scoped_refptr signaling_safety, Timestamp timestamp, std::set transport_names, - std::vector& transceiver_stats_infos) { + StatsGatheringResults results) { + RTC_DCHECK_RUN_ON(network_thread_); TRACE_EVENT0("webrtc", "RTCStatsCollector::ProducePartialResultsOnNetworkThread"); - RTC_DCHECK_RUN_ON(network_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; - // Touching `network_report_` on this thread is safe by this method because - // `network_report_event_` is reset before this method is invoked. - network_report_ = RTCStatsReport::Create(timestamp); - - ProduceDataChannelStats_n(timestamp, network_report_.get()); + std::vector data_channel_stats = pc_->GetDataChannelStats(); std::map transport_stats_by_name = pc_->GetTransportStatsByNames(transport_names); std::map transport_cert_stats = PrepareTransportCertificateStats_n(transport_stats_by_name); - ProducePartialResultsOnNetworkThreadImpl( - timestamp, transport_stats_by_name, transport_cert_stats, - transceiver_stats_infos, network_report_.get()); - - // Signal that it is now safe to touch `network_report_` on the signaling - // thread, and post a task to merge it into the final results. - network_report_event_.Set(); - signaling_thread_->PostTask(SafeTask(std::move(signaling_safety), - [this] { MergeNetworkReport_s(); })); + signaling_thread_->PostTask(SafeTask( + std::move(signaling_safety), + [this, timestamp, + transport_stats_by_name = std::move(transport_stats_by_name), + transport_cert_stats = std::move(transport_cert_stats), + transceiver_stats_infos = std::move(results.transceiver_stats_infos), + call_stats = results.call_stats, + audio_device_stats = results.audio_device_stats, + data_channel_stats = std::move(data_channel_stats)]() mutable { + auto network_report = RTCStatsReport::Create(timestamp); + ProcessResultsFromNetworkThread( + timestamp, std::move(transport_stats_by_name), + std::move(transport_cert_stats), std::move(transceiver_stats_infos), + call_stats, audio_device_stats, network_report.get()); + OnNetworkReportReady(std::move(network_report), + std::move(data_channel_stats)); + })); } -void RTCStatsCollector::ProducePartialResultsOnNetworkThreadImpl( +void RTCStatsCollector::ProcessResultsFromNetworkThread( Timestamp timestamp, - const std::map& transport_stats_by_name, - const std::map& transport_cert_stats, - const std::vector& transceiver_stats_infos, + std::map transport_stats_by_name, + std::map transport_cert_stats, + std::vector transceiver_stats_infos, + Call::Stats call_stats, + std::optional audio_device_stats, RTCStatsReport* partial_report) { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; - ProduceCertificateStats_n(timestamp, transport_cert_stats, partial_report); - ProduceIceCandidateAndPairStats_n(timestamp, transport_stats_by_name, - call_stats_, partial_report); - ProduceTransportStats_n(timestamp, transport_stats_by_name, - transport_cert_stats, call_stats_, partial_report); - ProduceRTPStreamStats_n(timestamp, transceiver_stats_infos, partial_report); + ProduceCertificateStats_s(timestamp, transport_cert_stats, partial_report); + ProduceIceCandidateAndPairStats_s(timestamp, transport_stats_by_name, + call_stats, partial_report); + ProduceTransportStats_s(timestamp, transport_stats_by_name, + transport_cert_stats, call_stats, partial_report); + ProduceRTPStreamStats_s(timestamp, transceiver_stats_infos, call_stats, + audio_device_stats, partial_report); } -void RTCStatsCollector::MergeNetworkReport_s() { +void RTCStatsCollector::OnNetworkReportReady( + scoped_refptr network_report, + std::vector data_channel_stats) { RTC_DCHECK_RUN_ON(signaling_thread_); + RTC_DCHECK(collection_context_); + RTC_DCHECK(collection_context_->partial_report); + RTC_DCHECK(network_report); + + collection_context_->partial_report->TakeMembersFrom(network_report); + + ProduceDataChannelStats_s(collection_context_->partial_report->timestamp(), + data_channel_stats, + collection_context_->partial_report.get()); + + cache_timestamp_us_ = collection_context_->partial_report_timestamp_us; + cached_report_ = std::move(collection_context_->partial_report); + collection_context_ = nullptr; - // The `network_report_event_` must be signaled for it to be safe to touch - // `network_report_`. This is normally not blocking, but if - // WaitForPendingRequest() is called while a request is pending, we might have - // to wait until the network thread is done touching `network_report_`. - network_report_event_.Wait(Event::kForever); - if (!network_report_) { - // Normally, MergeNetworkReport_s() is executed because it is posted from - // the network thread. But if WaitForPendingRequest() is called while a - // request is pending, an early call to MergeNetworkReport_s() is made, - // merging the report and setting `network_report_` to null. If so, when the - // previously posted MergeNetworkReport_s() is later executed, the report is - // already null and nothing needs to be done here. - return; - } - RTC_DCHECK_GT(num_pending_partial_reports_, 0); - RTC_DCHECK(partial_report_); - partial_report_->TakeMembersFrom(network_report_); - network_report_ = nullptr; - --num_pending_partial_reports_; - // `network_report_` is currently the only partial report collected - // asynchronously, so `num_pending_partial_reports_` must now be 0 and we are - // ready to deliver the result. - RTC_DCHECK_EQ(num_pending_partial_reports_, 0); - cache_timestamp_us_ = partial_report_timestamp_us_; - cached_report_ = partial_report_; - partial_report_ = nullptr; - transceiver_stats_infos_.clear(); // Trace WebRTC Stats when getStats is called on Javascript. // This allows access to WebRTC stats from trace logs. To enable them, // select the "webrtc_stats" category when recording traces. TRACE_EVENT_INSTANT1("webrtc_stats", "webrtc_stats", TRACE_EVENT_SCOPE_GLOBAL, "report", cached_report_->ToJson()); - // Deliver report and clear `requests_`. - std::vector requests; - requests.swap(requests_); - DeliverCachedReport(cached_report_, std::move(requests)); + // Clear `requests_` before the loop in case a callback adds a request. + auto requests = std::move(requests_); + for (const RequestInfo& request : requests) { + DeliverReport(request, cached_report_); + } } -void RTCStatsCollector::DeliverCachedReport( - scoped_refptr cached_report, - std::vector requests) { +void RTCStatsCollector::DeliverReport( + const RequestInfo& request, + const scoped_refptr& report) { RTC_DCHECK_RUN_ON(signaling_thread_); - RTC_DCHECK(!requests.empty()); - RTC_DCHECK(cached_report); - - for (const RequestInfo& request : requests) { - if (request.filter_mode() == RequestInfo::FilterMode::kAll) { - request.callback()->OnStatsDelivered(cached_report); + if (request.filter_mode() == RequestInfo::FilterMode::kAll) { + request.callback()->OnStatsDelivered(report); + } else { + bool filter_by_sender_selector; + scoped_refptr sender_selector; + scoped_refptr receiver_selector; + if (request.filter_mode() == RequestInfo::FilterMode::kSenderSelector) { + filter_by_sender_selector = true; + sender_selector = request.sender_selector(); } else { - bool filter_by_sender_selector; - scoped_refptr sender_selector; - scoped_refptr receiver_selector; - if (request.filter_mode() == RequestInfo::FilterMode::kSenderSelector) { - filter_by_sender_selector = true; - sender_selector = request.sender_selector(); - } else { - RTC_DCHECK(request.filter_mode() == - RequestInfo::FilterMode::kReceiverSelector); - filter_by_sender_selector = false; - receiver_selector = request.receiver_selector(); - } - request.callback()->OnStatsDelivered(CreateReportFilteredBySelector( - filter_by_sender_selector, cached_report, sender_selector, - receiver_selector)); + RTC_DCHECK(request.filter_mode() == + RequestInfo::FilterMode::kReceiverSelector); + filter_by_sender_selector = false; + receiver_selector = request.receiver_selector(); } + request.callback()->OnStatsDelivered(CreateReportFilteredBySelector( + filter_by_sender_selector, report, sender_selector, receiver_selector)); } } -void RTCStatsCollector::ProduceCertificateStats_n( +void RTCStatsCollector::ProduceCertificateStats_s( Timestamp timestamp, const std::map& transport_cert_stats, RTCStatsReport* report) const { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; for (const auto& transport_cert_stats_pair : transport_cert_stats) { @@ -1508,12 +1578,13 @@ void RTCStatsCollector::ProduceCertificateStats_n( } } -void RTCStatsCollector::ProduceDataChannelStats_n( +void RTCStatsCollector::ProduceDataChannelStats_s( Timestamp timestamp, + const std::vector& data_stats, RTCStatsReport* report) const { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; - std::vector data_stats = pc_->GetDataChannelStats(); + for (const auto& stats : data_stats) { auto data_channel_stats = std::make_unique( "D" + absl::StrCat(stats.internal_id), timestamp); @@ -1533,12 +1604,12 @@ void RTCStatsCollector::ProduceDataChannelStats_n( } } -void RTCStatsCollector::ProduceIceCandidateAndPairStats_n( +void RTCStatsCollector::ProduceIceCandidateAndPairStats_s( Timestamp timestamp, const std::map& transport_stats_by_name, const Call::Stats& call_stats, RTCStatsReport* report) const { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; for (const auto& entry : transport_stats_by_name) { @@ -1641,12 +1712,18 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_n( void RTCStatsCollector::ProduceMediaSourceStats_s( Timestamp timestamp, + const std::vector& transceiver_stats_infos, + const std::vector& transceiver_references, RTCStatsReport* report) const { RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; - for (const RtpTransceiverStatsInfo& transceiver_stats_info : - transceiver_stats_infos_) { + RTC_DCHECK_EQ(transceiver_stats_infos.size(), transceiver_references.size()); + for (size_t i = 0; i < transceiver_stats_infos.size(); ++i) { + const RtpTransceiverStatsInfo& transceiver_stats_info = + transceiver_stats_infos[i]; + const TransceiverReferences& refs = transceiver_references[i]; + // The transceiver will still exist but in a stopped state after pc.close(). if (transceiver_stats_info.current_direction == RtpTransceiverDirection::kStopped) { @@ -1656,7 +1733,9 @@ void RTCStatsCollector::ProduceMediaSourceStats_s( const TrackMediaInfoMap& track_media_info_map = *transceiver_stats_info.track_media_info_map; - for (const auto& sender : transceiver_stats_info.transceiver->senders()) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() + for (const auto& sender : refs.transceiver->senders()) { + RTC_ALLOW_PLAN_B_DEPRECATION_END() const auto& sender_internal = sender->internal(); const auto& track = sender_internal->track(); if (!track) @@ -1669,7 +1748,7 @@ void RTCStatsCollector::ProduceMediaSourceStats_s( // levels are moved to the corresponding audio track/source object), don't // create separate media source stats objects on a per-attachment basis. std::unique_ptr media_source_stats; - if (track->kind() == MediaStreamTrackInterface::kAudioKind) { + if (sender_internal->media_type() == MediaType::AUDIO) { AudioTrackInterface* audio_track = static_cast(track.get()); auto audio_source_stats = std::make_unique( @@ -1759,20 +1838,23 @@ void RTCStatsCollector::ProducePeerConnectionStats_s( void RTCStatsCollector::ProduceAudioPlayoutStats_s( Timestamp timestamp, + const std::optional& audio_device_stats, RTCStatsReport* report) const { RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; - if (audio_device_stats_) { - report->AddStats(CreateAudioPlayoutStats(*audio_device_stats_, timestamp)); + if (audio_device_stats) { + report->AddStats(CreateAudioPlayoutStats(*audio_device_stats, timestamp)); } } -void RTCStatsCollector::ProduceRTPStreamStats_n( +void RTCStatsCollector::ProduceRTPStreamStats_s( Timestamp timestamp, const std::vector& transceiver_stats_infos, + const Call::Stats& call_stats, + const std::optional& audio_device_stats, RTCStatsReport* report) const { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; for (const RtpTransceiverStatsInfo& stats : transceiver_stats_infos) { @@ -1780,20 +1862,27 @@ void RTCStatsCollector::ProduceRTPStreamStats_n( continue; } + if (stats.current_direction == RtpTransceiverDirection::kStopped) { + continue; + } + if (stats.media_type == MediaType::AUDIO) { - ProduceAudioRTPStreamStats_n(timestamp, stats, report); + ProduceAudioRTPStreamStats_s(timestamp, stats, call_stats, + audio_device_stats, report); } else { RTC_DCHECK_EQ(stats.media_type, MediaType::VIDEO); - ProduceVideoRTPStreamStats_n(timestamp, stats, report); + ProduceVideoRTPStreamStats_s(timestamp, stats, call_stats, report); } } } -void RTCStatsCollector::ProduceAudioRTPStreamStats_n( +void RTCStatsCollector::ProduceAudioRTPStreamStats_s( Timestamp timestamp, const RtpTransceiverStatsInfo& stats, + const Call::Stats& call_stats, + const std::optional& audio_device_stats, RTCStatsReport* report) const { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(stats.mid); RTC_DCHECK(stats.transport_name); RTC_DCHECK(stats.track_media_info_map); @@ -1825,14 +1914,14 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n( CreateInboundAudioStreamStats( *stats.track_media_info_map->voice_media_info(), voice_receiver_info, transport_id, mid, timestamp, report); - AppendCallStats(call_stats_, *inbound_audio); + AppendCallStats(call_stats, *inbound_audio); // TODO(hta): This lookup should look for the sender, not the track. auto track_id = stats.track_media_info_map->GetReceiverTrackIdBySsrc( voice_receiver_info.ssrc(), MediaType::AUDIO); if (track_id.has_value()) { inbound_audio->track_identifier = *track_id; } - if (audio_device_stats_ && stats.media_type == MediaType::AUDIO && + if (audio_device_stats && stats.media_type == MediaType::AUDIO && stats.current_direction && (*stats.current_direction == RtpTransceiverDirection::kSendRecv || *stats.current_direction == RtpTransceiverDirection::kRecvOnly)) { @@ -1904,17 +1993,18 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n( for (const auto& report_block_data : voice_sender_info.report_block_datas) { report->AddStats(ProduceRemoteInboundRtpStreamStats( transport_id, report_block_data, MediaType::AUDIO, - audio_outbound_rtps, *report, call_stats_, + audio_outbound_rtps, *report, call_stats, stats_timestamp_with_environment_clock_)); } } } -void RTCStatsCollector::ProduceVideoRTPStreamStats_n( +void RTCStatsCollector::ProduceVideoRTPStreamStats_s( Timestamp timestamp, const RtpTransceiverStatsInfo& stats, + const Call::Stats& call_stats, RTCStatsReport* report) const { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(stats.mid); RTC_DCHECK(stats.transport_name); RTC_DCHECK(stats.track_media_info_map); @@ -1943,7 +2033,7 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n( CreateInboundRTPStreamStatsFromVideoReceiverInfo( transport_id, mid, *stats.track_media_info_map->video_media_info(), video_receiver_info, timestamp, report); - AppendCallStats(call_stats_, *inbound_video); + AppendCallStats(call_stats, *inbound_video); auto track_id = stats.track_media_info_map->GetReceiverTrackIdBySsrc( video_receiver_info.ssrc(), MediaType::VIDEO); if (track_id.has_value()) { @@ -2015,19 +2105,19 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n( for (const auto& report_block_data : video_sender_info.report_block_datas) { report->AddStats(ProduceRemoteInboundRtpStreamStats( transport_id, report_block_data, MediaType::VIDEO, - video_outbound_rtps, *report, call_stats_, + video_outbound_rtps, *report, call_stats, stats_timestamp_with_environment_clock_)); } } } -void RTCStatsCollector::ProduceTransportStats_n( +void RTCStatsCollector::ProduceTransportStats_s( Timestamp timestamp, const std::map& transport_stats_by_name, const std::map& transport_cert_stats, const Call::Stats& call_stats, RTCStatsReport* report) const { - RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_RUN_ON(signaling_thread_); Thread::ScopedDisallowBlockingCalls no_blocking_calls; for (const auto& entry : transport_stats_by_name) { @@ -2129,7 +2219,7 @@ void RTCStatsCollector::ProduceTransportStats_n( SrtpCryptoSuiteToName(channel_stats.srtp_crypto_suite); } channel_transport_stats->ccfb_messages_received = - call_stats_.ccfb_messages_received; + call_stats.ccfb_messages_received; report->AddStats(std::move(channel_transport_stats)); } } @@ -2177,32 +2267,30 @@ RTCStatsCollector::PrepareTransportCertificateStats_n( return transport_cert_stats; } -void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { +absl::AnyInvocable +RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w() { RTC_DCHECK_RUN_ON(signaling_thread_); + RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); - transceiver_stats_infos_.clear(); - // These are used to invoke GetStats for all the media channels together in - // one worker thread hop. - std::map - voice_send_stats; - std::map - video_send_stats; - std::map - voice_receive_stats; - std::map - video_receive_stats; + std::vector transceiver_stats_infos; + std::vector transceiver_references; auto transceivers = pc_->GetTransceiversInternal(); for (const auto& transceiver_proxy : transceivers) { RtpTransceiver* transceiver = transceiver_proxy->internal(); + RtpTransceiverStatsInfo stats{ - .transceiver = scoped_refptr(transceiver), .media_type = transceiver->media_type(), .mid = transceiver->mid(), - .transport_name = std::nullopt, - .current_direction = transceiver->current_direction()}; + .transport_name = transceiver->transport_name(), + .current_direction = transceiver->current_direction(), + .has_channel = transceiver->HasChannel()}; + + TransceiverReferences refs{.transceiver = + scoped_refptr(transceiver)}; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (const auto& sender : transceiver->senders()) { stats.sender_infos.push_back( {.ssrc = sender->ssrc(), @@ -2214,147 +2302,124 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { {.track_id = receiver->track() ? receiver->track()->id() : "", .attachment_id = receiver->internal()->AttachmentId(), .media_type = receiver->media_type()}); - stats.receivers.push_back( + refs.receivers.push_back( scoped_refptr(receiver->internal())); } - stats.has_receivers = !stats.receivers.empty(); - - transceiver_stats_infos_.push_back(std::move(stats)); - } - - // TODO(tommi): See if we can avoid synchronously blocking the signaling - // thread while we do this (or avoid the BlockingCall at all). Note also that - // where PrepareTransceiverStatsInfosAndCallStats_s_w_n is called from, - // there's a PostTask() to the network thread to call - // ProducePartialResultsOnNetworkThread(). See if this block should be merged - // with that. - // Currently using RTC_NO_THREAD_SAFETY_ANALYSIS here and below due to use of - // transceiver_stats_infos_. Remove this and pass transceiver_stats_infos_ in - // an object that's used to gather the data from start to finish. - network_thread_->BlockingCall( - [&, &transceiver_stats_infos = transceiver_stats_infos_]() - RTC_NO_THREAD_SAFETY_ANALYSIS mutable { - Thread::ScopedDisallowBlockingCalls no_blocking_calls; - - for (auto& stats : transceiver_stats_infos) { - ChannelInterface* channel = stats.transceiver->channel(); - if (!channel) { - continue; - } - - stats.transport_name = std::string(channel->transport_name()); - - if (stats.media_type == MediaType::AUDIO) { - auto voice_send_channel = channel->voice_media_send_channel(); - RTC_DCHECK(voice_send_stats.find(voice_send_channel) == - voice_send_stats.end()); - voice_send_stats.insert( - std::make_pair(voice_send_channel, VoiceMediaSendInfo())); - - auto voice_receive_channel = - channel->voice_media_receive_channel(); - RTC_DCHECK(voice_receive_stats.find(voice_receive_channel) == - voice_receive_stats.end()); - voice_receive_stats.insert(std::make_pair( - voice_receive_channel, VoiceMediaReceiveInfo())); - } else if (stats.media_type == MediaType::VIDEO) { - auto video_send_channel = channel->video_media_send_channel(); - RTC_DCHECK(video_send_stats.find(video_send_channel) == - video_send_stats.end()); - video_send_stats.insert( - std::make_pair(video_send_channel, VideoMediaSendInfo())); - auto video_receive_channel = - channel->video_media_receive_channel(); - RTC_DCHECK(video_receive_stats.find(video_receive_channel) == - video_receive_stats.end()); - video_receive_stats.insert(std::make_pair( - video_receive_channel, VideoMediaReceiveInfo())); - } else { - RTC_DCHECK_NOTREACHED(); - } - } - }); - - // We jump to the worker thread and call GetStats() on each media channel as - // well as GetCallStats(). At the same time we construct the - // TrackMediaInfoMaps, which also needs info from the worker thread. This - // minimizes the number of thread jumps. - // Currently using RTC_NO_THREAD_SAFETY_ANALYSIS here too due to use of - // transceiver_stats_infos_ in a blocking call. - worker_thread_->BlockingCall([&]() RTC_NO_THREAD_SAFETY_ANALYSIS mutable { - Thread::ScopedDisallowBlockingCalls no_blocking_calls; - - for (auto& pair : voice_send_stats) { - if (!pair.first->GetStats(&pair.second)) { - RTC_LOG(LS_WARNING) << "Failed to get voice send stats."; - } - } - for (auto& pair : voice_receive_stats) { - if (!pair.first->GetStats(&pair.second, - /*get_and_clear_legacy_stats=*/false)) { - RTC_LOG(LS_WARNING) << "Failed to get voice receive stats."; - } - } - for (auto& pair : video_send_stats) { - if (!pair.first->GetStats(&pair.second)) { - RTC_LOG(LS_WARNING) << "Failed to get video send stats."; - } - } - for (auto& pair : video_receive_stats) { - if (!pair.first->GetStats(&pair.second)) { - RTC_LOG(LS_WARNING) << "Failed to get video receive stats."; + RTC_ALLOW_PLAN_B_DEPRECATION_END() + stats.has_receivers = !refs.receivers.empty(); + + if (stats.has_channel) { + auto* send_channel = transceiver->media_send_channel(); + auto* receive_channel = transceiver->media_receive_channel(); + if (stats.media_type == MediaType::AUDIO) { + VoiceMediaSendChannelInterface* voice_send = + send_channel->AsVoiceSendChannel(); + RTC_CHECK(voice_send); + VoiceMediaReceiveChannelInterface* voice_receive = + receive_channel->AsVoiceReceiveChannel(); + RTC_CHECK(voice_receive); + refs.get_send_stats_voice = voice_send->GetStatsCallback(); + refs.get_receive_stats_voice = voice_receive->GetStatsCallback(false); + refs.get_send_parameters = voice_send->GetRtpSendParametersCallback(); + } else if (stats.media_type == MediaType::VIDEO) { + VideoMediaSendChannelInterface* video_send = + send_channel->AsVideoSendChannel(); + RTC_CHECK(video_send); + VideoMediaReceiveChannelInterface* video_receive = + receive_channel->AsVideoReceiveChannel(); + RTC_CHECK(video_receive); + refs.get_send_stats_video = video_send->GetStatsCallback(); + refs.get_receive_stats_video = video_receive->GetStatsCallback(); + refs.get_send_parameters = video_send->GetRtpSendParametersCallback(); } } + transceiver_stats_infos.push_back(std::move(stats)); + transceiver_references.push_back(std::move(refs)); + } + + // Embed the collected information into this lambda which will run on the + // worker thread. There, we'll call GetStats() on each media channel as well + // as GetCallStats(). At the same time we construct the TrackMediaInfoMaps, + // which also needs info from the worker thread. + return [this, transceiver_stats_infos = std::move(transceiver_stats_infos), + transceiver_references = + std::move(transceiver_references)]() mutable { + Thread::ScopedDisallowBlockingCalls no_blocking_calls; + + WorkerThreadResult worker_result; + worker_result.results.transceiver_stats_infos = + std::move(transceiver_stats_infos); + worker_result.transceiver_references = std::move(transceiver_references); + // Create the TrackMediaInfoMap for each transceiver stats object // and keep track of whether we have at least one audio receiver. bool has_audio_receiver = false; - for (auto& stats : transceiver_stats_infos_) { + RTC_DCHECK_EQ(worker_result.results.transceiver_stats_infos.size(), + worker_result.transceiver_references.size()); + for (size_t i = 0; i < worker_result.results.transceiver_stats_infos.size(); + ++i) { + auto& stats = worker_result.results.transceiver_stats_infos[i]; + auto& refs = worker_result.transceiver_references[i]; // The transceiver will still exist but in a stopped state after // pc.close(). if (stats.current_direction == RtpTransceiverDirection::kStopped) { continue; } + std::vector sender_parameters; + for (const auto& sender : stats.sender_infos) { + RtpParameters params; + if (sender.ssrc != 0 && stats.has_channel) { + params = refs.get_send_parameters(sender.ssrc); + } + // `sender_parameters` must be the same size as `stats.sender_infos` to + // allow 1:1 mapping when TrackMediaInfoMap is constructed. Empty params + // are safely ignored since they have no encodings/SSRCs. + sender_parameters.push_back(std::move(params)); + } + std::vector receiver_parameters; - for (const auto& receiver : stats.receivers) { + for (const auto& receiver : refs.receivers) { receiver_parameters.push_back(receiver->GetParameters()); } - auto transceiver = stats.transceiver; std::optional voice_media_info; std::optional video_media_info; - auto channel = transceiver->channel(); - if (channel) { - MediaType media_type = transceiver->media_type(); - if (media_type == MediaType::AUDIO) { - auto voice_send_channel = channel->voice_media_send_channel(); - auto voice_receive_channel = channel->voice_media_receive_channel(); + if (stats.has_channel) { + if (stats.media_type == MediaType::AUDIO) { + std::optional voice_send = + refs.get_send_stats_voice(); + std::optional voice_receive = + refs.get_receive_stats_voice(); voice_media_info = VoiceMediaInfo( - std::move(voice_send_stats[voice_send_channel]), - std::move(voice_receive_stats[voice_receive_channel])); - } else if (media_type == MediaType::VIDEO) { - auto video_send_channel = channel->video_media_send_channel(); - auto video_receive_channel = channel->video_media_receive_channel(); + std::move(voice_send).value_or(VoiceMediaSendInfo{}), + std::move(voice_receive).value_or(VoiceMediaReceiveInfo{})); + } else if (stats.media_type == MediaType::VIDEO) { + std::optional video_send = + refs.get_send_stats_video(); + std::optional video_receive = + refs.get_receive_stats_video(); video_media_info = VideoMediaInfo( - std::move(video_send_stats[video_send_channel]), - std::move(video_receive_stats[video_receive_channel])); + std::move(video_send).value_or(VideoMediaSendInfo{}), + std::move(video_receive).value_or(VideoMediaReceiveInfo{})); } } stats.track_media_info_map = std::make_unique( std::move(voice_media_info), std::move(video_media_info), - std::move(stats.sender_infos), std::move(stats.receiver_infos), - std::move(receiver_parameters)); - if (transceiver->media_type() == MediaType::AUDIO) { + std::move(stats.sender_infos), std::move(sender_parameters), + std::move(stats.receiver_infos), std::move(receiver_parameters)); + if (stats.media_type == MediaType::AUDIO) { has_audio_receiver |= stats.has_receivers; } } - call_stats_ = pc_->GetCallStats(); - audio_device_stats_ = + worker_result.results.call_stats = pc_->GetCallStats(); + worker_result.results.audio_device_stats = has_audio_receiver ? pc_->GetAudioDeviceStats() : std::nullopt; - }); + return worker_result; + }; } void RTCStatsCollector::OnSctpDataChannelStateChanged( diff --git a/pc/rtc_stats_collector.h b/pc/rtc_stats_collector.h index b9458bf0eab..f20a67d0e36 100644 --- a/pc/rtc_stats_collector.h +++ b/pc/rtc_stats_collector.h @@ -26,6 +26,7 @@ #include "api/data_channel_interface.h" #include "api/environment/environment.h" #include "api/media_types.h" +#include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" #include "api/stats/rtc_stats_collector_callback.h" @@ -34,6 +35,8 @@ #include "api/task_queue/task_queue_base.h" #include "api/units/timestamp.h" #include "call/call.h" +#include "media/base/media_channel.h" +#include "pc/data_channel_utils.h" #include "pc/peer_connection_internal.h" #include "pc/rtp_receiver.h" #include "pc/rtp_sender.h" @@ -42,7 +45,6 @@ #include "pc/transport_stats.h" #include "rtc_base/checks.h" #include "rtc_base/containers/flat_set.h" -#include "rtc_base/event.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" @@ -61,16 +63,29 @@ class RtpReceiverInternal; // If a BaseChannel is not available (e.g., if signaling has not started), // then `mid` and `transport_name` will be null. struct RtpTransceiverStatsInfo { - const scoped_refptr transceiver; const MediaType media_type; const std::optional mid; std::optional transport_name; std::vector sender_infos; std::vector receiver_infos; - std::vector> receivers; std::unique_ptr track_media_info_map; const std::optional current_direction; bool has_receivers = false; + const bool has_channel; +}; + +// References to objects used on the signaling and worker threads for populating +// RtpTransceiverStatsInfo but must always be released on the signaling thread +struct TransceiverReferences { + scoped_refptr transceiver; + std::vector> receivers; + absl::AnyInvocable()> get_send_stats_voice; + absl::AnyInvocable()> get_send_stats_video; + absl::AnyInvocable()> + get_receive_stats_voice; + absl::AnyInvocable()> + get_receive_stats_video; + absl::AnyInvocable get_send_parameters; }; // All public methods of the collector are to be called on the signaling thread. @@ -103,14 +118,13 @@ class RTCStatsCollector { // and it must be called any time negotiation happens. void ClearCachedStatsReport(); - // If there is a `GetStatsReport` requests in-flight, waits until it has been - // completed. Must be called on the signaling thread. - void WaitForPendingRequest(); - // Cancels pending stats gathering operations and prepares for shutdown. - // This method returns a task that the caller needs to make sure is executed - // on the network thread before the RTCStatsCollector instance is deleted. - absl::AnyInvocable CancelPendingRequestAndGetShutdownTask(); + // This method adds tasks that the caller needs to make sure is executed + // on the worker and network threads before the RTCStatsCollector instance is + // deleted. + void CancelPendingRequestAndGetShutdownTasks( + std::vector>& network_tasks, + std::vector>& worker_tasks); // Called by the PeerConnection instance when data channel states change. void OnSctpDataChannelStateChanged(int channel_id, @@ -133,16 +147,33 @@ class RTCStatsCollector { // Stats gathering on a particular thread. Virtual for the sake of testing. virtual void ProducePartialResultsOnSignalingThreadImpl( Timestamp timestamp, + const std::vector& transceiver_stats_infos, + const std::vector& transceiver_references, + const std::optional& audio_device_stats, RTCStatsReport* partial_report); - virtual void ProducePartialResultsOnNetworkThreadImpl( + void ProcessResultsFromNetworkThread( Timestamp timestamp, - const std::map& transport_stats_by_name, - const std::map& transport_cert_stats, - const std::vector& transceiver_stats_infos, + std::map transport_stats_by_name, + std::map transport_cert_stats, + std::vector transceiver_stats_infos, + Call::Stats call_stats, + std::optional audio_device_stats, RTCStatsReport* partial_report); private: + struct StatsGatheringResults { + std::vector transceiver_stats_infos; + Call::Stats call_stats; + std::optional audio_device_stats; + }; + + struct WorkerThreadResult { + StatsGatheringResults results; + std::vector> sender_parameters; + std::vector transceiver_references; + }; + struct CollectionContext; class RequestInfo { public: enum class FilterMode { kAll, kSenderSelector, kReceiverSelector }; @@ -185,50 +216,64 @@ class RTCStatsCollector { void GetStatsReportInternal(RequestInfo request); - void DeliverCachedReport(scoped_refptr cached_report, - std::vector requests); + // Invokes the completion callback for a pending request. + void DeliverReport(const RequestInfo& request, + const scoped_refptr& report); // Produces `RTCCertificateStats`. - void ProduceCertificateStats_n( + void ProduceCertificateStats_s( Timestamp timestamp, const std::map& transport_cert_stats, RTCStatsReport* report) const; // Produces `RTCDataChannelStats`. - void ProduceDataChannelStats_n(Timestamp timestamp, - RTCStatsReport* report) const; + void ProduceDataChannelStats_s( + Timestamp timestamp, + const std::vector& data_channel_stats, + RTCStatsReport* report) const; // Produces `RTCIceCandidatePairStats` and `RTCIceCandidateStats`. - void ProduceIceCandidateAndPairStats_n( + void ProduceIceCandidateAndPairStats_s( Timestamp timestamp, const std::map& transport_stats_by_name, const Call::Stats& call_stats, RTCStatsReport* report) const; // Produces RTCMediaSourceStats, including RTCAudioSourceStats and // RTCVideoSourceStats. - void ProduceMediaSourceStats_s(Timestamp timestamp, - RTCStatsReport* report) const; + void ProduceMediaSourceStats_s( + Timestamp timestamp, + const std::vector& transceiver_stats_infos, + const std::vector& transceiver_references, + RTCStatsReport* report) const; // Produces `RTCPeerConnectionStats`. void ProducePeerConnectionStats_s(Timestamp timestamp, RTCStatsReport* report) const; // Produces `RTCAudioPlayoutStats`. - void ProduceAudioPlayoutStats_s(Timestamp timestamp, - RTCStatsReport* report) const; + void ProduceAudioPlayoutStats_s( + Timestamp timestamp, + const std::optional& audio_device_stats, + RTCStatsReport* report) const; // Produces `RTCInboundRtpStreamStats`, `RTCOutboundRtpStreamStats`, // `RTCRemoteInboundRtpStreamStats`, `RTCRemoteOutboundRtpStreamStats` and any // referenced `RTCCodecStats`. This has to be invoked after transport stats // have been created because some metrics are calculated through lookup of // other metrics. - void ProduceRTPStreamStats_n( + void ProduceRTPStreamStats_s( Timestamp timestamp, const std::vector& transceiver_stats_infos, + const Call::Stats& call_stats, + const std::optional& audio_device_stats, RTCStatsReport* report) const; - void ProduceAudioRTPStreamStats_n(Timestamp timestamp, - const RtpTransceiverStatsInfo& stats, - RTCStatsReport* report) const; - void ProduceVideoRTPStreamStats_n(Timestamp timestamp, + void ProduceAudioRTPStreamStats_s( + Timestamp timestamp, + const RtpTransceiverStatsInfo& stats, + const Call::Stats& call_stats, + const std::optional& audio_device_stats, + RTCStatsReport* report) const; + void ProduceVideoRTPStreamStats_s(Timestamp timestamp, const RtpTransceiverStatsInfo& stats, + const Call::Stats& call_stats, RTCStatsReport* report) const; // Produces `RTCTransportStats`. - void ProduceTransportStats_n( + void ProduceTransportStats_s( Timestamp timestamp, const std::map& transport_stats_by_name, const std::map& transport_cert_stats, @@ -240,18 +285,25 @@ class RTCStatsCollector { PrepareTransportCertificateStats_n( const std::map& transport_stats_by_name); // The results are stored in `transceiver_stats_infos_` and `call_stats_`. - void PrepareTransceiverStatsInfosAndCallStats_s_w_n(); + // Prepares the transceiver stats infos and call stats. + // Returns a callback that should be executed on the worker thread to populate + // the stats. + absl::AnyInvocable + PrepareTransceiverStatsInfosAndCallStats_s_w(); // Stats gathering on a particular thread. - void ProducePartialResultsOnSignalingThread(Timestamp timestamp); + void ProducePartialResultsOnSignalingThread( + const std::vector& transceiver_stats_infos, + const std::vector& transceiver_references, + const std::optional& audio_device_stats); void ProducePartialResultsOnNetworkThread( scoped_refptr signaling_safety, Timestamp timestamp, std::set transport_names, - std::vector& transceiver_stats_infos); - // Merges `network_report_` into `partial_report_` and completes the request. - // This is a NO-OP if `network_report_` is null. - void MergeNetworkReport_s(); + StatsGatheringResults results); + // Merges `network_report` into `partial_report_` and completes the request. + void OnNetworkReportReady(scoped_refptr network_report, + std::vector data_channel_stats); scoped_refptr CreateReportFilteredBySelector( bool filter_by_sender_selector, @@ -267,45 +319,14 @@ class RTCStatsCollector { Thread* const worker_thread_; Thread* const network_thread_; - int num_pending_partial_reports_; - int64_t partial_report_timestamp_us_; - // Reports that are produced on the signaling thread or the network thread are - // merged into this report. It is only touched on the signaling thread. Once - // all partial reports are merged this is the result of a request. - scoped_refptr partial_report_; std::vector requests_ RTC_GUARDED_BY(signaling_thread_); - // Holds the result of ProducePartialResultsOnNetworkThread(). It is merged - // into `partial_report_` on the signaling thread and then nulled by - // MergeNetworkReport_s(). Thread-safety is ensured by using - // `network_report_event_`. - scoped_refptr network_report_; - // If set, it is safe to touch the `network_report_` on the signaling thread. - // This is reset before async-invoking ProducePartialResultsOnNetworkThread() - // and set when ProducePartialResultsOnNetworkThread() is complete, after it - // has updated the value of `network_report_`. - Event network_report_event_; - - // Cleared and set in `PrepareTransceiverStatsInfosAndCallStats_s_w_n`, - // starting out on the signaling thread, then network. Later read on the - // network and signaling threads as part of collecting stats and finally - // reset on the signaling thread when the work is done. - // Initially this variable was added and not passed around as an arguments to - // avoid copies. This is thread safe due to how operations are sequenced, - // sometimes blocking, and we don't start the stats collection sequence if one - // is in progress. As a future improvement though, we could now get rid of the - // variable and keep the data scoped within a stats collection sequence. - std::vector transceiver_stats_infos_ - RTC_GUARDED_BY(signaling_thread_); + // This cache avoids having to call webrtc::SSLCertChain::GetStats(), which // can relatively expensive. ClearCachedStatsReport() needs to be called on // negotiation to ensure the cache is not obsolete. std::map cached_certificates_by_transport_ RTC_GUARDED_BY(network_thread_); - Call::Stats call_stats_; - - std::optional audio_device_stats_; - // A timestamp, in microseconds, that is based on a timer that is // monotonically increasing. That is, even if the system clock is modified the // difference between the timer and this timestamp is how fresh the cached @@ -333,7 +354,11 @@ class RTCStatsCollector { }; InternalRecord internal_record_; const scoped_refptr signaling_safety_; + const scoped_refptr worker_safety_; const scoped_refptr network_safety_; + + std::unique_ptr collection_context_ + RTC_GUARDED_BY(signaling_thread_); }; } // namespace webrtc diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 52188dbaecf..18b4df29977 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -25,15 +26,15 @@ #include "absl/strings/str_replace.h" #include "api/audio/audio_device.h" #include "api/audio/audio_processing_statistics.h" +#include "api/audio_options.h" #include "api/candidate.h" #include "api/data_channel_interface.h" #include "api/dtls_transport_interface.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" -#include "api/media_stream_track.h" #include "api/media_types.h" +#include "api/peer_connection_interface.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" @@ -47,17 +48,14 @@ #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" -#include "api/video/recordable_encoded_frame.h" #include "api/video/video_content_type.h" -#include "api/video/video_frame.h" -#include "api/video/video_sink_interface.h" -#include "api/video/video_source_interface.h" #include "api/video/video_timing.h" #include "api/video_codecs/scalability_mode.h" #include "call/call.h" #include "common_video/include/quality_limitation_reason.h" #include "json/reader.h" #include "json/value.h" +#include "media/base/fake_media_engine.h" #include "media/base/media_channel.h" #include "media/base/stream_params.h" #include "modules/rtp_rtcp/include/report_block_data.h" @@ -69,17 +67,22 @@ #include "p2p/base/transport_description.h" #include "pc/media_stream.h" #include "pc/peer_connection_internal.h" +#include "pc/rtp_sender.h" +#include "pc/rtp_transceiver.h" #include "pc/sctp_data_channel.h" #include "pc/stream_collection.h" +#include "pc/test/fake_audio_track.h" #include "pc/test/fake_data_channel_controller.h" #include "pc/test/fake_peer_connection_for_stats.h" +#include "pc/test/fake_video_track.h" +#include "pc/test/fake_video_track_source.h" #include "pc/test/mock_data_channel.h" #include "pc/test/mock_rtp_receiver_internal.h" #include "pc/test/mock_rtp_sender_internal.h" #include "pc/test/rtc_stats_obtainer.h" #include "pc/transport_stats.h" #include "rtc_base/checks.h" -#include "rtc_base/fake_clock.h" +#include "rtc_base/event.h" #include "rtc_base/fake_ssl_identity.h" #include "rtc_base/network_constants.h" #include "rtc_base/rtc_certificate.h" @@ -89,13 +92,20 @@ #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" +#include "system_wrappers/include/clock.h" +#include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/run_loop.h" +#include "test/time_controller/simulated_time_controller.h" using ::testing::_; +using ::testing::IsEmpty; +using ::testing::Not; +using ::testing::NotNull; using ::testing::Return; namespace webrtc { @@ -194,129 +204,26 @@ class MockStatsCollectorCallback : public RTCStatsCollectorCallback { (override)); }; -class FakeAudioProcessor : public AudioProcessorInterface { - public: - FakeAudioProcessor() {} - ~FakeAudioProcessor() override {} - - private: - AudioProcessorInterface::AudioProcessorStatistics GetStats( - bool has_recv_streams) override { - AudioProcessorStatistics stats; - stats.apm_statistics.echo_return_loss = 2.0; - stats.apm_statistics.echo_return_loss_enhancement = 3.0; - return stats; - } -}; - -class FakeAudioTrackForStats : public MediaStreamTrack { - public: - static scoped_refptr Create( - const std::string& id, - MediaStreamTrackInterface::TrackState state, - bool create_fake_audio_processor) { - auto audio_track_stats = make_ref_counted(id); - audio_track_stats->set_state(state); - if (create_fake_audio_processor) { - audio_track_stats->processor_ = make_ref_counted(); - } - return audio_track_stats; - } - - explicit FakeAudioTrackForStats(const std::string& id) - : MediaStreamTrack(id) {} - - std::string kind() const override { - return MediaStreamTrackInterface::kAudioKind; - } - AudioSourceInterface* GetSource() const override { return nullptr; } - void AddSink(AudioTrackSinkInterface* sink) override {} - void RemoveSink(AudioTrackSinkInterface* sink) override {} - bool GetSignalLevel(int* level) override { return false; } - scoped_refptr GetAudioProcessor() override { - return processor_; - } - - private: - scoped_refptr processor_; -}; - -class FakeVideoTrackSourceForStats : public VideoTrackSourceInterface { - public: - static scoped_refptr Create(int input_width, - int input_height) { - return make_ref_counted(input_width, - input_height); - } - - FakeVideoTrackSourceForStats(int input_width, int input_height) - : input_width_(input_width), input_height_(input_height) {} - ~FakeVideoTrackSourceForStats() override {} - - // VideoTrackSourceInterface - bool is_screencast() const override { return false; } - std::optional needs_denoising() const override { return false; } - bool GetStats(VideoTrackSourceInterface::Stats* stats) override { - stats->input_width = input_width_; - stats->input_height = input_height_; - return true; - } - // MediaSourceInterface (part of VideoTrackSourceInterface) - MediaSourceInterface::SourceState state() const override { - return MediaSourceInterface::SourceState::kLive; - } - bool remote() const override { return false; } - // NotifierInterface (part of MediaSourceInterface) - void RegisterObserver(ObserverInterface* observer) override {} - void UnregisterObserver(ObserverInterface* observer) override {} - // webrtc::VideoSourceInterface (part of - // VideoTrackSourceInterface) - void AddOrUpdateSink(VideoSinkInterface* sink, - const VideoSinkWants& wants) override {} - void RemoveSink(VideoSinkInterface* sink) override {} - bool SupportsEncodedOutput() const override { return false; } - void GenerateKeyFrame() override {} - void AddEncodedSink( - VideoSinkInterface* sink) override {} - void RemoveEncodedSink( - VideoSinkInterface* sink) override {} - - private: - int input_width_; - int input_height_; -}; - -class FakeVideoTrackForStats : public MediaStreamTrack { - public: - static scoped_refptr Create( - const std::string& id, - MediaStreamTrackInterface::TrackState state, - scoped_refptr source) { - auto video_track = - make_ref_counted(id, std::move(source)); - video_track->set_state(state); - return video_track; - } - - FakeVideoTrackForStats(const std::string& id, - scoped_refptr source) - : MediaStreamTrack(id), source_(source) {} - - std::string kind() const override { - return MediaStreamTrackInterface::kVideoKind; - } - - void AddOrUpdateSink(VideoSinkInterface* sink, - const VideoSinkWants& wants) override {} - void RemoveSink(VideoSinkInterface* sink) override {} - - VideoTrackSourceInterface* GetSource() const override { - return source_.get(); +// Helper functions to create fake tracks for stats tests. +scoped_refptr CreateFakeAudioTrackForStats( + const std::string& id, + MediaStreamTrackInterface::TrackState state, + bool create_fake_audio_processor) { + scoped_refptr processor = nullptr; + if (create_fake_audio_processor) { + processor = make_ref_counted(); + processor->stats.apm_statistics.echo_return_loss = 2.0; + processor->stats.apm_statistics.echo_return_loss_enhancement = 3.0; } + return FakeAudioTrack::Create(id, state, processor); +} - private: - scoped_refptr source_; -}; +scoped_refptr CreateFakeVideoTrackForStats( + const std::string& id, + MediaStreamTrackInterface::TrackState state, + scoped_refptr source) { + return FakeVideoTrack::Create(id, state, source); +} scoped_refptr CreateFakeTrack( MediaType media_type, @@ -324,11 +231,11 @@ scoped_refptr CreateFakeTrack( MediaStreamTrackInterface::TrackState track_state, bool create_fake_audio_processor = false) { if (media_type == MediaType::AUDIO) { - return FakeAudioTrackForStats::Create(track_id, track_state, - create_fake_audio_processor); + return CreateFakeAudioTrackForStats(track_id, track_state, + create_fake_audio_processor); } else { RTC_DCHECK_EQ(media_type, MediaType::VIDEO); - return FakeVideoTrackForStats::Create(track_id, track_state, nullptr); + return CreateFakeVideoTrackForStats(track_id, track_state, nullptr); } } @@ -348,9 +255,9 @@ scoped_refptr CreateMockSender( EXPECT_CALL(*sender, ssrc()).WillRepeatedly(Return(ssrc)); EXPECT_CALL(*sender, media_type()).WillRepeatedly(Return(media_type)); EXPECT_CALL(*sender, GetParameters()).WillRepeatedly([s = sender.get()]() { - return s->GetParametersInternal(); + return s->GetParametersInternal(false, false); }); - EXPECT_CALL(*sender, GetParametersInternal()).WillRepeatedly([ssrc]() { + EXPECT_CALL(*sender, GetParametersInternal(_, _)).WillRepeatedly([ssrc]() { RtpParameters params; params.encodings.push_back(RtpEncodingParameters()); params.encodings[0].ssrc = ssrc; @@ -393,7 +300,7 @@ class RTCStatsCollectorWrapper { explicit RTCStatsCollectorWrapper( scoped_refptr pc, const Environment& env) - : pc_(pc), stats_collector_(pc_.get(), env) {} + : pc_(pc), env_(env), stats_collector_(pc_.get(), env) {} RTCStatsCollector& stats_collector() { return stats_collector_; } @@ -402,14 +309,14 @@ class RTCStatsCollectorWrapper { stats_collector_.GetStatsReport( RTCStatsObtainer::Create(&report, [&loop] { loop.Quit(); })); loop.Run(); - int64_t after = TimeUTCMicros(); + Timestamp after = env_.clock().CurrentTime(); for (const RTCStats& stats : *report) { if (stats.type() == RTCRemoteInboundRtpStreamStats::kType || stats.type() == RTCRemoteOutboundRtpStreamStats::kType) { // Ignore remote timestamps. continue; } - EXPECT_LE(stats.timestamp().us(), after); + EXPECT_LE(stats.timestamp(), after); } return report; } @@ -454,9 +361,9 @@ class RTCStatsCollectorWrapper { return media_type; }); EXPECT_CALL(*sender, GetParameters()).WillRepeatedly([s = sender.get()]() { - return s->GetParametersInternal(); + return s->GetParametersInternal(false, false); }); - EXPECT_CALL(*sender, GetParametersInternal()).WillRepeatedly([ssrc]() { + EXPECT_CALL(*sender, GetParametersInternal(_, _)).WillRepeatedly([ssrc]() { RtpParameters params; params.encodings.push_back(RtpEncodingParameters()); params.encodings[0].ssrc = ssrc; @@ -464,7 +371,10 @@ class RTCStatsCollectorWrapper { }); EXPECT_CALL(*sender, AttachmentId()).WillRepeatedly(Return(attachment_id)); EXPECT_CALL(*sender, Stop()).WillRepeatedly(Return()); + EXPECT_CALL(*sender, SetSendCodecs(_)).WillRepeatedly(Return()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); return sender; } @@ -495,7 +405,9 @@ class RTCStatsCollectorWrapper { .WillRepeatedly(Return( std::vector>({remote_stream}))); EXPECT_CALL(*receiver, SetMediaChannel(_)).WillRepeatedly(Return()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddReceiver(receiver); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); return receiver; } @@ -541,7 +453,9 @@ class RTCStatsCollectorWrapper { return nullptr; }); EXPECT_CALL(*rtp_sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(rtp_sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Remote audio tracks and voice receiver infos @@ -559,7 +473,9 @@ class RTCStatsCollectorWrapper { EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)).WillRepeatedly(Return()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddReceiver(rtp_receiver); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Local video tracks and video sender infos @@ -581,7 +497,9 @@ class RTCStatsCollectorWrapper { return nullptr; }); EXPECT_CALL(*rtp_sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(rtp_sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Remote video tracks and video receiver infos @@ -599,26 +517,41 @@ class RTCStatsCollectorWrapper { EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)).WillRepeatedly(Return()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddReceiver(rtp_receiver); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", "transport", voice_media_info); pc_->AddVideoChannel("video", "transport", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } private: scoped_refptr pc_; + const Environment env_; RTCStatsCollector stats_collector_; }; -class RTCStatsCollectorTest : public ::testing::Test { +class RTCStatsCollectorTestBase : public ::testing::Test { public: - RTCStatsCollectorTest() - : pc_(make_ref_counted()), - stats_(std::make_unique(pc_, - CreateEnvironment())), - data_channel_controller_(std::make_unique( - pc_->network_thread())) {} + RTCStatsCollectorTestBase() + : time_controller_(Timestamp::Zero()), + env_(CreateTestEnvironment({.time = time_controller_.GetClock()})), + pc_(make_ref_counted(env_)) { + data_channel_controller_ = + std::make_unique(pc_->network_thread()); + } + + void SetStatsTimestampWithEnvironmentClock(bool enabled) { + PeerConnectionInterface::RTCConfiguration config = pc_->GetConfiguration(); + config.set_stats_timestamp_with_environment_clock(enabled); + pc_->SetConfiguration(config); + // RTCStatsCollector reads this flag in the constructor, so we need to + // recreate it. + stats_ = std::make_unique(pc_, env_); + } void ExpectReportContainsCertificateInfo( const scoped_refptr& report, @@ -707,7 +640,9 @@ class RTCStatsCollectorTest : public ::testing::Test { video_media_info.receivers[0].packets_received = 123; // transport graph.transport_id = "TTransportName1"; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // outbound-rtp's sender graph.sender = stats_->SetupLocalTrackAndSender( MediaType::VIDEO, "LocalVideoTrackID", 3, false, 50); @@ -798,6 +733,8 @@ class RTCStatsCollectorTest : public ::testing::Test { graph.remote_outbound_rtp_id = "ROA4"; media_info.receivers[0].last_sender_report_utc_timestamp = kRemoteOutboundStatsTimestamp; + media_info.receivers[0].last_sender_report_timestamp = + kRemoteOutboundStatsTimestamp; media_info.receivers[0].last_sender_report_remote_utc_timestamp = kRemoteOutboundStatsRemoteTimestamp; media_info.receivers[0].sender_reports_packets_sent = @@ -809,7 +746,9 @@ class RTCStatsCollectorTest : public ::testing::Test { } // transport graph.transport_id = "TTransportName1"; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("VoiceMid", "TransportName", media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // outbound-rtp's sender graph.sender = stats_->SetupLocalTrackAndSender( MediaType::AUDIO, "LocalAudioTrackID", kLocalSsrc, false, 50); @@ -863,14 +802,21 @@ class RTCStatsCollectorTest : public ::testing::Test { } protected: - ScopedFakeClock fake_clock_; + GlobalSimulatedTimeController time_controller_; + Environment env_; RunLoop main_thread_; scoped_refptr pc_; std::unique_ptr stats_; std::unique_ptr data_channel_controller_; }; -TEST_F(RTCStatsCollectorTest, SingleCallback) { +class RTCStatsCollectorTest : public RTCStatsCollectorTestBase, + public ::testing::WithParamInterface { + public: + RTCStatsCollectorTest() { SetStatsTimestampWithEnvironmentClock(GetParam()); } +}; + +TEST_P(RTCStatsCollectorTest, SingleCallback) { scoped_refptr result; stats_->stats_collector().GetStatsReport( RTCStatsObtainer::Create(&result, [&] { main_thread_.Quit(); })); @@ -878,7 +824,7 @@ TEST_F(RTCStatsCollectorTest, SingleCallback) { EXPECT_TRUE(result); } -TEST_F(RTCStatsCollectorTest, MultipleCallbacks) { +TEST_P(RTCStatsCollectorTest, MultipleCallbacks) { scoped_refptr a, b, c; int remaining = 3; auto on_complete = [&] { @@ -900,7 +846,7 @@ TEST_F(RTCStatsCollectorTest, MultipleCallbacks) { EXPECT_EQ(b.get(), c.get()); } -TEST_F(RTCStatsCollectorTest, CachedStatsReports) { +TEST_P(RTCStatsCollectorTest, CachedStatsReports) { // Caching should ensure `a` and `b` are the same report. scoped_refptr a = stats_->GetStatsReport(main_thread_); scoped_refptr b = stats_->GetStatsReport(main_thread_); @@ -910,13 +856,13 @@ TEST_F(RTCStatsCollectorTest, CachedStatsReports) { scoped_refptr c = stats_->GetStatsReport(main_thread_); EXPECT_NE(b.get(), c.get()); // Invalidate cache by advancing time. - fake_clock_.AdvanceTime(TimeDelta::Millis(51)); + time_controller_.AdvanceTime(TimeDelta::Millis(51)); scoped_refptr d = stats_->GetStatsReport(main_thread_); EXPECT_TRUE(d); EXPECT_NE(c.get(), d.get()); } -TEST_F(RTCStatsCollectorTest, MultipleCallbacksWithInvalidatedCacheInBetween) { +TEST_P(RTCStatsCollectorTest, MultipleCallbacksWithInvalidatedCacheInBetween) { scoped_refptr a, b, c; int remaining = 3; auto on_complete = [&] { @@ -929,7 +875,7 @@ TEST_F(RTCStatsCollectorTest, MultipleCallbacksWithInvalidatedCacheInBetween) { RTCStatsObtainer::Create(&b, on_complete)); // Cache is invalidated after 50 ms. main_thread_.Flush(); - fake_clock_.AdvanceTime(TimeDelta::Millis(51)); + time_controller_.AdvanceTime(TimeDelta::Millis(51)); stats_->stats_collector().GetStatsReport( RTCStatsObtainer::Create(&c, on_complete)); main_thread_.Run(); @@ -942,7 +888,71 @@ TEST_F(RTCStatsCollectorTest, MultipleCallbacksWithInvalidatedCacheInBetween) { EXPECT_NE(c.get(), b.get()); } -TEST_F(RTCStatsCollectorTest, ToJsonProducesParseableJson) { +TEST_P(RTCStatsCollectorTest, StatsPreservedWhenChannelClearedConcurrently) { + VoiceMediaInfo voice_media_info; + voice_media_info.receivers.emplace_back(); + voice_media_info.receivers[0].add_ssrc(1); + voice_media_info.receivers[0].packets_received = 123; + + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); + pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); + auto remote_receiver = stats_->SetupRemoteTrackAndReceiver( + MediaType::AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1); + auto local_sender = stats_->SetupLocalTrackAndSender( + MediaType::AUDIO, "LocalAudioTrackID", 2, + /*add_stream=*/true, /*attachment_id=*/2); + + EXPECT_CALL(*local_sender, SetSendCodecs(_)).WillRepeatedly(Return()); + EXPECT_CALL(*local_sender, SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*local_sender, DetachTrackAndGetStopTask()).WillRepeatedly([] { + return nullptr; + }); + EXPECT_CALL(*remote_receiver, SetMediaChannel(_)).WillRepeatedly(Return()); + + scoped_refptr report; + bool stats_obtained = false; + stats_->stats_collector().GetStatsReport( + RTCStatsObtainer::Create(&report, [&] { + stats_obtained = true; + main_thread_.Quit(); + })); + + auto transceivers = pc_->GetTransceiversInternal(); + ASSERT_FALSE(transceivers.empty()); + auto* transceiver = transceivers[0]->internal(); + EXPECT_TRUE(transceiver->HasChannel()); + + auto network_task = transceiver->GetClearChannelNetworkTask(); + pc_->network_thread()->BlockingCall([&]() { std::move(network_task)(); }); + + auto delete_worker_task = + transceiver->GetDeleteChannelWorkerTask(/*stop_senders=*/false); + // Now the state of the transceiver is that on the signaling thread, + // there is no channel. However, the work of actually deleting the channels + // belongs to the worker thread task, which hasn't run yet. + // Let's post the execution of that task so that it ends up running + // *after* the task that the `stats_collector` has queued up for + // gathering the stats from the channel. The stats collector should + // be able to get the stats safely even though technically the + // transceiver by now, doesn't have a channel. + EXPECT_FALSE(transceiver->HasChannel()); + Event worker_task_done; + pc_->worker_thread()->PostTask([&]() { + std::move(delete_worker_task)(); + worker_task_done.Set(); + }); + + main_thread_.Run(); + + EXPECT_TRUE(report); + auto inbound_rtps = report->GetStatsOfType(); + EXPECT_EQ(inbound_rtps.size(), 1u); + // Wait for the cleanup task to complete. + worker_task_done.Wait(Event::kForever); +} + +TEST_P(RTCStatsCollectorTest, ToJsonProducesParseableJson) { ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests(); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -959,10 +969,40 @@ TEST_F(RTCStatsCollectorTest, ToJsonProducesParseableJson) { EXPECT_EQ(report->size(), json_value.size()); } -TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsSingle) { +TEST_P(RTCStatsCollectorTest, ProduceRTPStreamStatsWithStoppedTransceiver) { + const char kTransportName[] = "transport"; + const char kMid[] = "VideoMid"; + + VideoMediaInfo video_media_info; + video_media_info.senders.emplace_back(); + video_media_info.senders[0].add_ssrc(1); + + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); + pc_->AddVideoChannel(kMid, kTransportName, video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); + + // Get the transceiver and set it to stopped. + std::vector>> + transceivers = pc_->GetTransceiversInternal(); + ASSERT_THAT(transceivers, Not(IsEmpty())); + scoped_refptr> transceiver = + transceivers[0]; + ASSERT_TRUE(transceiver); + transceiver->internal()->set_current_direction( + RtpTransceiverDirection::kStopped); + + // This should not crash. + scoped_refptr report = + stats_->GetStatsReport(main_thread_); + EXPECT_THAT(report, NotNull()); +} + +TEST_P(RTCStatsCollectorTest, CollectRTCCertificateStatsSingle) { const char kTransportName[] = "transport"; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); std::unique_ptr local_certinfo = CreateFakeCertificateAndInfoFromDers( @@ -984,7 +1024,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsSingle) { } // These SSRC collisions are legal. -TEST_F(RTCStatsCollectorTest, ValidSsrcCollisionDoesNotCrash) { +TEST_P(RTCStatsCollectorTest, ValidSsrcCollisionDoesNotCrash) { // BUNDLE audio/video inbound/outbound. Unique SSRCs needed within the BUNDLE. VoiceMediaInfo mid1_info; mid1_info.receivers.emplace_back(); @@ -992,14 +1032,18 @@ TEST_F(RTCStatsCollectorTest, ValidSsrcCollisionDoesNotCrash) { mid1_info.receivers[0].packets_received = 123; mid1_info.senders.emplace_back(); mid1_info.senders[0].add_ssrc(2); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("Mid1", "Transport1", mid1_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); VideoMediaInfo mid2_info; mid2_info.receivers.emplace_back(); mid2_info.receivers[0].add_ssrc(3); mid2_info.receivers[0].packets_received = 123; mid2_info.senders.emplace_back(); mid2_info.senders[0].add_ssrc(4); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid2", "Transport1", mid2_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // Now create a second BUNDLE group with SSRCs colliding with the first group // (but again no collisions within the group). VoiceMediaInfo mid3_info; @@ -1008,14 +1052,18 @@ TEST_F(RTCStatsCollectorTest, ValidSsrcCollisionDoesNotCrash) { mid3_info.receivers[0].packets_received = 123; mid3_info.senders.emplace_back(); mid3_info.senders[0].add_ssrc(2); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("Mid3", "Transport2", mid3_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); VideoMediaInfo mid4_info; mid4_info.receivers.emplace_back(); mid4_info.receivers[0].add_ssrc(3); mid4_info.receivers[0].packets_received = 123; mid4_info.senders.emplace_back(); mid4_info.senders[0].add_ssrc(4); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid4", "Transport2", mid4_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // This should not crash (https://crbug.com/1361612). scoped_refptr report = @@ -1029,32 +1077,40 @@ TEST_F(RTCStatsCollectorTest, ValidSsrcCollisionDoesNotCrash) { // These SSRC collisions are illegal, so it is not clear if this setup can // happen even when talking to a malicious endpoint, but simulate illegal SSRC // collisions just to make sure we don't crash in even the most extreme cases. -TEST_F(RTCStatsCollectorTest, InvalidSsrcCollisionDoesNotCrash) { +TEST_P(RTCStatsCollectorTest, InvalidSsrcCollisionDoesNotCrash) { // One SSRC to rule them all. VoiceMediaInfo mid1_info; mid1_info.receivers.emplace_back(); mid1_info.receivers[0].add_ssrc(1); mid1_info.senders.emplace_back(); mid1_info.senders[0].add_ssrc(1); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("Mid1", "BundledTransport", mid1_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); VideoMediaInfo mid2_info; mid2_info.receivers.emplace_back(); mid2_info.receivers[0].add_ssrc(1); mid2_info.senders.emplace_back(); mid2_info.senders[0].add_ssrc(1); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid2", "BundledTransport", mid2_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); VoiceMediaInfo mid3_info; mid3_info.receivers.emplace_back(); mid3_info.receivers[0].add_ssrc(1); mid3_info.senders.emplace_back(); mid3_info.senders[0].add_ssrc(1); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("Mid3", "BundledTransport", mid3_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); VideoMediaInfo mid4_info; mid4_info.receivers.emplace_back(); mid4_info.receivers[0].add_ssrc(1); mid4_info.senders.emplace_back(); mid4_info.senders[0].add_ssrc(1); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid4", "BundledTransport", mid4_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // This should not crash (https://crbug.com/1361612). stats_->GetStatsReport(main_thread_); @@ -1062,7 +1118,7 @@ TEST_F(RTCStatsCollectorTest, InvalidSsrcCollisionDoesNotCrash) { // should look. We only care about not crashing. } -TEST_F(RTCStatsCollectorTest, CollectRTCCodecStatsOnlyIfReferenced) { +TEST_P(RTCStatsCollectorTest, CollectRTCCodecStatsOnlyIfReferenced) { // Audio VoiceMediaInfo voice_media_info; @@ -1130,10 +1186,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStatsOnlyIfReferenced) { outbound_video_info.codec_payload_type = 4; video_media_info.senders.push_back(outbound_video_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto audio_channels = pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); auto video_channels = pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -1212,7 +1270,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCodecStatsOnlyIfReferenced) { EXPECT_FALSE(report->Get(expected_outbound_video_codec.id())); } -TEST_F(RTCStatsCollectorTest, CodecStatsAreCollectedPerTransport) { +TEST_P(RTCStatsCollectorTest, CodecStatsAreCollectedPerTransport) { // PT=10 RtpCodecParameters outbound_codec_pt10; outbound_codec_pt10.payload_type = 10; @@ -1258,9 +1316,11 @@ TEST_F(RTCStatsCollectorTest, CodecStatsAreCollectedPerTransport) { outbound_codec_pt11.payload_type; // First two mids contain subsets, the third one contains all PTs. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid1", "FirstTransport", info_pt10); pc_->AddVideoChannel("Mid2", "FirstTransport", info_pt11); pc_->AddVideoChannel("Mid3", "FirstTransport", info_pt10_pt11); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // There should be no duplicate codecs because all codec references are on the // same transport. @@ -1271,14 +1331,16 @@ TEST_F(RTCStatsCollectorTest, CodecStatsAreCollectedPerTransport) { // If a second transport is added with the same PT information, this does // count as different codec objects. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid4", "SecondTransport", info_pt10_pt11); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->stats_collector().ClearCachedStatsReport(); report = stats_->GetStatsReport(main_thread_); codec_stats = report->GetStatsOfType(); EXPECT_EQ(codec_stats.size(), 4u); } -TEST_F(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) { +TEST_P(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) { // PT=111, useinbandfec=0 RtpCodecParameters inbound_codec_pt111_nofec; inbound_codec_pt111_nofec.payload_type = 111; @@ -1315,8 +1377,10 @@ TEST_F(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) { inbound_codec_pt111_fec.payload_type; // First two mids contain subsets, the third one contains all PTs. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid1", "BundledTransport", info_nofec); pc_->AddVideoChannel("Mid2", "BundledTransport", info_fec); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // Despite having the same PT we should see two codec stats because their FMTP // lines are different. @@ -1331,8 +1395,10 @@ TEST_F(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) { info_fec.receivers[0].local_stats[0].ssrc = 21; // Adding more m= sections that does have the same FMTP lines does not result // in duplicates. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid3", "BundledTransport", info_nofec); pc_->AddVideoChannel("Mid4", "BundledTransport", info_fec); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->stats_collector().ClearCachedStatsReport(); report = stats_->GetStatsReport(main_thread_); codec_stats = report->GetStatsOfType(); @@ -1355,18 +1421,22 @@ TEST_F(RTCStatsCollectorTest, SamePayloadTypeButDifferentFmtpLines) { info_fec_pt112.receivers[0].packets_received = 123; info_fec_pt112.receivers[0].codec_payload_type = inbound_codec_pt112_fec.payload_type; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid5", "BundledTransport", info_fec_pt112); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->stats_collector().ClearCachedStatsReport(); report = stats_->GetStatsReport(main_thread_); codec_stats = report->GetStatsOfType(); EXPECT_EQ(codec_stats.size(), 3u); } -TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsMultiple) { +TEST_P(RTCStatsCollectorTest, CollectRTCCertificateStatsMultiple) { const char kAudioTransport[] = "audio"; const char kVideoTransport[] = "video"; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", kAudioTransport); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); std::unique_ptr audio_local_certinfo = CreateFakeCertificateAndInfoFromDers( @@ -1379,7 +1449,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsMultiple) { kAudioTransport, audio_remote_certinfo->certificate->GetSSLCertificateChain().Clone()); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("video", kVideoTransport); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); std::unique_ptr video_local_certinfo = CreateFakeCertificateAndInfoFromDers( std::vector({"(local) video"})); @@ -1399,10 +1471,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsMultiple) { ExpectReportContainsCertificateInfo(report, *video_remote_certinfo); } -TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsChain) { +TEST_P(RTCStatsCollectorTest, CollectRTCCertificateStatsChain) { const char kTransportName[] = "transport"; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); std::unique_ptr local_certinfo = CreateFakeCertificateAndInfoFromDers( @@ -1423,11 +1497,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsChain) { ExpectReportContainsCertificateInfo(report, *remote_certinfo); } -TEST_F(RTCStatsCollectorTest, CertificateStatsCache) { +TEST_P(RTCStatsCollectorTest, CertificateStatsCache) { const char kTransportName[] = "transport"; - ScopedFakeClock fake_clock; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // Set local and remote cerificates. std::unique_ptr initial_local_certinfo = @@ -1455,10 +1530,10 @@ TEST_F(RTCStatsCollectorTest, CertificateStatsCache) { ASSERT_TRUE(first_local_cert1); ASSERT_TRUE(first_remote_cert0); ASSERT_TRUE(first_remote_cert1); - EXPECT_EQ(first_local_cert0->timestamp().us(), TimeMicros()); - EXPECT_EQ(first_local_cert1->timestamp().us(), TimeMicros()); - EXPECT_EQ(first_remote_cert0->timestamp().us(), TimeMicros()); - EXPECT_EQ(first_remote_cert1->timestamp().us(), TimeMicros()); + EXPECT_EQ(first_local_cert0->timestamp(), env_.clock().CurrentTime()); + EXPECT_EQ(first_local_cert1->timestamp(), env_.clock().CurrentTime()); + EXPECT_EQ(first_remote_cert0->timestamp(), env_.clock().CurrentTime()); + EXPECT_EQ(first_remote_cert1->timestamp(), env_.clock().CurrentTime()); // Replace all certificates. std::unique_ptr updated_local_certinfo = @@ -1480,7 +1555,7 @@ TEST_F(RTCStatsCollectorTest, CertificateStatsCache) { // Advance time to ensure a fresh stats report, but don't clear the // certificate stats cache. - fake_clock.AdvanceTime(TimeDelta::Seconds(1)); + time_controller_.AdvanceTime(TimeDelta::Seconds(1)); scoped_refptr second_report = stats_->GetStatsReport(main_thread_); // We expect to see the same certificates as before due to not clearing the @@ -1507,10 +1582,10 @@ TEST_F(RTCStatsCollectorTest, CertificateStatsCache) { EXPECT_EQ(*second_remote_cert1->fingerprint, initial_remote_certinfo->fingerprints[1]); // But timestamps are up-to-date, because this is a fresh stats report. - EXPECT_EQ(second_local_cert0->timestamp().us(), TimeMicros()); - EXPECT_EQ(second_local_cert1->timestamp().us(), TimeMicros()); - EXPECT_EQ(second_remote_cert0->timestamp().us(), TimeMicros()); - EXPECT_EQ(second_remote_cert1->timestamp().us(), TimeMicros()); + EXPECT_EQ(second_local_cert0->timestamp(), env_.clock().CurrentTime()); + EXPECT_EQ(second_local_cert1->timestamp(), env_.clock().CurrentTime()); + EXPECT_EQ(second_remote_cert0->timestamp(), env_.clock().CurrentTime()); + EXPECT_EQ(second_remote_cert1->timestamp(), env_.clock().CurrentTime()); // The updated certificates are not part of the report yet. EXPECT_FALSE(GetCertificateStatsFromFingerprint( second_report, updated_local_certinfo->fingerprints[0])); @@ -1541,7 +1616,7 @@ TEST_F(RTCStatsCollectorTest, CertificateStatsCache) { third_report, updated_remote_certinfo->fingerprints[1])); } -TEST_F(RTCStatsCollectorTest, CollectTwoRTCDataChannelStatsWithPendingId) { +TEST_P(RTCStatsCollectorTest, CollectTwoRTCDataChannelStatsWithPendingId) { // Note: The test assumes data channel IDs are predictable. // This is not a safe assumption, but in order to make it work for // the test, we reset the ID allocator at test start. @@ -1571,7 +1646,7 @@ TEST_F(RTCStatsCollectorTest, CollectTwoRTCDataChannelStatsWithPendingId) { report->Get(expected_data_channel0.id())->cast_to()); } -TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { +TEST_P(RTCStatsCollectorTest, CollectRTCDataChannelStats) { // Note: The test assumes data channel IDs are predictable. // This is not a safe assumption, but in order to make it work for // the test, we reset the ID allocator at test start. @@ -1649,7 +1724,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { report->Get(expected_data_channel3.id())->cast_to()); } -TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { +TEST_P(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { // Candidates in the first transport stats. std::unique_ptr a_local_host = CreateFakeCandidate( "1.2.3.4", 5, "a_local_host's protocol", ADAPTER_TYPE_VPN, @@ -1844,7 +1919,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { a_transport_channel_stats.ice_transport_stats.candidate_stats_list.push_back( CandidateStats(*a_local_host_not_paired)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", "a"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); pc_->SetTransportStats("a", a_transport_channel_stats); TransportChannelStats b_transport_channel_stats; @@ -1855,7 +1932,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { b_transport_channel_stats.ice_transport_stats.connection_infos[0] .remote_candidate = *b_remote; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("video", "b"); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); pc_->SetTransportStats("b", b_transport_channel_stats); scoped_refptr report = @@ -1899,7 +1978,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { EXPECT_TRUE(report->Get("Tb0")); } -TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { +TEST_P(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { const char kTransportName[] = "transport"; std::unique_ptr local_candidate = @@ -1942,7 +2021,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { transport_channel_stats.ice_transport_stats.connection_infos.push_back( connection_info); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("video", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); pc_->SetTransportStats(kTransportName, transport_channel_stats); scoped_refptr report = @@ -2079,7 +2160,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { ->cast_to()); } -TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { +TEST_P(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { { scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -2160,7 +2241,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { } } -TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio) { +TEST_P(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio) { VoiceMediaInfo voice_media_info; voice_media_info.receivers.push_back(VoiceReceiverInfo()); @@ -2212,8 +2293,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio) { .num_packets_reported_lost = 222, .num_packets_reported_recovered = 200}; pc_->SetCallStats(call_stats); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto voice_media_channels = pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupRemoteTrackAndReceiver(MediaType::AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1); @@ -2291,7 +2374,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio) { EXPECT_TRUE(report->Get(*expected_audio.codec_id)); } -TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio_PlayoutId) { +TEST_P(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio_PlayoutId) { VoiceMediaInfo voice_media_info; voice_media_info.receivers.push_back(VoiceReceiverInfo()); @@ -2299,7 +2382,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio_PlayoutId) { voice_media_info.receivers[0].local_stats[0].ssrc = 1; voice_media_info.receivers[0].packets_received = 123; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupRemoteTrackAndReceiver(MediaType::AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1); // Needed for playoutId to be populated. @@ -2330,7 +2415,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Audio_PlayoutId) { } } -TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) { +TEST_P(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) { VideoMediaInfo video_media_info; video_media_info.receivers.push_back(VideoReceiverInfo()); @@ -2402,8 +2487,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) { .num_packets_reported_lost = 222, .num_packets_reported_recovered = 200}; pc_->SetCallStats(call_stats); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto video_media_channels = pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupRemoteTrackAndReceiver(MediaType::VIDEO, "RemoteVideoTrackID", "RemoteStreamId", 1); @@ -2500,7 +2587,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRtpStreamStats_Video) { EXPECT_TRUE(report->Get(*expected_video.codec_id)); } -TEST_F(RTCStatsCollectorTest, CollectRTCAudioPlayoutStats) { +TEST_P(RTCStatsCollectorTest, CollectRTCAudioPlayoutStats) { AudioDeviceModule::Stats audio_device_stats; audio_device_stats.synthesized_samples_duration_s = 1; audio_device_stats.synthesized_samples_events = 2; @@ -2509,7 +2596,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCAudioPlayoutStats) { audio_device_stats.total_playout_delay_s = 5; pc_->SetAudioDeviceStats(audio_device_stats); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("AudioMid", "TransportName", {}); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupRemoteTrackAndReceiver(MediaType::AUDIO, "RemoteAudioTrackID", "RemoteStreamId", 1); @@ -2531,7 +2620,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCAudioPlayoutStats) { expected_stats); } -TEST_F(RTCStatsCollectorTest, CollectGoogTimingFrameInfo) { +TEST_P(RTCStatsCollectorTest, CollectGoogTimingFrameInfo) { VideoMediaInfo video_media_info; video_media_info.receivers.push_back(VideoReceiverInfo()); @@ -2555,7 +2644,9 @@ TEST_F(RTCStatsCollectorTest, CollectGoogTimingFrameInfo) { timing_frame_info.flags = 14; video_media_info.receivers[0].timing_frame_info = timing_frame_info; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("Mid0", "Transport0", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupRemoteTrackAndReceiver(MediaType::VIDEO, "RemoteVideoTrackID", "RemoteStreamId", 1); @@ -2568,7 +2659,7 @@ TEST_F(RTCStatsCollectorTest, CollectGoogTimingFrameInfo) { "1,2,3,4,5,6,7,8,9,10,11,12,13,1,0"); } -TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Audio) { +TEST_P(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Audio) { VoiceMediaInfo voice_media_info; voice_media_info.senders.push_back(VoiceSenderInfo()); @@ -2594,7 +2685,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Audio) { voice_media_info.send_codecs.insert( std::make_pair(codec_parameters.payload_type, codec_parameters)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupLocalTrackAndSender(MediaType::AUDIO, "LocalAudioTrackID", 1, true, /*attachment_id=*/50); @@ -2635,7 +2728,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Audio) { EXPECT_TRUE(report->Get(*expected_audio.codec_id)); } -TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) { +TEST_P(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) { VideoMediaInfo video_media_info; video_media_info.senders.push_back(VideoSenderInfo()); @@ -2693,11 +2786,21 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) { video_media_info.senders[1].rid = "h"; video_media_info.senders[1].encoding_index = 1; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); auto video_media_channels = pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); - stats_->SetupLocalTrackAndSender(MediaType::VIDEO, "LocalVideoTrackID", 1, - true, - /*attachment_id=*/50); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); + auto video_sender = stats_->SetupLocalTrackAndSender( + MediaType::VIDEO, "LocalVideoTrackID", 1, true, + /*attachment_id=*/50); + + // Set up the fake video channel to return the expected RtpParameters + // containing both simulcast encodings for the primary SSRC. + StreamParams sp; + sp.add_ssrc(1); + sp.add_ssrc(2); + sp.ssrc_groups.push_back(SsrcGroup(kSimSsrcGroupSemantics, {1, 2})); + video_media_channels.first->AddSendStream(sp); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -2705,7 +2808,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) { auto stats_of_my_type = report->GetStatsOfType(); ASSERT_EQ(2U, stats_of_my_type.size()); std::string id_of_first_ssrc; - for (const auto* outbound_rtp : stats_of_my_type) { + for (const RTCOutboundRtpStreamStats* outbound_rtp : stats_of_my_type) { if (outbound_rtp->ssrc.value_or(0) == 1) { id_of_first_ssrc = outbound_rtp->id(); break; @@ -2762,6 +2865,21 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) { report->Get(expected_video.id())->cast_to(), expected_video); + // Find the sub-stats for the second simulcast outbound RTP stream and verify + // that its `media_source_id` is set. + std::string id_of_second_ssrc; + for (const RTCOutboundRtpStreamStats* outbound_rtp : stats_of_my_type) { + if (outbound_rtp->ssrc.value_or(0) == 2) { + id_of_second_ssrc = outbound_rtp->id(); + break; + } + } + ASSERT_TRUE(report->Get(id_of_second_ssrc)); + auto& second_video = + report->Get(id_of_second_ssrc)->cast_to(); + EXPECT_TRUE(second_video.media_source_id.has_value()); + EXPECT_EQ(*second_video.media_source_id, "SV50"); + // Set previously undefined values and "GetStats" again. video_media_info.senders[0].qp_sum = 9; expected_video.qp_sum = 9; @@ -2789,10 +2907,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) { EXPECT_TRUE(report->Get(*expected_video.codec_id)); } -TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { +TEST_P(RTCStatsCollectorTest, CollectRTCTransportStats) { const char kTransportName[] = "transport"; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); std::unique_ptr rtp_local_candidate = CreateFakeCandidate("42.42.42.42", 42, "protocol", ADAPTER_TYPE_WIFI, @@ -2959,10 +3079,12 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { report->Get(expected_rtcp_transport.id())->cast_to()); } -TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { +TEST_P(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { const char kTransportName[] = "transport"; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("audio", kTransportName); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); std::unique_ptr rtp_local_candidate = CreateFakeCandidate("42.42.42.42", 42, "protocol", ADAPTER_TYPE_WIFI, @@ -3037,7 +3159,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { report->Get(expected_rtp_transport.id())->cast_to()); } -TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRtpStreamStats_Audio) { +TEST_P(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRtpStreamStats_Audio) { VoiceMediaInfo voice_media_info; voice_media_info.senders.push_back(VoiceSenderInfo()); @@ -3063,7 +3185,9 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRtpStreamStats_Audio) { std::make_pair(codec_parameters.payload_type, codec_parameters)); // Emulates the case where AddTrack is used without an associated MediaStream + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupLocalTrackAndSender(MediaType::AUDIO, "LocalAudioTrackID", 1, false, /*attachment_id=*/50); @@ -3097,7 +3221,7 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRtpStreamStats_Audio) { EXPECT_TRUE(report->Get(*expected_audio.codec_id)); } -TEST_F(RTCStatsCollectorTest, RTCAudioSourceStatsCollectedForSenderWithTrack) { +TEST_P(RTCStatsCollectorTest, RTCAudioSourceStatsCollectedForSenderWithTrack) { const uint32_t kSsrc = 4; const int kAttachmentId = 42; @@ -3111,7 +3235,9 @@ TEST_F(RTCStatsCollectorTest, RTCAudioSourceStatsCollectedForSenderWithTrack) { voice_media_info.senders[0].apm_statistics.echo_return_loss = 42.0; voice_media_info.senders[0].apm_statistics.echo_return_loss_enhancement = 52.0; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); stats_->SetupLocalTrackAndSender(MediaType::AUDIO, "LocalAudioTrackID", kSsrc, false, kAttachmentId); @@ -3132,7 +3258,7 @@ TEST_F(RTCStatsCollectorTest, RTCAudioSourceStatsCollectedForSenderWithTrack) { expected_audio); } -TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { +TEST_P(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { const uint32_t kSsrc = 4; const int kAttachmentId = 42; const int kVideoSourceWidth = 12; @@ -3149,11 +3275,13 @@ TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { video_media_info.aggregated_senders[0].local_stats[0].ssrc = kSsrc; video_media_info.aggregated_senders[0].framerate_input = 29.0; video_media_info.aggregated_senders[0].frames = 10001; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); - auto video_source = FakeVideoTrackSourceForStats::Create(kVideoSourceWidth, - kVideoSourceHeight); - auto video_track = FakeVideoTrackForStats::Create( + auto video_source = FakeVideoTrackSource::Create(false); + video_source->SetSize(kVideoSourceWidth, kVideoSourceHeight); + auto video_track = CreateFakeVideoTrackForStats( "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); scoped_refptr sender = CreateMockSender(MediaType::VIDEO, video_track, kSsrc, kAttachmentId, {}); @@ -3162,7 +3290,9 @@ TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { }); EXPECT_CALL(*sender, SetMediaChannel(_)).WillRepeatedly(Return()); EXPECT_CALL(*sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -3184,7 +3314,7 @@ TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { // behavior is to report frame rate even if we have no SSRC. // TODO(hbos): When we know the frame rate even if we have no SSRC, update the // expectations of this test. -TEST_F(RTCStatsCollectorTest, +TEST_P(RTCStatsCollectorTest, RTCVideoSourceStatsMissingFrameRateWhenSenderHasNoSsrc) { // TODO(https://crbug.com/webrtc/8694): When 0 is no longer a magic value for // "none", update this test. @@ -3197,11 +3327,13 @@ TEST_F(RTCStatsCollectorTest, video_media_info.senders.push_back(VideoSenderInfo()); video_media_info.senders[0].local_stats.push_back(SsrcSenderInfo()); video_media_info.senders[0].framerate_input = 29.0; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); - auto video_source = FakeVideoTrackSourceForStats::Create(kVideoSourceWidth, - kVideoSourceHeight); - auto video_track = FakeVideoTrackForStats::Create( + auto video_source = FakeVideoTrackSource::Create(false); + video_source->SetSize(kVideoSourceWidth, kVideoSourceHeight); + auto video_track = CreateFakeVideoTrackForStats( "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); scoped_refptr sender = CreateMockSender( MediaType::VIDEO, video_track, kNoSsrc, kAttachmentId, {}); @@ -3210,7 +3342,9 @@ TEST_F(RTCStatsCollectorTest, }); EXPECT_CALL(*sender, SetMediaChannel(_)).WillRepeatedly(Return()); EXPECT_CALL(*sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -3222,7 +3356,7 @@ TEST_F(RTCStatsCollectorTest, // The track not having a source is not expected to be true in practise, but // this is true in some tests relying on fakes. This test covers that code path. -TEST_F(RTCStatsCollectorTest, +TEST_P(RTCStatsCollectorTest, RTCVideoSourceStatsMissingResolutionWhenTrackHasNoSource) { const uint32_t kSsrc = 4; const int kAttachmentId = 42; @@ -3232,9 +3366,11 @@ TEST_F(RTCStatsCollectorTest, video_media_info.senders[0].local_stats.push_back(SsrcSenderInfo()); video_media_info.senders[0].local_stats[0].ssrc = kSsrc; video_media_info.senders[0].framerate_input = 29.0; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); - auto video_track = FakeVideoTrackForStats::Create( + auto video_track = CreateFakeVideoTrackForStats( "LocalVideoTrackID", MediaStreamTrackInterface::kLive, /*source=*/nullptr); scoped_refptr sender = @@ -3244,7 +3380,9 @@ TEST_F(RTCStatsCollectorTest, }); EXPECT_CALL(*sender, SetMediaChannel(_)).WillRepeatedly(Return()); EXPECT_CALL(*sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -3254,7 +3392,7 @@ TEST_F(RTCStatsCollectorTest, EXPECT_FALSE(video_stats.height.has_value()); } -TEST_F(RTCStatsCollectorTest, +TEST_P(RTCStatsCollectorTest, RTCAudioSourceStatsNotCollectedForSenderWithoutTrack) { const uint32_t kSsrc = 4; const int kAttachmentId = 42; @@ -3263,7 +3401,9 @@ TEST_F(RTCStatsCollectorTest, voice_media_info.senders.push_back(VoiceSenderInfo()); voice_media_info.senders[0].local_stats.push_back(SsrcSenderInfo()); voice_media_info.senders[0].local_stats[0].ssrc = kSsrc; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr sender = CreateMockSender( MediaType::AUDIO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); EXPECT_CALL(*sender, DetachTrackAndGetStopTask()).WillRepeatedly([] { @@ -3271,21 +3411,23 @@ TEST_F(RTCStatsCollectorTest, }); EXPECT_CALL(*sender, SetMediaChannel(_)).WillRepeatedly(Return()); EXPECT_CALL(*sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr report = stats_->GetStatsReport(main_thread_); EXPECT_FALSE(report->Get("SA42")); } -// Parameterized tests on webrtc::MediaType (audio or video). +// Parameterized tests on webrtc::MediaType (audio or video) and a boolean +// indicating if the stats timestamp should use the environment clock. class RTCStatsCollectorTestWithParamKind - : public RTCStatsCollectorTest, - public ::testing::WithParamInterface { + : public RTCStatsCollectorTestBase, + public ::testing::WithParamInterface> { public: - RTCStatsCollectorTestWithParamKind() : media_type_(GetParam()) { - RTC_DCHECK(media_type_ == MediaType::AUDIO || - media_type_ == MediaType::VIDEO); + RTCStatsCollectorTestWithParamKind() : media_type_(std::get<0>(GetParam())) { + SetStatsTimestampWithEnvironmentClock(std::get<1>(GetParam())); } std::string MediaTypeCharStr() const { @@ -3333,7 +3475,9 @@ class RTCStatsCollectorTestWithParamKind sender.report_block_datas.push_back(report_block_data); voice_media_info.senders.push_back(sender); } + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVoiceChannel("mid", transport_name, voice_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); return; } case MediaType::VIDEO: { @@ -3351,7 +3495,9 @@ class RTCStatsCollectorTestWithParamKind video_media_info.aggregated_senders.push_back(sender); video_media_info.senders.push_back(sender); } + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("mid", transport_name, video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); return; } case MediaType::DATA: @@ -3368,7 +3514,11 @@ class RTCStatsCollectorTestWithParamKind // RTCCodecStats (codecId, jitter) and without setting up an RTCP transport. TEST_P(RTCStatsCollectorTestWithParamKind, RTCRemoteInboundRtpStreamStatsCollectedFromReportBlock) { - const Timestamp kReportBlockTimestampUtc = Timestamp::Micros(123456789); + constexpr TimeDelta kReportBlockTimestampUtcOffset = + TimeDelta::Micros(123456789); + const Timestamp kReportBlockTimestampUtc = + time_controller_.GetClock()->CurrentTime() + + kReportBlockTimestampUtcOffset; const Timestamp kReportBlockTimestamp = Timestamp::Micros(12345678); const uint8_t kFractionLost = 12; const TimeDelta kRoundTripTimeSample1 = TimeDelta::Millis(1'234); @@ -3376,7 +3526,7 @@ TEST_P(RTCStatsCollectorTestWithParamKind, // The report block's timestamp cannot be from the future, set the fake clock // to match. - fake_clock_.SetTime(kReportBlockTimestampUtc); + time_controller_.AdvanceTime(kReportBlockTimestampUtcOffset); uint32_t ssrcs[] = {12, 13}; std::vector report_block_datas; Call::Stats call_stats; @@ -3407,12 +3557,15 @@ TEST_P(RTCStatsCollectorTestWithParamKind, std::nullopt); pc_->SetCallStats(call_stats); + const Timestamp expected_timestamp = std::get<1>(GetParam()) + ? kReportBlockTimestamp + : kReportBlockTimestampUtc; scoped_refptr report = stats_->GetStatsReport(main_thread_); for (uint32_t ssrc : ssrcs) { std::string stream_id = "" + std::to_string(ssrc); RTCRemoteInboundRtpStreamStats expected_remote_inbound_rtp( - "RI" + MediaTypeCharStr() + stream_id, kReportBlockTimestampUtc); + "RI" + MediaTypeCharStr() + stream_id, expected_timestamp); expected_remote_inbound_rtp.ssrc = ssrc; expected_remote_inbound_rtp.fraction_lost = static_cast(kFractionLost) / (1 << 8); @@ -3437,9 +3590,11 @@ TEST_P(RTCStatsCollectorTestWithParamKind, // expected to be missing. These are tested separately. ASSERT_TRUE(report->Get(expected_remote_inbound_rtp.id())); - EXPECT_EQ(report->Get(expected_remote_inbound_rtp.id()) - ->cast_to(), - expected_remote_inbound_rtp); + const auto& remote_inbound_rtp = + report->Get(expected_remote_inbound_rtp.id()) + ->cast_to(); + EXPECT_EQ(remote_inbound_rtp, expected_remote_inbound_rtp); + EXPECT_EQ(remote_inbound_rtp.timestamp(), expected_timestamp); EXPECT_TRUE(report->Get(*expected_remote_inbound_rtp.transport_id)); ASSERT_TRUE(report->Get(*expected_remote_inbound_rtp.local_id)); // Lookup works in both directions. @@ -3452,7 +3607,7 @@ TEST_P(RTCStatsCollectorTestWithParamKind, TEST_P(RTCStatsCollectorTestWithParamKind, RTCRemoteInboundRtpStreamStatsRttMissingBeforeMeasurement) { - constexpr Timestamp kReportBlockTimestampUtc = Timestamp::Micros(123456789); + const Timestamp kReportBlockTimestampUtc = Timestamp::Micros(123456789); const Timestamp kReportBlockTimestamp = Timestamp::Micros(12345678); rtcp::ReportBlock report_block; @@ -3483,7 +3638,8 @@ TEST_P(RTCStatsCollectorTestWithParamKind, RTCRemoteInboundRtpStreamStatsWithTimestampFromReportBlock) { const Timestamp kReportBlockTimestampUtc = Timestamp::Micros(123456789); const Timestamp kReportBlockTimestamp = Timestamp::Micros(12345678); - fake_clock_.SetTime(kReportBlockTimestampUtc); + time_controller_.AdvanceTime(kReportBlockTimestampUtc - + time_controller_.GetClock()->CurrentTime()); rtcp::ReportBlock report_block; // The remote-inbound-rtp SSRC and the outbound-rtp SSRC is the same as the @@ -3497,7 +3653,7 @@ TEST_P(RTCStatsCollectorTestWithParamKind, std::nullopt); // Advance time, it should be OK to have fresher reports than report blocks. - fake_clock_.AdvanceTime(TimeDelta::Micros(1234)); + time_controller_.AdvanceTime(TimeDelta::Micros(1234)); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -3511,14 +3667,19 @@ TEST_P(RTCStatsCollectorTestWithParamKind, // is of the time that the report block was received. EXPECT_EQ(report->timestamp(), kReportBlockTimestampUtc + TimeDelta::Micros(1234)); - EXPECT_EQ(remote_inbound_rtp.timestamp(), kReportBlockTimestampUtc); + if (std::get<1>(GetParam())) { + EXPECT_EQ(remote_inbound_rtp.timestamp(), kReportBlockTimestamp); + } else { + EXPECT_EQ(remote_inbound_rtp.timestamp(), kReportBlockTimestampUtc); + } } TEST_P(RTCStatsCollectorTestWithParamKind, RTCRemoteInboundRtpStreamStatsWithCodecBasedMembers) { const Timestamp kReportBlockTimestampUtc = Timestamp::Micros(123456789); const Timestamp kReportBlockTimestamp = Timestamp::Micros(12345678); - fake_clock_.SetTime(kReportBlockTimestampUtc); + time_controller_.AdvanceTime(kReportBlockTimestampUtc - + time_controller_.GetClock()->CurrentTime()); rtcp::ReportBlock report_block; // The remote-inbound-rtp SSRC and the outbound-rtp SSRC is the same as the @@ -3557,7 +3718,8 @@ TEST_P(RTCStatsCollectorTestWithParamKind, RTCRemoteInboundRtpStreamStatsWithRtcpTransport) { const Timestamp kReportBlockTimestampUtc = Timestamp::Micros(123456789); const Timestamp kReportBlockTimestamp = Timestamp::Micros(12345678); - fake_clock_.SetTime(kReportBlockTimestampUtc); + time_controller_.AdvanceTime(kReportBlockTimestampUtc - + time_controller_.GetClock()->CurrentTime()); rtcp::ReportBlock report_block; // The remote-inbound-rtp SSRC and the outbound-rtp SSRC is the same as the @@ -3592,14 +3754,17 @@ TEST_P(RTCStatsCollectorTestWithParamKind, EXPECT_TRUE(report->Get(*remote_inbound_rtp.transport_id)); } +INSTANTIATE_TEST_SUITE_P(All, RTCStatsCollectorTest, ::testing::Bool()); + INSTANTIATE_TEST_SUITE_P(All, RTCStatsCollectorTestWithParamKind, - ::testing::Values(MediaType::AUDIO, // "/0" - MediaType::VIDEO)); // "/1" + ::testing::Combine(::testing::Values(MediaType::AUDIO, + MediaType::VIDEO), + ::testing::Bool())); // Checks that no remote outbound stats are collected if not available in // `VoiceMediaInfo`. -TEST_F(RTCStatsCollectorTest, +TEST_P(RTCStatsCollectorTest, RTCRemoteOutboundRtpAudioStreamStatsNotCollected) { ExampleStatsGraph graph = SetupExampleStatsVoiceGraph(/*add_remote_outbound_stats=*/false); @@ -3618,7 +3783,7 @@ TEST_F(RTCStatsCollectorTest, // Checks that the remote outbound stats are collected when available in // `VoiceMediaInfo`. -TEST_F(RTCStatsCollectorTest, RTCRemoteOutboundRtpAudioStreamStatsCollected) { +TEST_P(RTCStatsCollectorTest, RTCRemoteOutboundRtpAudioStreamStatsCollected) { ExampleStatsGraph graph = SetupExampleStatsVoiceGraph(/*add_remote_outbound_stats=*/true); ASSERT_TRUE(graph.full_report->Get(graph.remote_outbound_rtp_id)); @@ -3634,7 +3799,7 @@ TEST_F(RTCStatsCollectorTest, RTCRemoteOutboundRtpAudioStreamStatsCollected) { kRemoteOutboundStatsReportsCount); } -TEST_F(RTCStatsCollectorTest, +TEST_P(RTCStatsCollectorTest, RTCVideoSourceStatsNotCollectedForSenderWithoutTrack) { const uint32_t kSsrc = 4; const int kAttachmentId = 42; @@ -3644,7 +3809,9 @@ TEST_F(RTCStatsCollectorTest, video_media_info.senders[0].local_stats.push_back(SsrcSenderInfo()); video_media_info.senders[0].local_stats[0].ssrc = kSsrc; video_media_info.senders[0].framerate_input = 29.0; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddVideoChannel("VideoMid", "TransportName", video_media_info); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr sender = CreateMockSender( MediaType::VIDEO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); @@ -3653,7 +3820,9 @@ TEST_F(RTCStatsCollectorTest, }); EXPECT_CALL(*sender, SetMediaChannel(_)).WillRepeatedly(Return()); EXPECT_CALL(*sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -3662,7 +3831,7 @@ TEST_F(RTCStatsCollectorTest, // Test collecting echo return loss stats from the audio processor attached to // the track, rather than the voice sender info. -TEST_F(RTCStatsCollectorTest, CollectEchoReturnLossFromTrackAudioProcessor) { +TEST_P(RTCStatsCollectorTest, CollectEchoReturnLossFromTrackAudioProcessor) { scoped_refptr local_stream = MediaStream::Create("LocalStreamId"); pc_->mutable_local_streams()->AddStream(local_stream); @@ -3699,7 +3868,7 @@ TEST_F(RTCStatsCollectorTest, CollectEchoReturnLossFromTrackAudioProcessor) { expected_audio); } -TEST_F(RTCStatsCollectorTest, GetStatsWithSenderSelector) { +TEST_P(RTCStatsCollectorTest, GetStatsWithSenderSelector) { ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests(); // Expected stats graph when filtered by sender: // @@ -3727,7 +3896,7 @@ TEST_F(RTCStatsCollectorTest, GetStatsWithSenderSelector) { EXPECT_TRUE(sender_report->Get(graph.media_source_id)); } -TEST_F(RTCStatsCollectorTest, GetStatsWithReceiverSelector) { +TEST_P(RTCStatsCollectorTest, GetStatsWithReceiverSelector) { ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests(); // Expected stats graph when filtered by receiver: // @@ -3754,7 +3923,53 @@ TEST_F(RTCStatsCollectorTest, GetStatsWithReceiverSelector) { EXPECT_FALSE(receiver_report->Get(graph.media_source_id)); } -TEST_F(RTCStatsCollectorTest, GetStatsWithNullSenderSelector) { +TEST_P(RTCStatsCollectorTest, + GetStatsDoesNotBlockWhenFetchingSenderParameters) { + // We must create a new FakePeerConnectionForStats with a separate worker + // thread because the test base uses the current thread for all three, which + // bypasses the thread hop and avoids the + // RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS check. + auto worker_thread = Thread::Create(); + worker_thread->Start(); + pc_ = make_ref_counted(env_, worker_thread.get()); + SetStatsTimestampWithEnvironmentClock(GetParam()); + // Create a track and a real sender to ensure we test the actual blocking + // behavior in RtpSenderBase::GetParametersInternal. + scoped_refptr track = CreateFakeTrack( + MediaType::AUDIO, "audioTrack", MediaStreamTrackInterface::kLive); + + auto fake_media_channel = std::make_unique( + webrtc::AudioOptions(), pc_->network_thread()); + + scoped_refptr sender = AudioRtpSender::Create( + env_, pc_->signaling_thread(), pc_->worker_thread(), "sender_id", + /*stats=*/nullptr, /*set_streams_observer=*/nullptr, + /*media_channel=*/nullptr); + + worker_thread->BlockingCall([&] { + fake_media_channel->AddSendStream(StreamParams::CreateLegacy(1234)); + sender->SetMediaChannel(fake_media_channel.get()); + }); + + sender->SetSsrc(1234); + sender->SetTrack(track.get()); + + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); + pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); + + scoped_refptr report; + stats_->stats_collector().GetStatsReport( + RTCStatsObtainer::Create(&report, [this] { main_thread_.Quit(); })); + main_thread_.Run(); + + sender->Stop(); + sender = nullptr; + stats_.reset(); + pc_ = nullptr; +} + +TEST_P(RTCStatsCollectorTest, GetStatsWithNullSenderSelector) { ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests(); scoped_refptr empty_report; stats_->stats_collector().GetStatsReport( @@ -3766,7 +3981,7 @@ TEST_F(RTCStatsCollectorTest, GetStatsWithNullSenderSelector) { EXPECT_EQ(empty_report->size(), 0u); } -TEST_F(RTCStatsCollectorTest, GetStatsWithNullReceiverSelector) { +TEST_P(RTCStatsCollectorTest, GetStatsWithNullReceiverSelector) { ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests(); scoped_refptr empty_report; stats_->stats_collector().GetStatsReport( @@ -3780,7 +3995,7 @@ TEST_F(RTCStatsCollectorTest, GetStatsWithNullReceiverSelector) { // Before SetLocalDescription() senders don't have an SSRC. // To simulate this case we create a mock sender with SSRC=0. -TEST_F(RTCStatsCollectorTest, RtpIsMissingWhileSsrcIsZero) { +TEST_P(RTCStatsCollectorTest, RtpIsMissingWhileSsrcIsZero) { scoped_refptr track = CreateFakeTrack( MediaType::AUDIO, "audioTrack", MediaStreamTrackInterface::kLive); scoped_refptr sender = @@ -3789,7 +4004,9 @@ TEST_F(RTCStatsCollectorTest, RtpIsMissingWhileSsrcIsZero) { return nullptr; }); EXPECT_CALL(*sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); scoped_refptr report = stats_->GetStatsReport(main_thread_); @@ -3800,7 +4017,7 @@ TEST_F(RTCStatsCollectorTest, RtpIsMissingWhileSsrcIsZero) { // We may also be in a case where the SSRC has been assigned but no // `voice_sender_info` stats exist yet. -TEST_F(RTCStatsCollectorTest, DoNotCrashIfSsrcIsKnownButInfosAreStillMissing) { +TEST_P(RTCStatsCollectorTest, DoNotCrashIfSsrcIsKnownButInfosAreStillMissing) { scoped_refptr track = CreateFakeTrack( MediaType::AUDIO, "audioTrack", MediaStreamTrackInterface::kLive); scoped_refptr sender = @@ -3809,7 +4026,9 @@ TEST_F(RTCStatsCollectorTest, DoNotCrashIfSsrcIsKnownButInfosAreStillMissing) { return nullptr; }); EXPECT_CALL(*sender, SetSendCodecs(_)); + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); pc_->AddSender(sender); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); // We do not generate any matching voice_sender_info stats. scoped_refptr report = @@ -3845,7 +4064,7 @@ class RecursiveCallback : public RTCStatsCollectorCallback { // Test that nothing bad happens if a callback causes GetStatsReport to be // called again recursively. Regression test for crbug.com/webrtc/8973. -TEST_F(RTCStatsCollectorTest, DoNotCrashWhenGetStatsCalledDuringCallback) { +TEST_P(RTCStatsCollectorTest, DoNotCrashWhenGetStatsCalledDuringCallback) { int remaining = 2; auto on_complete = [&] { if (--remaining == 0) @@ -3927,13 +4146,9 @@ class FakeRTCStatsCollector final : public RTCStatsCollector { if (!delivered_report_) return false; EXPECT_EQ(produced_on_signaling_thread_, 1); - EXPECT_EQ(produced_on_network_thread_, 1); - EXPECT_TRUE(delivered_report_->Get("SignalingThreadStats")); - EXPECT_TRUE(delivered_report_->Get("NetworkThreadStats")); produced_on_signaling_thread_ = 0; - produced_on_network_thread_ = 0; delivered_report_ = nullptr; return true; } @@ -3941,6 +4156,9 @@ class FakeRTCStatsCollector final : public RTCStatsCollector { protected: void ProducePartialResultsOnSignalingThreadImpl( Timestamp timestamp, + const std::vector& transceiver_stats_infos, + const std::vector& transceiver_references, + const std::optional& audio_device_stats, RTCStatsReport* partial_report) override { EXPECT_TRUE(signaling_thread_->IsCurrent()); { @@ -3952,22 +4170,6 @@ class FakeRTCStatsCollector final : public RTCStatsCollector { partial_report->AddStats(std::unique_ptr( new RTCTestStats("SignalingThreadStats", timestamp))); } - void ProducePartialResultsOnNetworkThreadImpl( - Timestamp timestamp, - const std::map& transport_stats_by_name, - const std::map& transport_cert_stats, - const std::vector& transceiver_stats_infos, - RTCStatsReport* partial_report) override { - EXPECT_TRUE(network_thread_->IsCurrent()); - { - MutexLock lock(&lock_); - EXPECT_FALSE(delivered_report_); - ++produced_on_network_thread_; - } - - partial_report->AddStats(std::unique_ptr( - new RTCTestStats("NetworkThreadStats", timestamp))); - } private: Thread* const signaling_thread_; @@ -3978,7 +4180,6 @@ class FakeRTCStatsCollector final : public RTCStatsCollector { Mutex lock_; scoped_refptr delivered_report_; int produced_on_signaling_thread_ = 0; - int produced_on_network_thread_ = 0; }; // Simple test that verifies that GetStatsReport() can be called and async @@ -3990,8 +4191,8 @@ class FakeRTCStatsCollector final : public RTCStatsCollector { // * Issue the `OnStatsDelivered` callback on the signaling thread. TEST(RTCStatsCollectorSafetyTest, WaitPendingRequestGetsCallback) { RunLoop loop; - auto pc = make_ref_counted(); - auto env = CreateEnvironment(); + auto env = CreateTestEnvironment(); + auto pc = make_ref_counted(env); RTCStatsCollectorWrapper wrapper(pc, env); auto callback = make_ref_counted(); EXPECT_CALL(*callback, OnStatsDelivered(_)).WillOnce([&] { loop.Quit(); }); @@ -4006,23 +4207,52 @@ TEST(RTCStatsCollectorSafetyTest, WaitPendingRequestGetsCallback) { // * Task posted from signaling to network thread. // * Task posted from network thread back to signaling // * The task for the signaling thread will be dropped, no callback. -TEST(RTCStatsCollectorSafetyTest, CancelPendingRequestPreventsCallback) { +TEST(RTCStatsCollectorSafetyTest, CancelPendingRequestReturnsImmediately) { RunLoop loop; - auto pc = make_ref_counted(); - RTCStatsCollectorWrapper wrapper(pc, CreateEnvironment()); + std::unique_ptr worker_and_network = Thread::Create(); + worker_and_network->Start(); + + auto env = CreateTestEnvironment(); + auto pc = make_ref_counted( + env, worker_and_network.get(), worker_and_network.get()); + RTCStatsCollectorWrapper wrapper(pc, env); auto callback = make_ref_counted(); + + // The callback that should not be called. EXPECT_CALL(*callback, OnStatsDelivered(_)).Times(0); - // At this point, cancellation has not been made, this posts a task to the - // network thread. + + // Let's pause the network/worker threads. + Event blocker; + worker_and_network->PostTask([&]() { blocker.Wait(Event::kForever); }); + + // Issue the GetStats call. At this point, cancellation has not been made, + // this posts a task to the worker/network threads which will be blocked by + // the above task. wrapper.stats_collector().GetStatsReport(callback); - // Now cancel any ongoing stats gathering operations. This should have the - // effect that the gathering that is ongoing on the network thread, will queue - // up a task for the signaling thread, but that task will be dropped. - auto network_task = - wrapper.stats_collector().CancelPendingRequestAndGetShutdownTask(); - loop.Flush(); - // Run the network cleanup task for posterity. - std::move(network_task)(); + std::vector> network_tasks; + std::vector> worker_tasks; + + // Now cancel any ongoing stats gathering operations. + // This should cancel the outstanding operations and invoke pending callbacks + // immediately. + wrapper.stats_collector().CancelPendingRequestAndGetShutdownTasks( + network_tasks, worker_tasks); + + // We should have one callback per conceptual thread. + EXPECT_EQ(network_tasks.size(), 1u); + EXPECT_EQ(worker_tasks.size(), 1u); + + // Resume the network and worker threads. + blocker.Set(); + + // Run the cleanup tasks. + auto quit = loop.QuitClosure(); + worker_and_network->PostTask([&]() { + std::move(network_tasks[0])(); + std::move(worker_tasks[0])(); + loop.task_queue()->PostTask([&]() { quit(); }); + }); + loop.Run(); } // This covers the following steps: @@ -4032,17 +4262,23 @@ TEST(RTCStatsCollectorSafetyTest, CancelPendingRequestPreventsCallback) { // * The task for the network thread will be dropped, no further work done. TEST(RTCStatsCollectorSafetyTest, NetworkThreadSafetyPreventsCallback) { RunLoop loop; - auto pc = make_ref_counted(); - RTCStatsCollectorWrapper wrapper(pc, CreateEnvironment()); + auto env = CreateTestEnvironment(); + auto pc = make_ref_counted(env); + RTCStatsCollectorWrapper wrapper(pc, env); auto callback = make_ref_counted(); EXPECT_CALL(*callback, OnStatsDelivered(_)).Times(0); // Start by canceling any ongoing tasks. There aren't actually any ongoing // tasks, but this gives us the network cleanup task. - auto network_task = - wrapper.stats_collector().CancelPendingRequestAndGetShutdownTask(); + std::vector> network_tasks; + std::vector> worker_tasks; + wrapper.stats_collector().CancelPendingRequestAndGetShutdownTasks( + network_tasks, worker_tasks); // Clean up the state on the network thread. This will have the effect of // dropping any tasks targeting the network thread. - std::move(network_task)(); + ASSERT_EQ(network_tasks.size(), 1u); + ASSERT_EQ(worker_tasks.size(), 1u); + std::move(network_tasks[0])(); + std::move(worker_tasks[0])(); // Now, attempt to get a stats report. This will try to post a task to the // network thread, which will be dropped. wrapper.stats_collector().GetStatsReport(callback); @@ -4050,28 +4286,16 @@ TEST(RTCStatsCollectorSafetyTest, NetworkThreadSafetyPreventsCallback) { loop.Flush(); } -class RTCStatsCollectorTestWithFakeCollector : public ::testing::Test { - public: - RTCStatsCollectorTestWithFakeCollector() - : pc_(make_ref_counted()) { - stats_collector_ = - FakeRTCStatsCollector::Create(pc_.get(), CreateEnvironment()); - } - - void VerifyThreadUsageAndResultsMerging() { - stats_collector_->GetStatsReport(stats_collector_->callback()); - time_controller_.AdvanceTime(TimeDelta::Zero()); - EXPECT_TRUE(stats_collector_->HasVerifiedResults()); - } - - RunLoop main_thread_; - scoped_refptr pc_; - std::unique_ptr stats_collector_; - ScopedFakeClock time_controller_; -}; - -TEST_F(RTCStatsCollectorTestWithFakeCollector, ThreadUsageAndResultsMerging) { - VerifyThreadUsageAndResultsMerging(); +TEST(RTCStatsCollectorTestWithFakeCollector, ThreadUsageAndResultsMerging) { + GlobalSimulatedTimeController time_controller{Timestamp::Seconds(1234)}; + Environment env = CreateTestEnvironment({.time = &time_controller}); + scoped_refptr pc = + make_ref_counted(env); + std::unique_ptr stats_collector = + FakeRTCStatsCollector::Create(pc.get(), env); + stats_collector->GetStatsReport(stats_collector->callback()); + time_controller.AdvanceTime(TimeDelta::Zero()); + EXPECT_TRUE(stats_collector->HasVerifiedResults()); } } // namespace diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index ffa035c50ce..a8a8b4e1582 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -48,6 +48,7 @@ #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" using ::testing::Contains; @@ -58,7 +59,7 @@ namespace webrtc { namespace { -constexpr int64_t kGetStatsTimeoutMs = 10000; +constexpr int64_t kGetStatsTimeoutMs = 20000; class RTCStatsIntegrationTest : public ::testing::Test { public: @@ -135,33 +136,30 @@ class RTCStatsIntegrationTest : public ::testing::Test { } protected: - static scoped_refptr GetStats( - PeerConnectionInterface* pc) { - scoped_refptr stats_obtainer = RTCStatsObtainer::Create(); + scoped_refptr GetStats(PeerConnectionInterface* pc) { + scoped_refptr stats_obtainer = + RTCStatsObtainer::Create(nullptr, [this]() { run_loop_.Quit(); }); pc->GetStats(stats_obtainer.get()); - EXPECT_THAT( - WaitUntil([&] { return stats_obtainer->report() != nullptr; }, IsTrue(), - {.timeout = TimeDelta::Millis(kGetStatsTimeoutMs)}), - IsRtcOk()); + run_loop_.RunFor(TimeDelta::Millis(kGetStatsTimeoutMs)); + EXPECT_TRUE(stats_obtainer->report()); return stats_obtainer->report(); } template - static scoped_refptr GetStats( - PeerConnectionInterface* pc, - scoped_refptr selector) { - scoped_refptr stats_obtainer = RTCStatsObtainer::Create(); + scoped_refptr GetStats(PeerConnectionInterface* pc, + scoped_refptr selector) { + scoped_refptr stats_obtainer = + RTCStatsObtainer::Create(nullptr, [this]() { run_loop_.Quit(); }); pc->GetStats(selector, stats_obtainer); - EXPECT_THAT( - WaitUntil([&] { return stats_obtainer->report() != nullptr; }, IsTrue(), - {.timeout = TimeDelta::Millis(kGetStatsTimeoutMs)}), - IsRtcOk()); + run_loop_.RunFor(TimeDelta::Millis(kGetStatsTimeoutMs)); + EXPECT_TRUE(stats_obtainer->report()); return stats_obtainer->report(); } + test::RunLoop run_loop_; + const Environment env_; // `network_thread_` uses `virtual_socket_server_` so they must be // constructed/destructed in the correct order. - const Environment env_; VirtualSocketServer virtual_socket_server_; std::unique_ptr network_thread_; std::unique_ptr worker_thread_; @@ -1170,10 +1168,11 @@ TEST_F(RTCStatsIntegrationTest, TEST_F(RTCStatsIntegrationTest, GetsStatsWhileClosingPeerConnection) { StartCall(); - scoped_refptr stats_obtainer = RTCStatsObtainer::Create(); + scoped_refptr stats_obtainer = + RTCStatsObtainer::Create(nullptr, [&]() { run_loop_.Quit(); }); caller_->pc()->GetStats(stats_obtainer.get()); caller_->pc()->Close(); - + run_loop_.Run(); ASSERT_TRUE(stats_obtainer->report()); } diff --git a/pc/rtp_parameters_conversion.cc b/pc/rtp_parameters_conversion.cc index 63d50d23ccf..ca19b217b87 100644 --- a/pc/rtp_parameters_conversion.cc +++ b/pc/rtp_parameters_conversion.cc @@ -81,7 +81,7 @@ RtpCodecCapability ToRtpCodecCapability(const Codec& cricket_codec) { codec.kind = cricket_codec.type == Codec::Type::kAudio ? MediaType::AUDIO : MediaType::VIDEO; codec.clock_rate.emplace(cricket_codec.clockrate); - codec.preferred_payload_type.emplace(cricket_codec.id); + codec.preferred_payload_type.emplace(cricket_codec.id.value()); for (const FeedbackParam& cricket_feedback : cricket_codec.feedback_params.params()) { std::optional feedback = ToRtcpFeedback(cricket_feedback); diff --git a/pc/rtp_receiver.cc b/pc/rtp_receiver.cc index 31a20df17ab..33dddb872dd 100644 --- a/pc/rtp_receiver.cc +++ b/pc/rtp_receiver.cc @@ -17,7 +17,11 @@ #include #include "api/media_stream_interface.h" +#include "api/rtc_error.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "pc/media_stream.h" #include "pc/media_stream_proxy.h" #include "rtc_base/thread.h" @@ -43,4 +47,16 @@ RtpReceiverInternal::CreateStreamsFromIds(std::vector stream_ids) { return streams; } +RtpReceiverBase::RtpReceiverBase(Thread* worker_thread) + : worker_thread_(worker_thread) {} + +RTCErrorOr> +RtpReceiverBase::CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) { + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + // TODO(bugs.webrtc.org/479862368): Create the internal Sframe decryption + // pipeline and return a key management handle. + return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Sframe decrypter not yet implemented"); +} + } // namespace webrtc diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h index 67720167395..6a2b21dc332 100644 --- a/pc/rtp_receiver.h +++ b/pc/rtp_receiver.h @@ -21,11 +21,18 @@ #include #include +#include "absl/functional/any_invocable.h" #include "api/dtls_transport_interface.h" #include "api/media_stream_interface.h" +#include "api/rtc_error.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "media/base/media_channel.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread.h" namespace webrtc { @@ -48,11 +55,19 @@ class RtpReceiverInternal : public RtpReceiverInterface { // Configures the RtpReceiver with the underlying media channel, with the // given SSRC as the stream identifier. - virtual void SetupMediaChannel(uint32_t ssrc) = 0; + // This function returns a callable object that must be invoked on the worker + // thread by the caller to complete the operation. This is done to allow the + // caller to batch up tasks for the worker thread. + [[nodiscard]] virtual absl::AnyInvocable GetSetupForMediaChannel( + uint32_t ssrc) = 0; // Configures the RtpReceiver with the underlying media channel to receive an // unsignaled receive stream. - virtual void SetupUnsignaledMediaChannel() = 0; + // This function returns a callable object that must be invoked on the worker + // thread by the caller to complete the operation. This is done to allow the + // caller to batch up tasks for the worker thread. + [[nodiscard]] virtual absl::AnyInvocable + GetSetupForUnsignaledMediaChannel() = 0; virtual void set_transport( scoped_refptr dtls_transport) = 0; @@ -62,9 +77,9 @@ class RtpReceiverInternal : public RtpReceiverInterface { // Call this to notify the RtpReceiver when the first packet has been received // on the corresponding channel. - virtual void NotifyFirstPacketReceived() = 0; + virtual void NotifyFirstPacketReceived(uint32_t ssrc) = 0; // Similar to the above but done whenever the receptive flag changed. - virtual void NotifyFirstPacketReceivedAfterReceptiveChange() = 0; + virtual void NotifyFirstPacketReceivedAfterReceptiveChange(uint32_t ssrc) = 0; // Set the associated remote media streams for this receiver. The remote track // will be removed from any streams that are no longer present and added to @@ -88,6 +103,18 @@ class RtpReceiverInternal : public RtpReceiverInterface { std::vector stream_ids); }; +class RtpReceiverBase : public RtpReceiverInternal { + public: + RTCErrorOr> + CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) override; + + protected: + explicit RtpReceiverBase(Thread* worker_thread); + + RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; + Thread* const worker_thread_; +}; + } // namespace webrtc #endif // PC_RTP_RECEIVER_H_ diff --git a/pc/rtp_receiver_proxy.h b/pc/rtp_receiver_proxy.h index 223b3070305..2022cbf0798 100644 --- a/pc/rtp_receiver_proxy.h +++ b/pc/rtp_receiver_proxy.h @@ -20,9 +20,12 @@ #include "api/frame_transformer_interface.h" #include "api/media_stream_interface.h" #include "api/media_types.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "api/transport/rtp/rtp_source.h" #include "pc/proxy.h" @@ -55,6 +58,9 @@ PROXY_SECONDARY_CONSTMETHOD0(scoped_refptr, PROXY_SECONDARY_METHOD1(void, SetFrameTransformer, scoped_refptr) +PROXY_METHOD1(RTCErrorOr>, + CreateSframeDecrypterOrError, + SframeCipherSuite) END_PROXY_MAP(RtpReceiver) } // namespace webrtc diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index f689ba13058..7af007cfa89 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -31,12 +31,14 @@ #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" +#include "api/media_types.h" #include "api/priority.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/video_codecs/video_encoder_factory.h" @@ -112,6 +114,61 @@ RtpParameters RestoreEncodingLayers( return result; } +// Checks that the codec parameters are valid. +RTCError CheckCodecParameters(const RtpParameters& parameters, + const std::vector& send_codecs, + const std::optional& send_codec) { + // Match the currently used codec against the codec preferences to gather + // the SVC capabilities. + std::optional send_codec_with_svc_info; + if (send_codec && send_codec->type == Codec::Type::kVideo) { + auto codec_match = absl::c_find_if( + send_codecs, [&](auto& codec) { return send_codec->Matches(codec); }); + if (codec_match != send_codecs.end()) { + send_codec_with_svc_info = *codec_match; + } + } + + return CheckScalabilityModeValues(parameters, send_codecs, + send_codec_with_svc_info); +} + +// Logic that runs on the worker thread to set the parameters. +// Returns an error if the parameters check failed or if the set failed. +void SetRtpParametersOnWorkerThread( + MediaSendChannelInterface* media_channel, + const std::vector& send_codecs, + const std::vector& disabled_rids, + const Environment& env, + uint32_t ssrc, + RtpParameters parameters, + SetParametersCallback callback) { + RTC_DCHECK(media_channel); + RtpParameters old_parameters = media_channel->GetRtpSendParameters(ssrc); + // Add the inactive layers if disabled_rids isn't empty. + RtpParameters rtp_parameters = + disabled_rids.empty() ? parameters + : RestoreEncodingLayers(parameters, disabled_rids, + old_parameters.encodings); + + RTCError result = CheckRtpParametersInvalidModificationAndValues( + old_parameters, rtp_parameters, env.field_trials()); + if (!result.ok()) { + std::move(callback)(std::move(result)); + return; + } + + result = CheckCodecParameters(rtp_parameters, send_codecs, + media_channel->GetSendCodec()); + if (!result.ok()) { + std::move(callback)(std::move(result)); + return; + } + + media_channel->SetRtpSendParameters(ssrc, rtp_parameters, + std::move(callback)); +} + } // namespace // Returns true if any RtpParameters member that isn't implemented contains a @@ -132,15 +189,18 @@ bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) { } RtpSenderBase::RtpSenderBase(const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, + MediaType media_type, SetStreamsObserver* set_streams_observer, MediaSendChannelInterface* media_channel) : env_(env), - signaling_thread_(Thread::Current()), + signaling_thread_(signaling_thread), worker_thread_(worker_thread), id_(id), - media_channel_(media_channel), + media_type_(media_type), + media_channel_(nullptr), // Will be set in SetMediaChannel(). set_streams_observer_(set_streams_observer), worker_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( /*alive=*/media_channel != nullptr, @@ -148,8 +208,21 @@ RtpSenderBase::RtpSenderBase(const Environment& env, signaling_safety_( PendingTaskSafetyFlag::CreateAttachedToTaskQueue(/*alive=*/true, signaling_thread_)) { - RTC_DCHECK(worker_thread); + RTC_DCHECK(worker_thread_); init_parameters_.encodings.emplace_back(); + if (media_channel) { + // When initialized with a valid media channel, we need to be running on the + // worker thread in order to set things up properly. + RTC_DCHECK_RUN_ON(worker_thread_); + SetMediaChannel(media_channel); + } else { + // Otherwise, we're less picky (but probably running on the signaling + // thread). + } +} + +RtpSenderBase::~RtpSenderBase() { + RTC_DCHECK(!media_channel_) << "Missing call to SetMediaChannel(nullptr)"; } void RtpSenderBase::SetFrameEncryptor( @@ -181,6 +254,7 @@ void RtpSenderBase::SetEncoderSelectorOnChannel() { if (stopped_ || ssrc_ == 0) { return; } + worker_thread_->BlockingCall([&, ssrc = ssrc_] { RTC_DCHECK_RUN_ON(worker_thread_); if (media_channel_) @@ -188,20 +262,53 @@ void RtpSenderBase::SetEncoderSelectorOnChannel() { }); } +void RtpSenderBase::SetCachedParameters( + std::optional parameters) { + RTC_DCHECK_RUN_ON(signaling_thread_); + if (parameters.has_value()) { + cached_parameters_ = std::move(*parameters); + } else { + cached_parameters_.reset(); + last_transaction_id_.reset(); + } +} + void RtpSenderBase::SetMediaChannel(MediaSendChannelInterface* media_channel) { RTC_DCHECK_RUN_ON(worker_thread_); - RTC_DCHECK(media_channel == nullptr || - media_channel->media_type() == media_type()); + RTC_DCHECK(!media_channel || media_channel->media_type() == media_type_); + if (media_channel_ == media_channel) { + return; + } + + if (media_channel_) { + media_channel_->SetParametersChangedCallback(nullptr); + } + // Note that setting the media_channel_ to nullptr and clearing the send state // via ClearSend_w, are separate operations. Stopping the actual send // operation, needs to be done via any of the paths that end up with a call to // ClearSend_w(), such as DetachTrackAndGetStopTask(). media_channel_ = media_channel; - media_channel_ != nullptr ? worker_safety_->SetAlive() - : worker_safety_->SetNotAlive(); + if (media_channel_) { + media_channel_->SetParametersChangedCallback( + [this] { OnParametersChanged(); }); + } + media_channel_ ? worker_safety_->SetAlive() : worker_safety_->SetNotAlive(); +} + +void RtpSenderBase::OnParametersChanged() { + RTC_DCHECK_RUN_ON(worker_thread_); + RTC_LOG(LS_INFO) << "RtpSender: OnParametersChanged signaled."; + signaling_thread_->PostTask(SafeTask(signaling_safety_.flag(), [this] { + RTC_DCHECK_RUN_ON(signaling_thread_); + cached_parameters_.reset(); + last_transaction_id_.reset(); + RTC_LOG(LS_INFO) << "RtpSender: OnParametersChanged cache cleared."; + })); } -RtpParameters RtpSenderBase::GetParametersInternal() const { +RtpParameters RtpSenderBase::GetParametersInternal(bool may_use_cache, + bool with_all_layers) const { RTC_DCHECK_RUN_ON(signaling_thread_); if (stopped_) { return RtpParameters(); @@ -209,36 +316,41 @@ RtpParameters RtpSenderBase::GetParametersInternal() const { if (ssrc_ == 0) { return init_parameters_; } - return worker_thread_->BlockingCall([&, ssrc = ssrc_] { - RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) { + + RtpParameters result; + if (may_use_cache && cached_parameters_) { + result = *cached_parameters_; + } else { + bool success = worker_thread_->BlockingCall([&, ssrc = ssrc_]() { + RTC_DCHECK_RUN_ON(worker_thread_); + if (!media_channel_) { + return false; + } + result = media_channel_->GetRtpSendParameters(ssrc); + return true; + }); + if (!success) { + cached_parameters_.reset(); return init_parameters_; } - RtpParameters result = media_channel_->GetRtpSendParameters(ssrc); + cached_parameters_ = result; + } + + if (!with_all_layers) { RemoveEncodingLayers(disabled_rids_, &result.encodings); - return result; - }); + } + return result; } RtpParameters RtpSenderBase::GetParametersInternalWithAllLayers() const { RTC_DCHECK_RUN_ON(signaling_thread_); - if (stopped_) { - return RtpParameters(); - } - if (ssrc_ == 0) { - return init_parameters_; - } - return worker_thread_->BlockingCall([&, ssrc = ssrc_] { - RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) { - return init_parameters_; - } - return media_channel_->GetRtpSendParameters(ssrc); - }); + return GetParametersInternal(/*may_use_cache=*/true, + /*with_all_layers=*/true); } RtpParameters RtpSenderBase::GetParameters() const { RTC_DCHECK_RUN_ON(signaling_thread_); +#if RTC_DCHECK_IS_ON // TODO(tommi): Here, we can use `last_transaction_id_` to allow for // multiple GetParameters() calls in a row return cached parameters // (we could still generate a new transaction_id every time). Since @@ -250,30 +362,48 @@ RtpParameters RtpSenderBase::GetParameters() const { // cache only at the GetParametersInternal() level that's used internally in // webrtc, e.g. for stats purposes, and use the cache only when // GetParametersInternal() is called directly and not via GetParameters(). - RtpParameters result = GetParametersInternal(); + // + // This `cached` variable and the `RTC_DCHECK` below are here temporarily + // to verify the correctness of the cache as the first implementation of it + // lands. Once we have confidence that the cache is reliably up to date, + // we can update GetParameters() to use the cache without having to thread + // hop. + auto cached = cached_parameters_; +#endif + + RtpParameters result = GetParametersInternal(/*may_use_cache=*/false, + /*with_all_layers=*/false); // Start a new transaction. `last_transaction_id_` will be reset whenever // the parameters change. last_transaction_id_ = CreateRandomUuid(); result.transaction_id = last_transaction_id_.value(); + +#if RTC_DCHECK_IS_ON + // The internal cache is only used when not stopped and ssrc_ is not 0. + // `cached_parameters_` might get reset if the media channel is gone. + if (cached && !stopped_ && ssrc_ != 0 && cached_parameters_) { + RtpParameters cached_filtered = *cached; + RemoveEncodingLayers(disabled_rids_, &cached_filtered.encodings); + if (cached_filtered != result) { + RTC_LOG(LS_ERROR) + << "Cached send params not equal to worker thread state.\n" + << "Cached: " << cached_filtered << "\n" + << "Result: " << result; + } + RTC_DCHECK(cached_filtered == result) + << "The cached value should have been equal (filtered)"; + } +#endif return result; } -void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters, - SetParametersCallback callback, - bool blocking) { - RTC_DCHECK_RUN_ON(signaling_thread_); - RTC_DCHECK(!stopped_); - +std::optional RtpSenderBase::ValidateAndMaybeUpdateInitParameters( + const RtpParameters& parameters) { if (UnimplementedRtpParameterHasValue(parameters)) { - RTCError error( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); - RTC_LOG(LS_ERROR) << error.message() << " (" << ToString(error.type()) - << ")"; - std::move(callback)(error); - return; + return LOG_ERROR(RTCError::UnsupportedParameter() + << "Attempted to set an unimplemented parameter of " + "RtpParameters."); } - if (ssrc_ == 0) { auto result = CheckRtpParametersInvalidModificationAndValues( init_parameters_, parameters, send_codecs_, std::nullopt, @@ -281,61 +411,147 @@ void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters, if (result.ok()) { init_parameters_ = parameters; } - std::move(callback)(result); - return; + return result; + } + return std::nullopt; +} + +RTCError RtpSenderBase::SetParametersInternalWorkaround( + const RtpParameters& parameters) { + RTC_DCHECK_RUN_ON(signaling_thread_); + RTCError error = RTCError::InvalidState(); + RtpParameters fetched_parameters; + worker_thread_->BlockingCall([&, ssrc = ssrc_]() { + RTC_DCHECK_RUN_ON(worker_thread_); + if (!media_channel_) + return; + Event done_event; + SetRtpParametersOnWorkerThread(media_channel_, send_codecs_, disabled_rids_, + env_, ssrc, parameters, [&](RTCError err) { + error = std::move(err); + done_event.Set(); + }); + done_event.Wait(Event::kForever); + if (error.ok()) { + fetched_parameters = media_channel_->GetRtpSendParameters(ssrc); + } + }); + if (error.ok()) { + init_parameters_ = fetched_parameters; + cached_parameters_ = std::move(fetched_parameters); + } + return error; +} + +RTCError RtpSenderBase::SetParametersInternal(const RtpParameters& parameters, + SetParametersCallback callback, + bool blocking) { + RTC_DCHECK_RUN_ON(signaling_thread_); + RTC_DCHECK(!stopped_); + RTC_DCHECK(!blocking || !callback) << "Callback must be null if blocking"; + + if (auto error = ValidateAndMaybeUpdateInitParameters(parameters)) { + if (callback) { + std::move(callback)(*error); + } + return *error; + } + + // Invalidate the cache to ensure that GetParameters() doesn't use a stale + // cache while the worker thread is updating the parameters. + cached_parameters_.reset(); + + if (blocking && worker_thread_ == signaling_thread_) { + return SetParametersInternalWorkaround(parameters); } - if (!blocking) { - // For an async operation, in order to still maintain the promise - // that the callback is safely invoked on the signaling thread, we - // add a callback layer that posts a task to the signaling thread. - callback = [signaling_thread = signaling_thread_, - signaling_safety = signaling_safety_.flag(), - callback = std::move(callback)](RTCError error) mutable { - signaling_thread->PostTask( - SafeTask(std::move(signaling_safety), - [callback = std::move(callback), error = std::move(error), - signaling_thread]() mutable { - RTC_DCHECK_RUN_ON(signaling_thread); - std::move(callback)(error); - })); + // Specific handling for when a blocking operation is requested. + Event done_event; + RTCError blocking_error = RTCError::OK(); + std::unique_ptr blocking_applied_parameters; + if (blocking) { + callback = [&done_event, &blocking_error](RTCError error) { + blocking_error = std::move(error); + done_event.Set(); }; } - auto task = [&, callback = std::move(callback), - parameters = std::move(parameters), ssrc = ssrc_]() mutable { + // A wrapper callback that fetches the parameters on the worker thread + // immediately after they have been set, then posts a task to the signaling + // thread to update the cache and invoke the original callback. + // This ensures strict ordering: Set -> Fetch -> Update Cache -> Callback. + // + // Note: The callback might be invoked on a thread other than the worker + // thread (e.g. the encoder queue). In that case, we must post a task back + // to the worker thread to safely access `media_channel_`. + auto callback_wrapper = + [this, blocking, &blocking_applied_parameters, + signaling_safety = signaling_safety_.flag(), + worker_safety_flag = worker_safety_, input_parameters = parameters, + callback = std::move(callback), ssrc = ssrc_](RTCError error) mutable { + auto on_worker_thread = [this, blocking, &blocking_applied_parameters, + signaling_safety = std::move(signaling_safety), + input_parameters = std::move(input_parameters), + callback = std::move(callback), ssrc, + error = std::move(error)]() mutable { + RTC_DCHECK_RUN_ON(worker_thread_); + std::unique_ptr fetched_parameters; + if (error.ok()) { + fetched_parameters = std::make_unique( + media_channel_->GetRtpSendParameters(ssrc)); + } + + if (blocking) { + blocking_applied_parameters = std::move(fetched_parameters); + std::move(callback)(std::move(error)); + } else { + signaling_thread_->PostTask(SafeTask( + std::move(signaling_safety), + [this, callback = std::move(callback), error = std::move(error), + fetched_parameters = std::move(fetched_parameters), + input_parameters = std::move(input_parameters)]() mutable { + RTC_DCHECK_RUN_ON(signaling_thread_); + if (error.ok()) { + init_parameters_ = std::move(input_parameters); + cached_parameters_ = *fetched_parameters; + } + std::move(callback)(std::move(error)); + })); + } + }; + + if (worker_thread_->IsCurrent()) { + on_worker_thread(); + } else { + worker_thread_->PostTask(SafeTask(std::move(worker_safety_flag), + std::move(on_worker_thread))); + } + }; + + auto task = [&, callback = std::move(callback_wrapper), + parameters = parameters, ssrc = ssrc_]() mutable { RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) { - std::move(callback)(RTCError(RTCErrorType::INVALID_STATE)); - return; - } - RtpParameters old_parameters = media_channel_->GetRtpSendParameters(ssrc); - // Add the inactive layers if disabled_rids_ isn't empty. - RtpParameters rtp_parameters = - disabled_rids_.empty() - ? parameters - : RestoreEncodingLayers(parameters, disabled_rids_, - old_parameters.encodings); - - RTCError result = CheckRtpParametersInvalidModificationAndValues( - old_parameters, rtp_parameters, env_.field_trials()); - if (!result.ok()) { - std::move(callback)(result); - return; + if (media_channel_) { + SetRtpParametersOnWorkerThread(media_channel_, send_codecs_, + disabled_rids_, env_, ssrc, parameters, + std::move(callback)); + } else { + std::move(callback)(RTCError::InvalidState()); } + }; - result = CheckCodecParameters(rtp_parameters); - if (!result.ok()) { - std::move(callback)(result); - return; + if (blocking) { + worker_thread_->BlockingCall(task); + done_event.Wait(Event::kForever); + if (blocking_error.ok()) { + cached_parameters_ = std::move(*blocking_applied_parameters); + init_parameters_ = *cached_parameters_; } + return blocking_error; + } - media_channel_->SetRtpSendParameters(ssrc, rtp_parameters, - std::move(callback)); - }; - blocking - ? worker_thread_->BlockingCall(task) - : worker_thread_->PostTask(SafeTask(worker_safety_, std::move(task))); + worker_thread_->PostTask(SafeTask(worker_safety_, std::move(task))); + return RTCError::OK(); } RTCError RtpSenderBase::SetParametersInternalWithAllLayers( @@ -343,70 +559,50 @@ RTCError RtpSenderBase::SetParametersInternalWithAllLayers( RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DCHECK(!stopped_); - if (UnimplementedRtpParameterHasValue(parameters)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); - } - if (ssrc_ == 0) { - auto result = CheckRtpParametersInvalidModificationAndValues( - init_parameters_, parameters, send_codecs_, std::nullopt, - env_.field_trials()); - if (result.ok()) { - init_parameters_ = parameters; - } - return result; + if (auto error = ValidateAndMaybeUpdateInitParameters(parameters)) { + return *error; } - return worker_thread_->BlockingCall([&, ssrc = ssrc_] { + RtpParameters applied_parameters; + RTCError error = worker_thread_->BlockingCall([&, ssrc = ssrc_] { RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) { - return RTCError(RTCErrorType::INVALID_STATE); + RTCError err = media_channel_ ? media_channel_->SetRtpSendParameters( + ssrc, parameters, nullptr) + : RTCError::InvalidState(); + if (err.ok()) { + applied_parameters = media_channel_->GetRtpSendParameters(ssrc); } - RtpParameters rtp_parameters = parameters; - return media_channel_->SetRtpSendParameters(ssrc, rtp_parameters, nullptr); + return err; }); + + if (error.ok()) { + cached_parameters_ = std::move(applied_parameters); + } + + return error; } RTCError RtpSenderBase::CheckSetParameters(const RtpParameters& parameters) { RTC_DCHECK_RUN_ON(signaling_thread_); if (stopped_) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "Cannot set parameters on a stopped sender."); + return LOG_ERROR(RTCError::InvalidState() + << "Cannot set parameters on a stopped sender."); } if (!last_transaction_id_) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_STATE, - "Failed to set parameters since getParameters() has never been called" - " on this sender"); + return LOG_ERROR(RTCError::InvalidState() + << "Failed to set parameters since getParameters() has " + "never been called" + " on this sender"); } if (last_transaction_id_ != parameters.transaction_id) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Failed to set parameters since the transaction_id doesn't match" - " the last value returned from getParameters()"); + return LOG_ERROR(RTCError::InvalidModification() + << "Failed to set parameters since the transaction_id " + "doesn't match" + " the last value returned from getParameters()"); } return RTCError::OK(); } -RTCError RtpSenderBase::CheckCodecParameters(const RtpParameters& parameters) { - std::optional send_codec = media_channel_->GetSendCodec(); - - // Match the currently used codec against the codec preferences to gather - // the SVC capabilities. - std::optional send_codec_with_svc_info; - if (send_codec && send_codec->type == Codec::Type::kVideo) { - auto codec_match = absl::c_find_if( - send_codecs_, [&](auto& codec) { return send_codec->Matches(codec); }); - if (codec_match != send_codecs_.end()) { - send_codec_with_svc_info = *codec_match; - } - } - - return CheckScalabilityModeValues(parameters, send_codecs_, - send_codec_with_svc_info); -} - RTCError RtpSenderBase::SetParameters(const RtpParameters& parameters) { RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "RtpSenderBase::SetParameters"); @@ -414,19 +610,7 @@ RTCError RtpSenderBase::SetParameters(const RtpParameters& parameters) { if (!result.ok()) return result; - // Some tests rely on working in single thread mode without a run loop and a - // blocking call is required to keep them working. The encoder configuration - // also involves another thread with an asynchronous task, thus we still do - // need to wait for the callback to be resolved this way. - std::unique_ptr done_event = std::make_unique(); - SetParametersInternal( - parameters, - [done = done_event.get(), &result](RTCError error) { - result = error; - done->Set(); - }, - true); - done_event->Wait(Event::kForever); + result = SetParametersInternal(parameters, nullptr, /*blocking=*/true); last_transaction_id_.reset(); return result; } @@ -449,7 +633,7 @@ void RtpSenderBase::SetParametersAsync(const RtpParameters& parameters, last_transaction_id_.reset(); std::move(callback)(error); }, - false); + /*blocking=*/false); } void RtpSenderBase::SetObserver(RtpSenderObserverInterface* observer) { @@ -487,6 +671,7 @@ void RtpSenderBase::SetStreams(const std::vector& stream_ids) { bool RtpSenderBase::SetTrack(MediaStreamTrackInterface* track) { RTC_DCHECK_RUN_ON(signaling_thread_); TRACE_EVENT0("webrtc", "RtpSenderBase::SetTrack"); + if (stopped_) { RTC_LOG(LS_ERROR) << "SetTrack can't be called on a stopped RtpSender."; return false; @@ -532,6 +717,9 @@ void RtpSenderBase::SetSsrc(uint32_t ssrc) { if (stopped_ || ssrc == ssrc_) { return; } + + cached_parameters_.reset(); + // If we are already sending with a particular SSRC, stop sending. if (can_send_track()) { ClearSend(); @@ -542,48 +730,63 @@ void RtpSenderBase::SetSsrc(uint32_t ssrc) { SetSend(); AddTrackToStats(); } - if (!init_parameters_.encodings.empty() || - init_parameters_.degradation_preference.has_value()) { - worker_thread_->BlockingCall([&, ssrc = ssrc_] { - RTC_DCHECK_RUN_ON(worker_thread_); + + const bool update_parameters = + (ssrc_ != 0 && (!init_parameters_.encodings.empty() || + init_parameters_.degradation_preference.has_value())); + RtpParameters current_parameters; + bool params_modified = false; + worker_thread_->BlockingCall([&, ssrc = ssrc_] { + RTC_DCHECK_RUN_ON(worker_thread_); + if (update_parameters) { RTC_DCHECK(media_channel_); - // Get the current parameters, which are constructed from the SDP. - // The number of layers in the SDP is currently authoritative to support - // SDP munging for Plan-B simulcast with "a=ssrc-group:SIM ..." - // lines as described in RFC 5576. - // All fields should be default constructed and the SSRC field set, which - // we need to copy. - RtpParameters current_parameters = - media_channel_->GetRtpSendParameters(ssrc); - // SSRC 0 has special meaning as "no stream". - // In this case, current_parameters may have size 0. - if (ssrc != 0) { - RTC_CHECK_GE(current_parameters.encodings.size(), - init_parameters_.encodings.size()); - for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) { - init_parameters_.encodings[i].ssrc = - current_parameters.encodings[i].ssrc; - init_parameters_.encodings[i].rid = - current_parameters.encodings[i].rid; - current_parameters.encodings[i] = init_parameters_.encodings[i]; - } - current_parameters.degradation_preference = - init_parameters_.degradation_preference; - media_channel_->SetRtpSendParameters(ssrc, current_parameters, nullptr); + // Get the current parameters, which are constructed from the SDP. The + // number of layers in the SDP is currently authoritative to support SDP + // munging for Plan-B simulcast with "a=ssrc-group:SIM ..." lines + // as described in RFC 5576. All fields should be default constructed and + // the SSRC field set, which we need to copy. + current_parameters = media_channel_->GetRtpSendParameters(ssrc); + // SSRC 0 has special meaning as "no stream". In this case, + // current_parameters may have size 0. + RTC_CHECK_GE(current_parameters.encodings.size(), + init_parameters_.encodings.size()); + for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) { + init_parameters_.encodings[i].ssrc = + current_parameters.encodings[i].ssrc; + init_parameters_.encodings[i].rid = current_parameters.encodings[i].rid; + current_parameters.encodings[i] = init_parameters_.encodings[i]; } - init_parameters_.encodings.clear(); - init_parameters_.degradation_preference = std::nullopt; - }); - } - // Attempt to attach the frame decryptor to the current media channel. - if (frame_encryptor_) { - SetFrameEncryptor(frame_encryptor_); - } - if (frame_transformer_) { - SetFrameTransformer(frame_transformer_); - } - if (encoder_selector_) { - SetEncoderSelectorOnChannel(); + current_parameters.degradation_preference = + init_parameters_.degradation_preference; + params_modified = + media_channel_ + ->SetRtpSendParameters(ssrc, current_parameters, nullptr) + .ok(); + if (params_modified) { + // The parameters may change as they're applied. + current_parameters = media_channel_->GetRtpSendParameters(ssrc); + } + } + + // While we're on the worker thread, attach the frame decryptor, transformer + // and selector to the current media channel. + if (frame_encryptor_) { + media_channel_->SetFrameEncryptor(ssrc, frame_encryptor_); + } + if (frame_transformer_) { + media_channel_->SetEncoderToPacketizerFrameTransformer( + ssrc, frame_transformer_); + } + if (encoder_selector_) { + media_channel_->SetEncoderSelector(ssrc, encoder_selector_.get()); + } + }); + if (params_modified) { + // As a result of the `SetRtpSendParameters` call, an async task will be + // queued to update `cached_parameters_` - unless the parameters didn't + // really change. In any case, we might as well stash away the current + // parameters right away. + cached_parameters_ = std::move(current_parameters); } } @@ -598,11 +801,22 @@ void RtpSenderBase::Stop() { DetachTrack(); track_->UnregisterObserver(this); } - if (can_send_track()) { - ClearSend(); + + bool clear_send = can_send_track(); + if (clear_send) { RemoveTrackFromStats(); } + + worker_thread_->BlockingCall([this, clear_send, ssrc = ssrc_] { + RTC_DCHECK_RUN_ON(worker_thread_); + if (clear_send) { + ClearSend_w(ssrc); + } + SetMediaChannel(nullptr); + }); + stopped_ = true; + cached_parameters_.reset(); } absl::AnyInvocable RtpSenderBase::DetachTrackAndGetStopTask() { @@ -617,17 +831,20 @@ absl::AnyInvocable RtpSenderBase::DetachTrackAndGetStopTask() { track_->UnregisterObserver(this); } - stopped_ = true; - - if (can_send_track()) { + bool clear_send = can_send_track(); + if (clear_send) { RemoveTrackFromStats(); - } else { - return nullptr; } - return [this, ssrc = ssrc_] { + stopped_ = true; + cached_parameters_.reset(); + + return [this, clear_send, ssrc = ssrc_] { RTC_DCHECK_RUN_ON(worker_thread_); - ClearSend_w(ssrc); + if (clear_send) { + ClearSend_w(ssrc); + } + SetMediaChannel(nullptr); }; } @@ -635,8 +852,8 @@ RTCError RtpSenderBase::DisableEncodingLayers( const std::vector& rids) { RTC_DCHECK_RUN_ON(signaling_thread_); if (stopped_) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "Cannot disable encodings on a stopped sender."); + return LOG_ERROR(RTCError::InvalidState() + << "Cannot disable encodings on a stopped sender."); } if (rids.empty()) { @@ -650,8 +867,9 @@ RTCError RtpSenderBase::DisableEncodingLayers( [&rid](const RtpEncodingParameters& encoding) { return encoding.rid == rid; })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "RID: " + rid + " does not refer to a valid layer."); + return LOG_ERROR(RTCError::InvalidParameter() + << "RID: " << rid + << " does not refer to a valid layer."); } } @@ -699,6 +917,15 @@ void RtpSenderBase::SetFrameTransformer( } } +RTCErrorOr> +RtpSenderBase::CreateSframeEncrypterOrError( + const SframeEncrypterInit& options) { + RTC_DCHECK_RUN_ON(signaling_thread_); + // TODO(bugs.webrtc.org/479862368): Implement Sframe encrypter creation. + return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Sframe encrypter not yet implemented"); +} + LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(nullptr) {} LocalAudioSinkAdapter::~LocalAudioSinkAdapter() { @@ -732,30 +959,35 @@ void LocalAudioSinkAdapter::SetSink(AudioSource::Sink* sink) { scoped_refptr AudioRtpSender::Create( const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, LegacyStatsCollectorInterface* stats, SetStreamsObserver* set_streams_observer, MediaSendChannelInterface* media_channel) { - return make_ref_counted(env, worker_thread, id, stats, - set_streams_observer, media_channel); + return make_ref_counted(env, signaling_thread, worker_thread, + id, stats, set_streams_observer, + media_channel); } AudioRtpSender::AudioRtpSender(const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, LegacyStatsCollectorInterface* stats, SetStreamsObserver* set_streams_observer, MediaSendChannelInterface* media_channel) : RtpSenderBase(env, + signaling_thread, worker_thread, id, + MediaType::AUDIO, set_streams_observer, media_channel), legacy_stats_(stats), - dtmf_sender_(DtmfSender::Create(Thread::Current(), this)), + dtmf_sender_(DtmfSender::Create(signaling_thread, this)), dtmf_sender_proxy_( - DtmfSenderProxy::Create(Thread::Current(), dtmf_sender_)), + DtmfSenderProxy::Create(signaling_thread, dtmf_sender_)), sink_adapter_(new LocalAudioSinkAdapter()) {} AudioRtpSender::~AudioRtpSender() { @@ -843,8 +1075,8 @@ RTCError AudioRtpSender::GenerateKeyFrame( const std::vector& rids) { RTC_DCHECK_RUN_ON(signaling_thread_); RTC_DLOG(LS_ERROR) << "Tried to get generate a key frame for audio."; - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Generating key frames for audio is not supported."); + return RTCError::UnsupportedOperation() + << "Generating key frames for audio is not supported."; } void AudioRtpSender::SetSend() { @@ -868,6 +1100,7 @@ void AudioRtpSender::SetSend() { // `track_->enabled()` hops to the signaling thread, so call it before we hop // to the worker thread or else it will deadlock. bool track_enabled = track_->enabled(); + InvalidateCache(); bool success = worker_thread_->BlockingCall([&, ssrc = ssrc_] { RTC_DCHECK_RUN_ON(worker_thread_); return media_channel_ @@ -899,22 +1132,27 @@ void AudioRtpSender::ClearSend_w(uint32_t ssrc) { scoped_refptr VideoRtpSender::Create( const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, MediaSendChannelInterface* media_channel) { - return make_ref_counted(env, worker_thread, id, - set_streams_observer, media_channel); + return make_ref_counted(env, signaling_thread, worker_thread, + id, set_streams_observer, + media_channel); } VideoRtpSender::VideoRtpSender(const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, MediaSendChannelInterface* media_channel) : RtpSenderBase(env, + signaling_thread, worker_thread, id, + MediaType::VIDEO, set_streams_observer, media_channel) {} @@ -960,15 +1198,15 @@ RTCError VideoRtpSender::GenerateKeyFrame( const auto parameters = GetParametersInternal(); for (const auto& rid : rids) { if (rid.empty()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Attempted to specify an empty rid."); + return LOG_ERROR(RTCError::InvalidParameter() + << "Attempted to specify an empty rid."); } if (!absl::c_any_of(parameters.encodings, [&rid](const RtpEncodingParameters& parameters) { return parameters.rid == rid; })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Attempted to specify a rid not configured."); + return LOG_ERROR(RTCError::InvalidParameter() + << "Attempted to specify a rid not configured."); } } worker_thread_->PostTask(SafeTask(worker_safety_, [this, rids, ssrc = ssrc_] { @@ -1004,6 +1242,7 @@ void VideoRtpSender::SetSend() { break; } auto* video_track = static_cast(track_.get()); + InvalidateCache(); bool success = worker_thread_->BlockingCall([&, ssrc = ssrc_] { RTC_DCHECK_RUN_ON(worker_thread_); return media_channel_ ? video_media_channel()->SetVideoSend(ssrc, &options, diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index 82a732239f5..eeec9ae1c80 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -37,6 +37,7 @@ #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/video_codecs/video_encoder_factory.h" @@ -85,10 +86,12 @@ class RtpSenderInternal : public RtpSenderInterface { // `GetParameters` and `SetParameters` operate with a transactional model. // Allow access to get/set parameters without invalidating transaction id. - virtual RtpParameters GetParametersInternal() const = 0; - virtual void SetParametersInternal(const RtpParameters& parameters, - SetParametersCallback, - bool blocking) = 0; + virtual RtpParameters GetParametersInternal(bool may_use_cache, + bool with_all_layers) const = 0; + virtual RTCError SetParametersInternal(const RtpParameters& parameters, + SetParametersCallback, + bool blocking) = 0; + virtual void SetCachedParameters(std::optional parameters) = 0; // GetParameters and SetParameters will remove deactivated simulcast layers // and restore them on SetParameters. This is probably a Bad Idea, but we @@ -124,6 +127,8 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { virtual void OnSetStreams() = 0; }; + ~RtpSenderBase() override; + // Sets the underlying MediaEngine channel associated with this RtpSender. // A VoiceMediaChannel should be used for audio RtpSenders and // a VideoMediaChannel should be used for video RtpSenders. @@ -136,6 +141,8 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { return track_; } + MediaType media_type() const final { return media_type_; } + RtpParameters GetParameters() const override; RTCError SetParameters(const RtpParameters& parameters) override; void SetParametersAsync(const RtpParameters& parameters, @@ -143,14 +150,19 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { // `GetParameters` and `SetParameters` operate with a transactional model. // Allow access to get/set parameters without invalidating transaction id. - RtpParameters GetParametersInternal() const override; - void SetParametersInternal(const RtpParameters& parameters, - SetParametersCallback callback = nullptr, - bool blocking = true) override; + RtpParameters GetParametersInternal( + bool may_use_cache = true, + bool with_all_layers = false) const override; + RTCError SetParametersInternal(const RtpParameters& parameters, + SetParametersCallback callback = nullptr, + bool blocking = true) override; + void SetCachedParameters(std::optional parameters) override; RTCError CheckSetParameters(const RtpParameters& parameters); RtpParameters GetParametersInternalWithAllLayers() const override; RTCError SetParametersInternalWithAllLayers( const RtpParameters& parameters) override; + std::optional ValidateAndMaybeUpdateInitParameters( + const RtpParameters& parameters) RTC_RUN_ON(signaling_thread_); // Used to set the SSRC of the sender, once a local description has been set. // If `ssrc` is 0, this indiates that the sender should disconnect from the @@ -213,6 +225,9 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { void SetFrameTransformer( scoped_refptr frame_transformer) override; + RTCErrorOr> + CreateSframeEncrypterOrError(const SframeEncrypterInit& options) override; + void SetEncoderSelector( std::unique_ptr encoder_selector) override; @@ -228,14 +243,25 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { void SetObserver(RtpSenderObserverInterface* observer) override; protected: + void InvalidateCache() { + RTC_DCHECK_RUN_ON(signaling_thread_); + cached_parameters_.reset(); + } + + // Called by the media channel when parameters change autonomously on the + // worker thread (e.g., encoder fallback). + void OnParametersChanged(); // If `set_streams_observer` is not null, it is invoked when SetStreams() // is called. `set_streams_observer` is not owned by this object. If not // null, it must be valid at least until this sender becomes stopped. RtpSenderBase(const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, + MediaType media_type, SetStreamsObserver* set_streams_observer, MediaSendChannelInterface* media_channel); + // TODO(bugs.webrtc.org/8694): Since SSRC == 0 is technically valid, figure // out some other way to test if we have a valid SSRC. bool can_send_track() const RTC_RUN_ON(signaling_thread_) { @@ -249,8 +275,6 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { // Disable sending on the media channel. virtual void ClearSend() = 0; virtual void ClearSend_w(uint32_t ssrc) RTC_RUN_ON(worker_thread_) = 0; - RTCError CheckCodecParameters(const RtpParameters& parameters) - RTC_RUN_ON(worker_thread_); // Template method pattern to allow subclasses to add custom behavior for // when tracks are attached, detached, and for adding tracks to statistics. @@ -259,6 +283,11 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { virtual void AddTrackToStats() RTC_RUN_ON(signaling_thread_) {} virtual void RemoveTrackFromStats() RTC_RUN_ON(signaling_thread_) {} + // Special case for downstream code that calls into this code with a + // configuration where the signaling, worker and network threads are all + // configured to be the same thread. + RTCError SetParametersInternalWorkaround(const RtpParameters& parameters); + const Environment env_; TaskQueueBase* const signaling_thread_; Thread* const worker_thread_; @@ -268,9 +297,12 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { bool stopped_ RTC_GUARDED_BY(signaling_thread_) = false; int attachment_id_ = 0; const std::string id_; + const MediaType media_type_; std::vector stream_ids_; RtpParameters init_parameters_; + mutable std::optional cached_parameters_ + RTC_GUARDED_BY(signaling_thread_); std::vector send_codecs_; // TODO(tommi): Several member variables in this class (ssrc_, stopped_, etc) @@ -361,6 +393,7 @@ class AudioRtpSender : public DtmfProviderInterface, public RtpSenderBase { // null, it must be valid at least until this sender becomes stopped. static scoped_refptr Create( const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, LegacyStatsCollectorInterface* stats, @@ -375,7 +408,6 @@ class AudioRtpSender : public DtmfProviderInterface, public RtpSenderBase { // ObserverInterface implementation. void OnChanged() override; - MediaType media_type() const override { return MediaType::AUDIO; } std::string track_kind() const override { return MediaStreamTrackInterface::kAudioKind; } @@ -385,6 +417,7 @@ class AudioRtpSender : public DtmfProviderInterface, public RtpSenderBase { protected: AudioRtpSender(const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, LegacyStatsCollectorInterface* legacy_stats, @@ -431,6 +464,7 @@ class VideoRtpSender : public RtpSenderBase { // null, it must be valid at least until this sender becomes stopped. static scoped_refptr Create( const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, @@ -440,7 +474,6 @@ class VideoRtpSender : public RtpSenderBase { // ObserverInterface implementation void OnChanged() override; - MediaType media_type() const override { return MediaType::VIDEO; } std::string track_kind() const override { return MediaStreamTrackInterface::kVideoKind; } @@ -450,6 +483,7 @@ class VideoRtpSender : public RtpSenderBase { protected: VideoRtpSender(const Environment& env, + Thread* signaling_thread, Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, diff --git a/pc/rtp_sender_proxy.h b/pc/rtp_sender_proxy.h index 8e609c5e43f..74d28b55ed3 100644 --- a/pc/rtp_sender_proxy.h +++ b/pc/rtp_sender_proxy.h @@ -26,6 +26,7 @@ #include "api/rtp_parameters.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/video_codecs/video_encoder_factory.h" #include "pc/proxy.h" @@ -58,6 +59,9 @@ PROXY_METHOD1(void, SetStreams, const std::vector&) PROXY_METHOD1(void, SetFrameTransformer, scoped_refptr) +PROXY_METHOD1(RTCErrorOr>, + CreateSframeEncrypterOrError, + const SframeEncrypterInit&) PROXY_METHOD1(void, SetEncoderSelector, std::unique_ptr) diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc index 2a310dea3d4..6598701cad4 100644 --- a/pc/rtp_sender_receiver_unittest.cc +++ b/pc/rtp_sender_receiver_unittest.cc @@ -18,6 +18,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/functional/any_invocable.h" #include "api/audio_options.h" #include "api/crypto/crypto_options.h" #include "api/crypto/frame_decryptor_interface.h" @@ -31,6 +32,7 @@ #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_base.h" #include "api/test/fake_frame_decryptor.h" #include "api/test/fake_frame_encryptor.h" #include "api/test/rtc_error_matchers.h" @@ -67,6 +69,8 @@ #include "test/run_loop.h" #include "test/wait_until.h" +namespace webrtc { + namespace { constexpr char kStreamId1[] = "local_stream_1"; @@ -79,19 +83,32 @@ constexpr uint32_t kAudioSsrc2 = 101; constexpr uint32_t kVideoSsrcSimulcast = 102; constexpr uint32_t kVideoSimulcastLayerCount = 2; -class MockSetStreamsObserver - : public webrtc::RtpSenderBase::SetStreamsObserver { +class MockSetStreamsObserver : public RtpSenderBase::SetStreamsObserver { public: MOCK_METHOD(void, OnSetStreams, (), (override)); }; } // namespace -namespace webrtc { using ::testing::ContainerEq; using RidList = std::vector; +class MockVideoMediaSendChannel : public FakeVideoMediaSendChannel { + public: + MockVideoMediaSendChannel(const VideoOptions& options, + TaskQueueBase* network_thread) + : FakeVideoMediaSendChannel(options, network_thread) {} + + void SetFrameEncryptor( + uint32_t ssrc, + scoped_refptr frame_encryptor) override { + last_set_frame_encryptor_ = frame_encryptor; + } + + scoped_refptr last_set_frame_encryptor_; +}; + class RtpSenderReceiverTest : public ::testing::Test, public ::testing::WithParamInterface> { @@ -148,10 +165,10 @@ class RtpSenderReceiverTest } ~RtpSenderReceiverTest() override { - audio_rtp_sender_ = nullptr; - video_rtp_sender_ = nullptr; - audio_rtp_receiver_ = nullptr; - video_rtp_receiver_ = nullptr; + DestroyAudioRtpSender(); + DestroyVideoRtpSender(); + DestroyAudioRtpReceiver(); + DestroyVideoRtpReceiver(); local_stream_ = nullptr; video_track_ = nullptr; audio_track_ = nullptr; @@ -190,9 +207,12 @@ class RtpSenderReceiverTest EXPECT_TRUE(local_stream_->AddTrack(audio_track_)); std::unique_ptr set_streams_observer = std::make_unique(); - audio_rtp_sender_ = AudioRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), audio_track_->id(), nullptr, - set_streams_observer.get(), voice_media_send_channel_.get()); + worker_thread_->BlockingCall([&]() { + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + audio_track_->id(), nullptr, set_streams_observer.get(), + voice_media_send_channel_.get()); + }); ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); audio_rtp_sender_->SetStreams({local_stream_->id()}); @@ -201,9 +221,11 @@ class RtpSenderReceiverTest } void CreateAudioRtpSenderWithNoTrack() { - audio_rtp_sender_ = AudioRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), /*id=*/"", nullptr, nullptr, - voice_media_send_channel_.get()); + worker_thread_->BlockingCall([&]() { + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, nullptr, voice_media_send_channel_.get()); + }); } void CreateVideoRtpSender(uint32_t ssrc) { @@ -251,9 +273,12 @@ class RtpSenderReceiverTest AddVideoTrack(is_screencast); std::unique_ptr set_streams_observer = std::make_unique(); - video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), video_track_->id(), - set_streams_observer.get(), video_media_send_channel()); + worker_thread_->BlockingCall([&]() { + video_rtp_sender_ = VideoRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + video_track_->id(), set_streams_observer.get(), + video_media_send_channel()); + }); ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); @@ -261,17 +286,25 @@ class RtpSenderReceiverTest VerifyVideoChannelInput(ssrc); } void CreateVideoRtpSenderWithNoTrack() { - video_rtp_sender_ = - VideoRtpSender::Create(CreateEnvironment(), worker_thread_.get(), - /*id=*/"", nullptr, video_media_send_channel()); + worker_thread_->BlockingCall([&]() { + video_rtp_sender_ = VideoRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, video_media_send_channel()); + }); } void DestroyAudioRtpSender() { + if (!audio_rtp_sender_) + return; + audio_rtp_sender_->Stop(); audio_rtp_sender_ = nullptr; VerifyVoiceChannelNoInput(); } void DestroyVideoRtpSender() { + if (!video_rtp_sender_) + return; + video_rtp_sender_->Stop(); video_rtp_sender_ = nullptr; VerifyVideoChannelNoInput(); } @@ -279,9 +312,13 @@ class RtpSenderReceiverTest void CreateAudioRtpReceiver( std::vector> streams = {}) { audio_rtp_receiver_ = make_ref_counted( - Thread::Current(), kAudioTrackId, streams); - audio_rtp_receiver_->SetMediaChannel(voice_media_receive_channel()); - audio_rtp_receiver_->SetupMediaChannel(kAudioSsrc); + worker_thread_.get(), kAudioTrackId, streams); + worker_thread_->BlockingCall([this] { + audio_rtp_receiver_->SetMediaChannel(voice_media_receive_channel()); + }); + auto setup_task = audio_rtp_receiver_->GetSetupForMediaChannel(kAudioSsrc); + worker_thread_->BlockingCall( + [task = std::move(setup_task)]() mutable { std::move(task)(); }); audio_track_ = audio_rtp_receiver_->audio_track(); VerifyVoiceChannelOutput(); } @@ -289,9 +326,13 @@ class RtpSenderReceiverTest void CreateVideoRtpReceiver( std::vector> streams = {}) { video_rtp_receiver_ = make_ref_counted( - Thread::Current(), kVideoTrackId, streams); - video_rtp_receiver_->SetMediaChannel(video_media_receive_channel()); - video_rtp_receiver_->SetupMediaChannel(kVideoSsrc); + worker_thread_.get(), kVideoTrackId, streams); + worker_thread_->BlockingCall([this] { + video_rtp_receiver_->SetMediaChannel(video_media_receive_channel()); + }); + auto setup_task = video_rtp_receiver_->GetSetupForMediaChannel(kVideoSsrc); + worker_thread_->BlockingCall( + [task = std::move(setup_task)]() mutable { std::move(task)(); }); video_track_ = video_rtp_receiver_->video_track(); VerifyVideoChannelOutput(); } @@ -308,16 +349,22 @@ class RtpSenderReceiverTest uint32_t primary_ssrc = stream_params.first_ssrc(); video_rtp_receiver_ = make_ref_counted( - Thread::Current(), kVideoTrackId, streams); - video_rtp_receiver_->SetMediaChannel(video_media_receive_channel()); - video_rtp_receiver_->SetupMediaChannel(primary_ssrc); + worker_thread_.get(), kVideoTrackId, streams); + worker_thread_->BlockingCall([this] { + video_rtp_receiver_->SetMediaChannel(video_media_receive_channel()); + }); + auto setup_task = + video_rtp_receiver_->GetSetupForMediaChannel(primary_ssrc); + worker_thread_->BlockingCall( + [task = std::move(setup_task)]() mutable { std::move(task)(); }); video_track_ = video_rtp_receiver_->video_track(); } void DestroyAudioRtpReceiver() { if (!audio_rtp_receiver_) return; - audio_rtp_receiver_->SetMediaChannel(nullptr); + worker_thread_->BlockingCall( + [this] { audio_rtp_receiver_->SetMediaChannel(nullptr); }); audio_rtp_receiver_ = nullptr; VerifyVoiceChannelNoOutput(); } @@ -326,11 +373,20 @@ class RtpSenderReceiverTest if (!video_rtp_receiver_) return; video_rtp_receiver_->Stop(); - video_rtp_receiver_->SetMediaChannel(nullptr); + worker_thread_->BlockingCall( + [this] { video_rtp_receiver_->SetMediaChannel(nullptr); }); video_rtp_receiver_ = nullptr; VerifyVideoChannelNoOutput(); } + void FlushWorker() { + RTC_DCHECK_EQ(TaskQueueBase::Current(), run_loop_.task_queue()); + worker_thread_->PostTask([this, signaling_thread = run_loop_.task_queue()] { + signaling_thread->PostTask([this] { run_loop_.Quit(); }); + }); + run_loop_.Run(); + } + void VerifyVoiceChannelInput() { VerifyVoiceChannelInput(kAudioSsrc); } void VerifyVoiceChannelInput(uint32_t ssrc) { @@ -441,8 +497,9 @@ class RtpSenderReceiverTest void RunDisableSimulcastLayersWithoutMediaEngineTest( const std::vector& all_layers, const std::vector& disabled_layers) { - auto sender = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), "1", nullptr, nullptr); + auto sender = + VideoRtpSender::Create(CreateEnvironment(), signaling_thread_, + worker_thread_.get(), "1", nullptr, nullptr); RtpParameters parameters; parameters.encodings.resize(all_layers.size()); for (size_t i = 0; i < all_layers.size(); ++i) { @@ -480,7 +537,8 @@ class RtpSenderReceiverTest MediaReceiveChannelInterface* media_channel, RtpReceiverInterface* receiver, uint32_t ssrc) { - receiver->SetJitterBufferMinimumDelay(/*delay_seconds=*/0.5); + worker_thread_->BlockingCall( + [&] { receiver->SetJitterBufferMinimumDelay(/*delay_seconds=*/0.5); }); std::optional delay_ms = media_channel->GetBaseMinimumPlayoutDelayMs(ssrc); // In milliseconds. EXPECT_DOUBLE_EQ(0.5, delay_ms.value_or(0) / 1000.0); @@ -505,6 +563,9 @@ class RtpSenderReceiverTest } test::RunLoop run_loop_; + // Initialize `signaling_thread_` to point to the current thread. + // This is the internal thread owned by `run_loop_`. + Thread* const signaling_thread_ = Thread::Current(); const std::unique_ptr network_thread_; const std::unique_ptr worker_thread_; const Environment env_ = CreateEnvironment(); @@ -608,14 +669,15 @@ TEST_F(RtpSenderReceiverTest, RemoteAudioTrackDisable) { // Handling of enable/disable is applied asynchronously. audio_track_->set_enabled(false); - run_loop_.Flush(); + FlushWorker(); // Wait for volume change. EXPECT_TRUE( voice_media_receive_channel()->GetOutputVolume(kAudioSsrc, &volume)); EXPECT_EQ(0, volume); audio_track_->set_enabled(true); - run_loop_.Flush(); + FlushWorker(); // Wait for volume change. + EXPECT_TRUE( voice_media_receive_channel()->GetOutputVolume(kAudioSsrc, &volume)); EXPECT_EQ(1, volume); @@ -669,30 +731,33 @@ TEST_F(RtpSenderReceiverTest, RemoteAudioTrackSetVolume) { double volume; audio_track_->GetSource()->SetVolume(0.5); - run_loop_.Flush(); + // Wait for the worker thread to apply the volume change. + FlushWorker(); + EXPECT_TRUE( voice_media_receive_channel()->GetOutputVolume(kAudioSsrc, &volume)); EXPECT_EQ(0.5, volume); // Disable the audio track, this should prevent setting the volume. audio_track_->set_enabled(false); - - run_loop_.Flush(); audio_track_->GetSource()->SetVolume(0.8); + FlushWorker(); // Wait for the the volume change. EXPECT_TRUE( voice_media_receive_channel()->GetOutputVolume(kAudioSsrc, &volume)); EXPECT_EQ(0, volume); // When the track is enabled, the previously set volume should take effect. audio_track_->set_enabled(true); - run_loop_.Flush(); + FlushWorker(); // The volume is applied asynchronously. + EXPECT_TRUE( voice_media_receive_channel()->GetOutputVolume(kAudioSsrc, &volume)); EXPECT_EQ(0.8, volume); // Try changing volume one more time. audio_track_->GetSource()->SetVolume(0.9); - run_loop_.Flush(); + FlushWorker(); + EXPECT_TRUE( voice_media_receive_channel()->GetOutputVolume(kAudioSsrc, &volume)); EXPECT_EQ(0.9, volume); @@ -843,7 +908,7 @@ TEST_F(RtpSenderReceiverTest, AudioSenderSsrcChanged) { VerifyVoiceChannelNoInput(kAudioSsrc); VerifyVoiceChannelInput(kAudioSsrc2); - audio_rtp_sender_ = nullptr; + DestroyAudioRtpSender(); VerifyVoiceChannelNoInput(kAudioSsrc2); } @@ -856,7 +921,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderSsrcChanged) { VerifyVideoChannelNoInput(kVideoSsrc); VerifyVideoChannelInput(kVideoSsrc2); - video_rtp_sender_ = nullptr; + DestroyVideoRtpSender(); VerifyVideoChannelNoInput(kVideoSsrc2); } @@ -887,9 +952,9 @@ TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParametersAsync) { } TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParametersBeforeNegotiation) { - audio_rtp_sender_ = - AudioRtpSender::Create(CreateEnvironment(), worker_thread_.get(), - /*id=*/"", nullptr, nullptr, nullptr); + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, nullptr, nullptr); RtpParameters params = audio_rtp_sender_->GetParameters(); ASSERT_EQ(1u, params.encodings.size()); @@ -905,9 +970,9 @@ TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParametersBeforeNegotiation) { TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParametersAsyncBeforeNegotiation) { - audio_rtp_sender_ = - AudioRtpSender::Create(CreateEnvironment(), worker_thread_.get(), - /*id=*/"", nullptr, nullptr, nullptr); + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, nullptr, nullptr); std::optional result; RtpParameters params = audio_rtp_sender_->GetParameters(); @@ -941,8 +1006,8 @@ TEST_F(RtpSenderReceiverTest, AudioSenderInitParametersMovedAfterNegotiation) { std::unique_ptr set_streams_observer = std::make_unique(); audio_rtp_sender_ = AudioRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), audio_track_->id(), nullptr, - set_streams_observer.get(), nullptr); + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + audio_track_->id(), nullptr, set_streams_observer.get(), nullptr); ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); audio_rtp_sender_->SetStreams({local_stream_->id()}); @@ -974,9 +1039,9 @@ TEST_F(RtpSenderReceiverTest, AudioSenderInitParametersMovedAfterNegotiation) { TEST_F(RtpSenderReceiverTest, AudioSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) { - audio_rtp_sender_ = - AudioRtpSender::Create(CreateEnvironment(), worker_thread_.get(), - /*id=*/"", nullptr, nullptr, nullptr); + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, nullptr, nullptr); RtpParameters params; RTCError result = audio_rtp_sender_->SetParameters(params); @@ -1153,8 +1218,9 @@ TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParametersAsync) { } TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParametersBeforeNegotiation) { - video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), /*id=*/"", nullptr, nullptr); + video_rtp_sender_ = + VideoRtpSender::Create(CreateEnvironment(), signaling_thread_, + worker_thread_.get(), /*id=*/"", nullptr, nullptr); RtpParameters params = video_rtp_sender_->GetParameters(); ASSERT_EQ(1u, params.encodings.size()); @@ -1170,8 +1236,9 @@ TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParametersBeforeNegotiation) { TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParametersAsyncBeforeNegotiation) { - video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), /*id=*/"", nullptr, nullptr); + video_rtp_sender_ = + VideoRtpSender::Create(CreateEnvironment(), signaling_thread_, + worker_thread_.get(), /*id=*/"", nullptr, nullptr); std::optional result; RtpParameters params = video_rtp_sender_->GetParameters(); @@ -1202,8 +1269,8 @@ TEST_F(RtpSenderReceiverTest, VideoSenderInitParametersMovedAfterNegotiation) { std::unique_ptr set_streams_observer = std::make_unique(); video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), video_track_->id(), - set_streams_observer.get(), nullptr); + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + video_track_->id(), set_streams_observer.get(), nullptr); ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); @@ -1246,8 +1313,8 @@ TEST_F(RtpSenderReceiverTest, std::unique_ptr set_streams_observer = std::make_unique(); video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), video_track_->id(), - set_streams_observer.get(), nullptr); + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + video_track_->id(), set_streams_observer.get(), nullptr); ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); @@ -1308,8 +1375,9 @@ TEST(RtpSenderReceiverDeathTest, std::unique_ptr set_streams_observer = std::make_unique(); - auto video_rtp_sender = VideoRtpSender::Create( - env, thread, video_track->id(), set_streams_observer.get(), nullptr); + auto video_rtp_sender = + VideoRtpSender::Create(env, thread, thread, video_track->id(), + set_streams_observer.get(), nullptr); ASSERT_TRUE(video_rtp_sender->SetTrack(video_track.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); @@ -1332,13 +1400,15 @@ TEST(RtpSenderReceiverDeathTest, video_rtp_sender->SetMediaChannel( video_media_send_channel->AsVideoSendChannel()); EXPECT_DEATH(video_rtp_sender->SetSsrc(kVideoSsrcSimulcast), ""); + video_rtp_sender->Stop(); } #endif TEST_F(RtpSenderReceiverTest, VideoSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) { - video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), /*id=*/"", nullptr, nullptr); + video_rtp_sender_ = + VideoRtpSender::Create(CreateEnvironment(), signaling_thread_, + worker_thread_.get(), /*id=*/"", nullptr, nullptr); RtpParameters params; RTCError result = video_rtp_sender_->SetParameters(params); @@ -1644,7 +1714,8 @@ TEST_F(RtpSenderReceiverTest, SetVideoBitratePriority) { TEST_F(RtpSenderReceiverTest, VideoReceiverCanGetParametersWithSimulcast) { CreateVideoRtpReceiverWithSimulcast({}, 2); - RtpParameters params = video_rtp_receiver_->GetParameters(); + RtpParameters params = worker_thread_->BlockingCall( + [&] { return video_rtp_receiver_->GetParameters(); }); EXPECT_EQ(2u, params.encodings.size()); DestroyVideoRtpReceiver(); @@ -1749,8 +1820,8 @@ TEST_F(RtpSenderReceiverTest, // applied even if the track is set on construction. video_track_->set_content_hint(VideoTrackInterface::ContentHint::kDetailed); video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), worker_thread_.get(), video_track_->id(), - set_streams_observer.get(), nullptr); + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + video_track_->id(), set_streams_observer.get(), nullptr); ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); @@ -1860,10 +1931,12 @@ TEST_F(RtpSenderReceiverTest, AudioReceiverCanSetFrameDecryptor) { CreateAudioRtpReceiver(); scoped_refptr fake_frame_decryptor( make_ref_counted()); - EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor()); - audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); - EXPECT_EQ(fake_frame_decryptor.get(), - audio_rtp_receiver_->GetFrameDecryptor().get()); + worker_thread_->BlockingCall([&] { + EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor()); + audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); + EXPECT_EQ(fake_frame_decryptor.get(), + audio_rtp_receiver_->GetFrameDecryptor().get()); + }); DestroyAudioRtpReceiver(); } @@ -1872,9 +1945,11 @@ TEST_F(RtpSenderReceiverTest, AudioReceiverCannotSetFrameDecryptorAfterStop) { CreateAudioRtpReceiver(); scoped_refptr fake_frame_decryptor( make_ref_counted()); - EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor()); - audio_rtp_receiver_->SetMediaChannel(nullptr); - audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); + worker_thread_->BlockingCall([&] { + EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor()); + audio_rtp_receiver_->SetMediaChannel(nullptr); + audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); + }); // TODO(webrtc:9926) - Validate media channel not set once fakes updated. DestroyAudioRtpReceiver(); } @@ -1907,10 +1982,12 @@ TEST_F(RtpSenderReceiverTest, VideoReceiverCanSetFrameDecryptor) { CreateVideoRtpReceiver(); scoped_refptr fake_frame_decryptor( make_ref_counted()); - EXPECT_EQ(nullptr, video_rtp_receiver_->GetFrameDecryptor()); - video_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); - EXPECT_EQ(fake_frame_decryptor.get(), - video_rtp_receiver_->GetFrameDecryptor().get()); + worker_thread_->BlockingCall([&] { + EXPECT_EQ(nullptr, video_rtp_receiver_->GetFrameDecryptor()); + video_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); + EXPECT_EQ(fake_frame_decryptor.get(), + video_rtp_receiver_->GetFrameDecryptor().get()); + }); DestroyVideoRtpReceiver(); } @@ -1919,9 +1996,11 @@ TEST_F(RtpSenderReceiverTest, VideoReceiverCannotSetFrameDecryptorAfterStop) { CreateVideoRtpReceiver(); scoped_refptr fake_frame_decryptor( make_ref_counted()); - EXPECT_EQ(nullptr, video_rtp_receiver_->GetFrameDecryptor()); - video_rtp_receiver_->SetMediaChannel(nullptr); - video_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); + worker_thread_->BlockingCall([&] { + EXPECT_EQ(nullptr, video_rtp_receiver_->GetFrameDecryptor()); + video_rtp_receiver_->SetMediaChannel(nullptr); + video_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); + }); // TODO(webrtc:9926) - Validate media channel not set once fakes updated. DestroyVideoRtpReceiver(); } @@ -1934,9 +2013,13 @@ TEST_F(RtpSenderReceiverTest, RtpParameters parameters = video_rtp_sender_->GetParameters(); RtpParameters new_parameters = video_rtp_sender_->GetParametersInternal(); new_parameters.encodings[0].active = false; - video_rtp_sender_->SetParametersInternal(new_parameters, nullptr, true); + EXPECT_TRUE( + video_rtp_sender_->SetParametersInternal(new_parameters, nullptr, true) + .ok()); new_parameters.encodings[0].active = true; - video_rtp_sender_->SetParametersInternal(new_parameters, nullptr, true); + EXPECT_TRUE( + video_rtp_sender_->SetParametersInternal(new_parameters, nullptr, true) + .ok()); parameters.encodings[0].active = false; EXPECT_TRUE(video_rtp_sender_->SetParameters(parameters).ok()); } @@ -1944,13 +2027,35 @@ TEST_F(RtpSenderReceiverTest, // Checks that the senders SetStreams eliminates duplicate stream ids. TEST_F(RtpSenderReceiverTest, SenderSetStreamsEliminatesDuplicateIds) { AddVideoTrack(); - video_rtp_sender_ = - VideoRtpSender::Create(CreateEnvironment(), worker_thread_.get(), - video_track_->id(), nullptr, nullptr); + video_rtp_sender_ = VideoRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + video_track_->id(), nullptr, nullptr); video_rtp_sender_->SetStreams({"1", "2", "1"}); EXPECT_EQ(video_rtp_sender_->stream_ids().size(), 2u); } +TEST_F(RtpSenderReceiverTest, SetSsrcPropagatesFrameEncryptorWithNoEncodings) { + // Create mock channel with strict expectations. + auto mock_channel = std::make_unique( + VideoOptions(), network_thread_.get()); + MockVideoMediaSendChannel* mock_channel_ptr = mock_channel.get(); + // RtpSenderReceiverTest takes ownership of the channel. + video_media_send_channel_ = std::move(mock_channel); + + // Create sender with no track, which results in empty encodings. + CreateVideoRtpSenderWithNoTrack(); + video_rtp_sender_->set_init_send_encodings({}); + + auto frame_encryptor = + scoped_refptr(new FakeFrameEncryptor()); + video_rtp_sender_->SetFrameEncryptor(frame_encryptor); + mock_channel_ptr->last_set_frame_encryptor_ = nullptr; + + // Expect propagation to media channel when SSRC is set. + video_rtp_sender_->SetSsrc(kVideoSsrc); + EXPECT_EQ(mock_channel_ptr->last_set_frame_encryptor_, frame_encryptor); +} + // Helper method for syntactic sugar for accepting a vector with '{}' notation. std::pair CreatePairOfRidVectors( const std::vector& first, diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index ecd827939be..553b6baed97 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -15,15 +15,15 @@ #include #include #include -#include +#include #include #include #include #include "absl/algorithm/container.h" +#include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_options.h" #include "api/crypto/crypto_options.h" #include "api/environment/environment.h" @@ -48,11 +48,13 @@ #include "media/base/media_channel.h" #include "media/base/media_config.h" #include "media/base/media_engine.h" +#include "media/base/stream_params.h" #include "pc/audio_rtp_receiver.h" #include "pc/channel.h" #include "pc/channel_interface.h" #include "pc/codec_vendor.h" #include "pc/connection_context.h" +#include "pc/dtls_transport.h" #include "pc/legacy_stats_collector_interface.h" #include "pc/rtp_media_utils.h" #include "pc/rtp_receiver.h" @@ -60,11 +62,13 @@ #include "pc/rtp_sender.h" #include "pc/rtp_sender_proxy.h" #include "pc/rtp_transport_internal.h" +#include "pc/scoped_operations_batcher.h" #include "pc/session_description.h" #include "pc/video_rtp_receiver.h" #include "rtc_base/checks.h" #include "rtc_base/crypto_random.h" #include "rtc_base/logging.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" namespace webrtc { @@ -96,17 +100,16 @@ RTCError VerifyCodecPreferences(const std::vector& codecs, return IsSameRtpCodec(codec_capability, codec); }); })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Invalid codec preferences: Missing codec from codec " - "capabilities."); + return LOG_ERROR(RTCError::InvalidModification() + << "Invalid codec preferences: Missing codec from codec " + "capabilities."); } // If `codecs` only contains entries for RTX, RED, FEC or Comfort Noise, throw // InvalidModificationError. if (!HasAnyMediaCodec(codecs)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Invalid codec preferences: codec list must have a non " - "RTX, RED, FEC or Comfort Noise entry."); + return LOG_ERROR(RTCError::InvalidModification() + << "Invalid codec preferences: codec list must have a non " + "RTX, RED, FEC or Comfort Noise entry."); } return RTCError::OK(); } @@ -161,39 +164,31 @@ scoped_refptr> CreateSender( return RtpSenderProxyWithInternal::Create( context->signaling_thread(), AudioRtpSender::Create( - env, context->worker_thread(), sender_id, legacy_stats, - set_streams_observer, + env, context->signaling_thread(), context->worker_thread(), + sender_id, legacy_stats, set_streams_observer, static_cast(media_send_channel))); } RTC_DCHECK_EQ(media_type, MediaType::VIDEO); return RtpSenderProxyWithInternal::Create( context->signaling_thread(), VideoRtpSender::Create( - env, context->worker_thread(), sender_id, set_streams_observer, + env, context->signaling_thread(), context->worker_thread(), sender_id, + set_streams_observer, static_cast(media_send_channel))); } -scoped_refptr> CreateSender( - MediaType media_type, - const Environment& env, - ConnectionContext* context, - LegacyStatsCollectorInterface* legacy_stats, - RtpSenderBase::SetStreamsObserver* set_streams_observer, - absl::string_view sender_id, - MediaSendChannelInterface* media_send_channel, +void ConfigureSender( + scoped_refptr>& sender, MediaStreamTrackInterface* track, const std::vector& stream_ids, const std::vector& send_encodings, CodecVendor& codec_vendor) { - scoped_refptr> sender = - CreateSender(media_type, env, context, legacy_stats, set_streams_observer, - sender_id, media_send_channel); bool set_track_succeeded = sender->SetTrack(track); RTC_DCHECK(set_track_succeeded); - sender->internal()->set_stream_ids(stream_ids); - sender->internal()->set_init_send_encodings(send_encodings); - ConfigureSendCodecs(codec_vendor, media_type, sender->internal()); - return sender; + auto* internal = sender->internal(); + internal->set_stream_ids(stream_ids); + internal->set_init_send_encodings(send_encodings); + ConfigureSendCodecs(codec_vendor, sender->media_type(), internal); } template @@ -237,7 +232,9 @@ CreateMediaContentChannels( const AudioOptions& audio_options, const VideoOptions& video_options, const CryptoOptions& crypto_options, - VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) { + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + video_encoder_switch_request_callback = nullptr) { if (media_type == MediaType::AUDIO) { return {media_engine->voice().CreateSendChannel( env, call, media_config, audio_options, crypto_options), @@ -246,7 +243,8 @@ CreateMediaContentChannels( } return {media_engine->video().CreateSendChannel( env, call, media_config, video_options, crypto_options, - video_bitrate_allocator_factory), + video_bitrate_allocator_factory, + std::move(video_encoder_switch_request_callback)), media_engine->video().CreateReceiveChannel( env, call, media_config, video_options, crypto_options)}; } @@ -292,8 +290,11 @@ RtpTransceiver::RtpTransceiver(const Environment& env, thread_(context->signaling_thread()), unified_plan_(false), media_type_(media_type), + signaling_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( + /*alive=*/false, + context->signaling_thread())), network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( - true, + /*alive=*/true, context->network_thread())), context_(context), codec_lookup_helper_(codec_lookup_helper), @@ -316,8 +317,11 @@ RtpTransceiver::RtpTransceiver( thread_(context->signaling_thread()), unified_plan_(true), media_type_(sender->media_type()), + signaling_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( + /*alive=*/false, + context->signaling_thread())), network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( - true, + /*alive=*/true, context->network_thread())), context_(context), codec_lookup_helper_(codec_lookup_helper), @@ -333,14 +337,18 @@ RtpTransceiver::RtpTransceiver( RTC_DCHECK_EQ(sender->media_type(), receiver->media_type()); RTC_DCHECK_EQ(media_type_, sender->media_type()); RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); + auto* sender_internal = sender->internal(); senders_.push_back(std::move(sender)); receivers_.push_back(std::move(receiver)); if (media_type_ == MediaType::VIDEO) { ConfigureExtraVideoHeaderExtensions( - sender_internal()->GetParametersInternal().encodings, + sender_internal + ->GetParametersInternal(/*may_use_cache*/ true, + /*with_all_layers=*/false) + .encodings, header_extensions_to_negotiate_); } - ConfigureSendCodecs(codec_vendor(), media_type_, sender_internal().get()); + ConfigureSendCodecs(codec_vendor(), media_type_, sender_internal); } RtpTransceiver::RtpTransceiver( @@ -367,8 +375,11 @@ RtpTransceiver::RtpTransceiver( thread_(context->signaling_thread()), unified_plan_(true), media_type_(media_type), + signaling_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( + /*alive=*/false, + context->signaling_thread())), network_thread_safety_(PendingTaskSafetyFlag::CreateAttachedToTaskQueue( - true, + /*alive=*/true, context->network_thread())), media_engine_ref_(nullptr), context_(context), @@ -391,19 +402,22 @@ RtpTransceiver::RtpTransceiver( // This should be possible without a blocking call to the worker, perhaps done // asynchronously. At the moment this is complicated by the fact that // construction of the channels actually changes the settings of the engine. - context_->worker_thread()->BlockingCall([&]() mutable { + context_->worker_thread()->BlockingCall([&]() { RTC_DCHECK_RUN_ON(this->context()->worker_thread()); auto channels = CreateMediaContentChannels( media_type_, env_, media_engine(), call, media_config, audio_options, - video_options, crypto_options, video_bitrate_allocator_factory); + video_options, crypto_options, video_bitrate_allocator_factory, + GetEncoderSwitchRequestCallback()); owned_send_channel_ = std::move(channels.first); owned_receive_channel_ = std::move(channels.second); + senders_.push_back(CreateSender(media_type_, env_, context_, legacy_stats_, + set_streams_observer_, sender_id, + owned_send_channel_.get())); }); - senders_.push_back(CreateSender( - media_type_, env_, context_, legacy_stats_, set_streams_observer_, - sender_id, owned_send_channel_.get(), track.get(), stream_ids, - init_send_encodings, codec_vendor())); + ConfigureSender(senders_.back(), track.get(), stream_ids, init_send_encodings, + codec_vendor()); + receivers_.push_back(CreateReceiver( media_type_, context_->signaling_thread(), context_->worker_thread(), receiver_id.empty() ? CreateRandomUuid() : receiver_id, @@ -438,12 +452,21 @@ RTCError RtpTransceiver::CreateChannel( absl::AnyInvocable transport_lookup) { RTC_DCHECK_RUN_ON(thread_); - RTC_DCHECK(!channel()); + RTC_DCHECK(!channel_); RTC_DCHECK(!mid_ || mid_.value() == mid); RTC_DCHECK(!stopped_); mid_ = mid; + if (!signaling_thread_safety_) { + // This code path is hit during rollback. + signaling_thread_safety_ = PendingTaskSafetyFlag::Create(); + } else { + // Newly constructed. + RTC_DCHECK(!signaling_thread_safety_->alive()); + signaling_thread_safety_->SetAlive(); + } + std::unique_ptr new_channel; // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to // the worker thread. We shouldn't be using the `call_ptr_` hack here but @@ -471,18 +494,11 @@ RTCError RtpTransceiver::CreateChannel( auto channels = CreateMediaContentChannels( media_type(), env_, media_engine(), call_ptr, media_config, audio_options, video_options, crypto_options, - video_bitrate_allocator_factory); + video_bitrate_allocator_factory, GetEncoderSwitchRequestCallback()); media_send_channel = std::move(channels.first); media_receive_channel = std::move(channels.second); SetMediaChannels(media_send_channel.get(), media_receive_channel.get()); } - // Note that this is safe because both sending and - // receiving channels will be deleted at the same time. - media_send_channel->SetSsrcListChangedCallback( - [receive_channel = - media_receive_channel.get()](const std::set& choices) { - receive_channel->ChooseReceiverReportSsrc(choices); - }); if (media_type() == MediaType::AUDIO) { new_channel = @@ -513,15 +529,15 @@ RTCError RtpTransceiver::SetChannel( RTC_DCHECK(!channel_); // Cannot set a channel on a stopped transceiver. if (stopped_) { - return RTCError(RTCErrorType::INVALID_STATE); + return RTCError::InvalidState(); } RTC_LOG_THREAD_BLOCK_COUNT(); RTC_DCHECK_EQ(media_type(), channel->media_type()); RTC_DCHECK(mid_ || channel->mid().empty()); - signaling_thread_safety_ = PendingTaskSafetyFlag::Create(); channel_ = std::move(channel); + transport_name_ = std::nullopt; // An alternative to this, could be to require SetChannel to be called // on the network thread. The channel object operates for the most part @@ -532,32 +548,44 @@ RTCError RtpTransceiver::SetChannel( // Similarly, if the channel() accessor is limited to the network thread, that // helps with keeping the channel implementation requirements being met and // avoids synchronization for accessing the pointer or network related state. + std::optional transport_name; RTCError err = context()->network_thread()->BlockingCall( - [&, flag = signaling_thread_safety_]() { - if (!channel_->SetRtpTransport( - std::move(transport_lookup)(channel_->mid()))) { + [&, flag = signaling_thread_safety_, channel = channel_.get()]() { + RtpTransportInternal* transport = + std::move(transport_lookup)(channel->mid()); + if (!channel->SetRtpTransport(transport)) { return RTCError::InvalidParameter() - << "Invalid transport for mid=" << channel_->mid(); + << "Invalid transport for mid=" << channel->mid(); } - channel_->SetFirstPacketReceivedCallback([thread = thread_, flag = flag, - this]() mutable { - thread->PostTask( - SafeTask(std::move(flag), [this]() { OnFirstPacketReceived(); })); - }); - channel_->SetFirstPacketSentCallback([thread = thread_, flag = flag, - this]() mutable { - thread->PostTask( - SafeTask(std::move(flag), [this]() { OnFirstPacketSent(); })); - }); - channel_->SetPacketReceivedCallback_n([this, flag = flag]() { - RTC_DCHECK_RUN_ON(context()->network_thread()); - OnPacketReceived(flag); - }); + if (transport) { + transport_name = transport->transport_name(); + } + channel->SetFirstPacketReceivedCallback_n( + [thread = thread_, flag = flag, + this](const RtpPacketReceived& packet) mutable { + thread->PostTask( + SafeTask(std::move(flag), [this, ssrc = packet.Ssrc()]() { + OnFirstPacketReceived(ssrc); + })); + }); + channel->SetFirstPacketSentCallback_n( + [thread = thread_, flag = flag, this]() mutable { + thread->PostTask( + SafeTask(std::move(flag), [this]() { OnFirstPacketSent(); })); + }); + channel->SetPacketReceivedCallback_n( + [this, flag = flag](const RtpPacketReceived& packet) { + RTC_DCHECK_RUN_ON(context()->network_thread()); + OnPacketReceived(packet.Ssrc(), flag); + }); return RTCError::OK(); }); - if (err.ok() && set_media_channels) { - PushNewMediaChannel(); + if (err.ok()) { + transport_name_ = std::move(transport_name); + if (set_media_channels) { + PushNewMediaChannel(); + } } RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); @@ -572,7 +600,7 @@ absl::AnyInvocable RtpTransceiver::GetClearChannelNetworkTask() { // combine these into one function to avoid an ordering mistake? if (!channel_) { - RTC_DCHECK(!signaling_thread_safety_); + RTC_DCHECK(!signaling_thread_safety_ || !signaling_thread_safety_->alive()); return nullptr; } @@ -582,8 +610,8 @@ absl::AnyInvocable RtpTransceiver::GetClearChannelNetworkTask() { ChannelInterface* channel = channel_.get(); return [channel, flag = network_thread_safety_] { flag->SetNotAlive(); - channel->SetFirstPacketReceivedCallback(nullptr); - channel->SetFirstPacketSentCallback(nullptr); + channel->SetFirstPacketReceivedCallback_n(nullptr); + channel->SetFirstPacketSentCallback_n(nullptr); channel->SetPacketReceivedCallback_n(nullptr); channel->SetRtpTransport(nullptr); }; @@ -592,24 +620,27 @@ absl::AnyInvocable RtpTransceiver::GetClearChannelNetworkTask() { absl::AnyInvocable RtpTransceiver::GetDeleteChannelWorkerTask( bool stop_senders) { RTC_DCHECK_RUN_ON(thread_); - RTC_DCHECK(signaling_thread_safety_ == nullptr) + RTC_DCHECK(!signaling_thread_safety_ || !signaling_thread_safety_->alive()) << "GetClearChannelNetworkTask() must be called first"; if (!channel_) { return nullptr; } - std::vector> stop; + std::vector> stop_sender_actions; if (stop_senders) { - stop = DetachAndGetStopTasksForSenders(senders_); + stop_sender_actions = DetachAndGetStopTasksForSenders(senders_); } + transport_name_ = std::nullopt; + // Ensure that channel_ is not reachable via the transceiver, but is deleted // only after clearing the references in senders_ and receivers_. return [this, channel = std::move(channel_), senders = senders_, - receivers = receivers_, stop = std::move(stop)]() mutable { + receivers = receivers_, + stop_sender_actions = std::move(stop_sender_actions)]() mutable { RTC_DCHECK_RUN_ON(context()->worker_thread()); - for (auto& task : stop) { + for (auto& task : stop_sender_actions) { std::move(task)(); } ClearMediaChannelReferences(); @@ -637,14 +668,15 @@ void RtpTransceiver::ClearChannel() { } void RtpTransceiver::PushNewMediaChannel() { + RTC_DCHECK_RUN_ON(thread_); RTC_DCHECK(channel_); if (senders_.empty() && receivers_.empty()) { return; } - context()->worker_thread()->BlockingCall([&]() { + context()->worker_thread()->BlockingCall([&, channel = channel_.get()]() { RTC_DCHECK_RUN_ON(context()->worker_thread()); - SetMediaChannels(channel_->media_send_channel(), - channel_->media_receive_channel()); + SetMediaChannels(channel->media_send_channel(), + channel->media_receive_channel()); }); } @@ -658,6 +690,33 @@ void RtpTransceiver::SetMediaChannels(MediaSendChannelInterface* send, receiver->internal()->SetMediaChannel(receive); } } +VideoMediaSendChannelInterface::EncoderSwitchRequestCallback +RtpTransceiver::GetEncoderSwitchRequestCallback() { + if (media_type() != MediaType::VIDEO) { + return nullptr; + } + RTC_DCHECK(signaling_thread_safety_); + // Return a task that first clears the sender parameter cache on the signaling + // thread and then posts a task to apply the codec switch changes to the + // parameters on the worker. + return + [this, signaling_thread = context_->signaling_thread(), + worker_thread = context_->worker_thread(), + signaling_safety = signaling_thread_safety_]( + VideoMediaSendChannelInterface::EncoderSwitchRequestAction action) { + // Called on the encoder task queue. + signaling_thread->PostTask(SafeTask( + signaling_safety, + [this, worker_thread, action = std::move(action)]() mutable { + for (const auto& sender : senders_) { + sender->internal()->SetCachedParameters(std::nullopt); + } + worker_thread->PostTask([action = std::move(action)]() mutable { + std::move(action)(); + }); + })); + }; +} // RTC_RUN_ON(context()->worker_thread()); void RtpTransceiver::ClearMediaChannelReferences() { @@ -667,7 +726,7 @@ void RtpTransceiver::ClearMediaChannelReferences() { media_engine_ref_ = nullptr; } -void RtpTransceiver::AddSenderPlanB( +PLAN_B_ONLY void RtpTransceiver::AddSenderPlanB( scoped_refptr> sender) { RTC_DCHECK_RUN_ON(thread_); RTC_DCHECK(!stopped_); @@ -679,7 +738,7 @@ void RtpTransceiver::AddSenderPlanB( senders_.push_back(sender); } -scoped_refptr> +PLAN_B_ONLY scoped_refptr> RtpTransceiver::AddSenderPlanB( scoped_refptr track, absl::string_view sender_id, @@ -690,16 +749,18 @@ RtpTransceiver::AddSenderPlanB( RTC_DCHECK(!unified_plan_); RTC_DCHECK(media_type_ == MediaType::AUDIO || media_type_ == MediaType::VIDEO); - scoped_refptr> sender = - CreateSender(media_type_, env_, context_, legacy_stats_, - set_streams_observer_, sender_id, - channel_ ? channel_->media_send_channel() : nullptr, - track.get(), stream_ids, send_encodings, codec_vendor()); - senders_.push_back(sender); - return sender; + context_->worker_thread()->BlockingCall([&]() mutable { + RTC_DCHECK_RUN_ON(context()->worker_thread()); + senders_.push_back(CreateSender( + media_type_, env_, context_, legacy_stats_, set_streams_observer_, + sender_id, channel_ ? channel_->media_send_channel() : nullptr)); + }); + ConfigureSender(senders_.back(), track.get(), stream_ids, send_encodings, + codec_vendor()); + return senders_.back(); } -bool RtpTransceiver::RemoveSenderPlanB(RtpSenderInterface* sender) { +PLAN_B_ONLY bool RtpTransceiver::RemoveSenderPlanB(RtpSenderInterface* sender) { RTC_DCHECK(!unified_plan_); RTC_DCHECK_EQ(media_type(), sender->media_type()); auto it = absl::c_find(senders_, sender); @@ -711,7 +772,7 @@ bool RtpTransceiver::RemoveSenderPlanB(RtpSenderInterface* sender) { return true; } -void RtpTransceiver::AddReceiverPlanB( +PLAN_B_ONLY void RtpTransceiver::AddReceiverPlanB( scoped_refptr> receiver) { RTC_DCHECK_RUN_ON(thread_); RTC_DCHECK(!stopped_); @@ -722,7 +783,8 @@ void RtpTransceiver::AddReceiverPlanB( receivers_.push_back(receiver); } -bool RtpTransceiver::RemoveReceiverPlanB(RtpReceiverInterface* receiver) { +PLAN_B_ONLY bool RtpTransceiver::RemoveReceiverPlanB( + RtpReceiverInterface* receiver) { RTC_DCHECK_RUN_ON(thread_); RTC_DCHECK(!unified_plan_); RTC_DCHECK_EQ(media_type(), receiver->media_type()); @@ -770,14 +832,15 @@ MediaEngineInterface* RtpTransceiver::media_engine() { return media_engine_ref_->media_engine(); } -void RtpTransceiver::OnFirstPacketReceived() { +void RtpTransceiver::OnFirstPacketReceived(uint32_t ssrc) { for (const auto& receiver : receivers_) { - receiver->internal()->NotifyFirstPacketReceived(); + receiver->internal()->NotifyFirstPacketReceived(ssrc); } } // RTC_RUN_ON(context()->network_thread()) void RtpTransceiver::OnPacketReceived( + uint32_t ssrc, scoped_refptr safety) { if (!receptive_n_) { return; @@ -786,13 +849,13 @@ void RtpTransceiver::OnPacketReceived( return; } packet_notified_after_receptive_ = true; - thread_->PostTask(SafeTask(safety, [this]() { + thread_->PostTask(SafeTask(safety, [this, ssrc]() { RTC_DCHECK_RUN_ON(thread_); if (stopping() || stopped() || !receptive_) { return; } for (const auto& receiver : receivers_) { - receiver->internal()->NotifyFirstPacketReceivedAfterReceptiveChange(); + receiver->internal()->NotifyFirstPacketReceivedAfterReceptiveChange(ssrc); } })); } @@ -855,15 +918,15 @@ RtpTransceiverDirection RtpTransceiver::direction() const { RTCError RtpTransceiver::SetDirectionWithError( RtpTransceiverDirection new_direction) { if (unified_plan_ && stopping()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "Cannot set direction on a stopping transceiver."); + return LOG_ERROR(RTCError::InvalidState() + << "Cannot set direction on a stopping transceiver."); } if (new_direction == direction_) return RTCError::OK(); if (new_direction == RtpTransceiverDirection::kStopped) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "The set direction 'stopped' is invalid."); + return LOG_ERROR(RTCError::InvalidParameter() + << "The set direction 'stopped' is invalid."); } direction_ = new_direction; @@ -902,14 +965,15 @@ void RtpTransceiver::set_receptive(bool receptive) { } } -void RtpTransceiver::StopSendingAndReceiving() { +absl_nonnull absl::AnyInvocable +RtpTransceiver::GetStopSendingAndReceiving() { RTC_DCHECK_RUN_ON(thread_); RTC_DCHECK(!stopped_); RTC_DCHECK(!stopping_); // 1. Let sender be transceiver.[[Sender]]. // 2. Let receiver be transceiver.[[Receiver]]. - RTC_LOG_THREAD_BLOCK_COUNT(); + RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); // Signal to receiver sources that we're stopping. for (const auto& receiver : receivers_) { @@ -921,25 +985,23 @@ void RtpTransceiver::StopSendingAndReceiving() { // worker thread to avoid each sender doing that within `Stop()`. // Senders will have already cleared send when the media channel was set to // nullptr. - std::vector> stop = + std::vector> stop_sender_actions = DetachAndGetStopTasksForSenders(senders_); - // 3. Send an RTCP BYE for each RTP stream that was being sent by sender, as - // specified in [RFC3550]. - context()->worker_thread()->BlockingCall([&]() { - RTC_DCHECK_RUN_ON(context()->worker_thread()); - for (auto& task : stop) { - std::move(task)(); - } - ClearMediaChannelReferences(); - }); - - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); - stopping_ = true; direction_ = RtpTransceiverDirection::kInactive; - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); + // 3. Send an RTCP BYE for each RTP stream that was being sent by sender, as + // specified in [RFC3550]. + + return + [this, stop_sender_actions = std::move(stop_sender_actions)]() mutable { + RTC_DCHECK_RUN_ON(context()->worker_thread()); + for (auto& task : stop_sender_actions) { + std::move(task)(); + } + ClearMediaChannelReferences(); + }; } RTCError RtpTransceiver::StopStandard() { @@ -965,7 +1027,9 @@ RTCError RtpTransceiver::StopStandard() { // 5. Stop sending and receiving given transceiver, and update the // negotiation-needed flag for connection. - StopSendingAndReceiving(); + auto stop_task = GetStopSendingAndReceiving(); + context_->worker_thread()->BlockingCall( + [&]() mutable { std::move(stop_task)(); }); on_negotiation_needed_(); return RTCError::OK(); @@ -973,16 +1037,24 @@ RTCError RtpTransceiver::StopStandard() { void RtpTransceiver::StopInternal() { RTC_DCHECK_RUN_ON(thread_); - StopTransceiverProcedure(); + auto stop_task = GetStopTransceiverProcedure(); + if (stop_task) { + context_->worker_thread()->BlockingCall( + [stop_task = std::move(stop_task)]() mutable { + std::move(stop_task)(); + }); + } } -void RtpTransceiver::StopTransceiverProcedure() { +absl_nullable absl::AnyInvocable +RtpTransceiver::GetStopTransceiverProcedure() { RTC_DCHECK_RUN_ON(thread_); // As specified in the "Stop the RTCRtpTransceiver" procedure // 1. If transceiver.[[Stopping]] is false, stop sending and receiving given // transceiver. + absl::AnyInvocable stop_task; if (!stopping_) - StopSendingAndReceiving(); + stop_task = GetStopSendingAndReceiving(); // 2. Set transceiver.[[Stopped]] to true. stopped_ = true; @@ -992,10 +1064,12 @@ void RtpTransceiver::StopTransceiverProcedure() { // 4. Set transceiver.[[CurrentDirection]] to null. current_direction_ = std::nullopt; + + return stop_task; } RTCError RtpTransceiver::SetCodecPreferences( - ArrayView codec_capabilities) { + std::span codec_capabilities) { RTC_DCHECK(unified_plan_); // 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot // to codecs and abort these steps. @@ -1157,24 +1231,24 @@ bool IsMandatoryHeaderExtension(absl::string_view uri) { } RTCError RtpTransceiver::SetHeaderExtensionsToNegotiate( - ArrayView header_extensions) { + std::span header_extensions) { RTC_DCHECK_RUN_ON(thread_); // https://w3c.github.io/webrtc-extensions/#dom-rtcrtptransceiver-setheaderextensionstonegotiate if (header_extensions.size() != header_extensions_to_negotiate_.size()) { - return RTCError(RTCErrorType::INVALID_MODIFICATION, - "Size of extensions to negotiate does not match."); + return RTCError::InvalidModification() + << "Size of extensions to negotiate does not match."; } // For each index i of extensions, run the following steps: ... for (size_t i = 0; i < header_extensions.size(); i++) { const auto& extension = header_extensions[i]; if (extension.uri != header_extensions_to_negotiate_[i].uri) { - return RTCError(RTCErrorType::INVALID_MODIFICATION, - "Reordering extensions is not allowed."); + return RTCError::InvalidModification() + << "Reordering extensions is not allowed."; } if (IsMandatoryHeaderExtension(extension.uri) && extension.direction != RtpTransceiverDirection::kSendRecv) { - return RTCError(RTCErrorType::INVALID_MODIFICATION, - "Attempted to stop a mandatory extension."); + return RTCError::InvalidModification() + << "Attempted to stop a mandatory extension."; } // TODO(bugs.webrtc.org/7477): Currently there are no recvonly extensions so @@ -1199,19 +1273,19 @@ void RtpTransceiver::OnNegotiationUpdate( RTC_DCHECK(content); if (sdp_type == SdpType::kAnswer || sdp_type == SdpType::kPrAnswer) { negotiated_header_extensions_ = content->rtp_header_extensions(); - if (env_.field_trials().IsEnabled( + if (!env_.field_trials().IsDisabled( "WebRTC-HeaderExtensionNegotiateMemory")) { header_extensions_to_negotiate_ = GetNegotiatedHeaderExtensions(); } } else if (sdp_type == SdpType::kOffer) { - if (env_.field_trials().IsEnabled( + if (!env_.field_trials().IsDisabled( "WebRTC-HeaderExtensionNegotiateMemory")) { header_extensions_for_rollback_ = header_extensions_to_negotiate_; header_extensions_to_negotiate_ = GetOfferedAndImplementedHeaderExtensions(content); } } else if (sdp_type == SdpType::kRollback) { - if (env_.field_trials().IsEnabled( + if (!env_.field_trials().IsDisabled( "WebRTC-HeaderExtensionNegotiateMemory")) { RTC_CHECK(!header_extensions_for_rollback_.empty()); header_extensions_to_negotiate_ = header_extensions_for_rollback_; @@ -1219,4 +1293,178 @@ void RtpTransceiver::OnNegotiationUpdate( } } +bool RtpTransceiver::SetChannelRtpTransport( + RtpTransportInternal* rtp_transport) { + RTC_DCHECK_RUN_ON(context()->network_thread()); + RTC_DCHECK(channel_); + return channel_->SetRtpTransport(rtp_transport); +} + +void RtpTransceiver::SetChannelLocalContent( + const MediaContentDescription* content, + SdpType type, + ScopedOperationsBatcher& batcher) { + RTC_DCHECK_RUN_ON(context()->signaling_thread()); + SetChannelContent( + [this, content, type]() { + return channel_->SetLocalContent(content, type); + }, + batcher); +} + +void RtpTransceiver::SetChannelRemoteContent( + const MediaContentDescription* content, + SdpType type, + ScopedOperationsBatcher& batcher) { + RTC_DCHECK_RUN_ON(context()->signaling_thread()); + SetChannelContent( + [this, content, type]() { + return channel_->SetRemoteContent(content, type); + }, + batcher); +} + +void RtpTransceiver::SetChannelContent( + absl::AnyInvocable set_content, + ScopedOperationsBatcher& batcher) { + RTC_DCHECK_RUN_ON(context()->signaling_thread()); + + struct SenderParameters { + const uint32_t ssrc; + RtpSenderInternal* const sender; + std::optional parameters; + }; + + std::vector sender_parameters; + sender_parameters.reserve(senders_.size()); + for (const auto& sender : senders_) { + sender_parameters.push_back( + {.ssrc = sender->ssrc(), .sender = sender->internal()}); + } + + batcher.AddWithFinalizer( + [this, set_content = std::move(set_content), + sender_parameters = std::move(sender_parameters)]() mutable + -> RTCErrorOr { + RTC_DCHECK_RUN_ON(context()->worker_thread()); + if (!channel_) { + return RTCError::InvalidState() << "No channel"; + } + RTCError result = std::move(set_content)(); + if (!result.ok()) { + return result; + } + for (auto& entry : sender_parameters) { + if (entry.ssrc != 0) { + entry.parameters = + channel_->media_send_channel()->GetRtpSendParameters( + entry.ssrc); + } + } + return ScopedOperationsBatcher::FinalizerTask( + [sender_parameters = std::move(sender_parameters)]() mutable { + for (auto& entry : sender_parameters) { + if (entry.parameters) { + entry.sender->SetCachedParameters( + std::move(*entry.parameters)); + } + } + }); + }); +} +void RtpTransceiver::EnableChannel(bool enable) { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(channel_); + channel_->Enable(enable); +} + +void RtpTransceiver::ResetUnsignaledRecvStream() { + RTC_DCHECK_RUN_ON(thread_); + if (MediaReceiveChannelInterface* receive_channel = media_receive_channel()) { + receive_channel->ResetUnsignaledRecvStream(); + } +} + +const std::vector& RtpTransceiver::channel_local_streams() const { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(channel_); + return channel_->local_streams(); +} + +const std::vector& RtpTransceiver::channel_remote_streams() + const { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(channel_); + return channel_->remote_streams(); +} + +absl::string_view RtpTransceiver::channel_transport_name() const { + RTC_DCHECK_RUN_ON(context()->network_thread()); + RTC_DCHECK(channel_); + return channel_->transport_name(); +} + +MediaSendChannelInterface* RtpTransceiver::media_send_channel() { + RTC_DCHECK_RUN_ON(thread_); + return channel_ ? channel_->media_send_channel() : nullptr; +} + +const MediaSendChannelInterface* RtpTransceiver::media_send_channel() const { + RTC_DCHECK_RUN_ON(thread_); + return channel_ ? channel_->media_send_channel() : nullptr; +} + +MediaReceiveChannelInterface* RtpTransceiver::media_receive_channel() { + RTC_DCHECK_RUN_ON(thread_); + return channel_ ? channel_->media_receive_channel() : nullptr; +} + +const MediaReceiveChannelInterface* RtpTransceiver::media_receive_channel() + const { + RTC_DCHECK_RUN_ON(thread_); + return channel_ ? channel_->media_receive_channel() : nullptr; +} + +VideoMediaSendChannelInterface* RtpTransceiver::video_media_send_channel() { + // Accessed from multiple threads. + // See https://issues.webrtc.org/475126742 + return channel_ ? channel_->video_media_send_channel() : nullptr; +} + +VoiceMediaSendChannelInterface* RtpTransceiver::voice_media_send_channel() { + // Accessed from multiple threads. + // See https://issues.webrtc.org/475126742 + return channel_ ? channel_->voice_media_send_channel() : nullptr; +} + +VideoMediaReceiveChannelInterface* +RtpTransceiver::video_media_receive_channel() { + // Accessed from multiple threads. + // See https://issues.webrtc.org/475126742 + return channel_ ? channel_->video_media_receive_channel() : nullptr; +} + +VoiceMediaReceiveChannelInterface* +RtpTransceiver::voice_media_receive_channel() { + // Accessed from multiple threads. + // See https://issues.webrtc.org/475126742 + return channel_ ? channel_->voice_media_receive_channel() : nullptr; +} + +void RtpTransceiver::SetTransport(scoped_refptr transport, + std::optional transport_name) { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(HasChannel() || !transport); + RTC_DCHECK((transport && transport_name.has_value()) || + (!transport && !transport_name)); + RTC_DCHECK(!transport_name.has_value() || !transport_name.value().empty()); + transport_name_ = std::move(transport_name); + for (auto& sender : senders_) { + sender->internal()->set_transport(transport); + } + for (auto& receiver : receivers_) { + receiver->internal()->set_transport(transport); + } +} + } // namespace webrtc diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h index 58d13512342..86e76f824fb 100644 --- a/pc/rtp_transceiver.h +++ b/pc/rtp_transceiver.h @@ -13,15 +13,17 @@ #include +#include #include #include +#include #include #include #include +#include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/audio_options.h" #include "api/crypto/crypto_options.h" #include "api/environment/environment.h" @@ -42,6 +44,7 @@ #include "media/base/media_channel.h" #include "media/base/media_config.h" #include "media/base/media_engine.h" +#include "media/base/stream_params.h" #include "pc/channel_interface.h" #include "pc/codec_vendor.h" #include "pc/connection_context.h" @@ -53,11 +56,16 @@ #include "pc/rtp_sender_proxy.h" #include "pc/rtp_transport_internal.h" #include "pc/session_description.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread_annotations.h" namespace webrtc { +class DtlsTransport; + class PeerConnectionSdpMethods; +class ScopedOperationsBatcher; // Implementation of the public RtpTransceiverInterface. // @@ -93,6 +101,8 @@ class RtpTransceiver : public RtpTransceiverInterface { // channel set. // `media_type` specifies the type of RtpTransceiver (and, by transitivity, // the type of senders, receivers, and channel). Can either by audio or video. + // This should be PLAN_B_ONLY; but this marking is deferred due to templating + // issues RtpTransceiver(const Environment& env, MediaType media_type, ConnectionContext* context, @@ -139,10 +149,6 @@ class RtpTransceiver : public RtpTransceiverInterface { RtpTransceiver(RtpTransceiver&&) = delete; RtpTransceiver& operator=(RtpTransceiver&&) = delete; - // Returns the Voice/VideoChannel set for this transceiver. May be null if - // the transceiver is not in the currently set local/remote description. - ChannelInterface* channel() const { return channel_.get(); } - // Creates the Voice/VideoChannel and sets it. RTCError CreateChannel( absl::string_view mid, @@ -203,39 +209,41 @@ class RtpTransceiver : public RtpTransceiverInterface { absl::AnyInvocable GetDeleteChannelWorkerTask(bool stop_senders); // Adds an RtpSender of the appropriate type to be owned by this transceiver. - scoped_refptr> AddSenderPlanB( - scoped_refptr track, - absl::string_view sender_id, - const std::vector& stream_ids, - const std::vector& send_encodings); + PLAN_B_ONLY scoped_refptr> + AddSenderPlanB(scoped_refptr track, + absl::string_view sender_id, + const std::vector& stream_ids, + const std::vector& send_encodings); // Adds an RtpSender of the appropriate type to be owned by this transceiver. // Must not be null. - void AddSenderPlanB( + PLAN_B_ONLY void AddSenderPlanB( scoped_refptr> sender); // Removes the given RtpSender. Returns false if the sender is not owned by // this transceiver. - bool RemoveSenderPlanB(RtpSenderInterface* sender); + PLAN_B_ONLY bool RemoveSenderPlanB(RtpSenderInterface* sender); // Returns a vector of the senders owned by this transceiver. - std::vector>> - senders() const { + PLAN_B_ONLY const + std::vector>>& + senders() const { return senders_; } // Adds an RtpReceiver of the appropriate type to be owned by this // transceiver. Must not be null. - void AddReceiverPlanB( + PLAN_B_ONLY void AddReceiverPlanB( scoped_refptr> receiver); // Removes the given RtpReceiver. Returns false if the receiver is not owned // by this transceiver. - bool RemoveReceiverPlanB(RtpReceiverInterface* receiver); + PLAN_B_ONLY bool RemoveReceiverPlanB(RtpReceiverInterface* receiver); // Returns a vector of the receivers owned by this transceiver. - std::vector>> + PLAN_B_ONLY const std::vector< + scoped_refptr>>& receivers() const { return receivers_; } @@ -256,6 +264,18 @@ class RtpTransceiver : public RtpTransceiverInterface { mline_index_ = mline_index; } + const std::optional& transport_name() const { + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK(!transport_name_ || HasChannel()); + return transport_name_; + } + + // Sets or clears the transport for the sender and receiver. + // This method is assumed to be called in tandem with transport changes being + // applied to the channel. Must be called on the signaling thread. + void SetTransport(scoped_refptr transport, + std::optional transport_name); + // Sets the MID for this transceiver. If the MID is not null, then the // transceiver is considered "associated" with the media section that has the // same MID. @@ -303,13 +323,17 @@ class RtpTransceiver : public RtpTransceiverInterface { // Executes the "stop the RTCRtpTransceiver" procedure from // the webrtc-pc specification, described under the stop() method. - void StopTransceiverProcedure(); + // The task must be executed on the worker thread. + // This is used by SdpOfferAnswerHandler to batch worker thread operations. + [[nodiscard]] absl_nullable absl::AnyInvocable + GetStopTransceiverProcedure(); // RtpTransceiverInterface implementation. MediaType media_type() const override; std::optional mid() const override; - scoped_refptr sender() const override; - scoped_refptr receiver() const override; + absl_nonnull scoped_refptr sender() const override; + absl_nonnull scoped_refptr receiver() const override; + bool stopped() const override; bool stopping() const override; RtpTransceiverDirection direction() const override; @@ -320,7 +344,7 @@ class RtpTransceiver : public RtpTransceiverInterface { bool receptive() const override; RTCError StopStandard() override; void StopInternal() override; - RTCError SetCodecPreferences(ArrayView codecs) override; + RTCError SetCodecPreferences(std::span codecs) override; // TODO(https://crbug.com/webrtc/391275081): Delete codec_preferences() in // favor of filtered_codec_preferences() because it's not used anywhere. std::vector codec_preferences() const override; @@ -334,7 +358,7 @@ class RtpTransceiver : public RtpTransceiverInterface { const override; RTCError SetHeaderExtensionsToNegotiate( - ArrayView header_extensions) override; + std::span header_extensions) override; // Called on the signaling thread when the local or remote content description // is updated. Used to update the negotiated header extensions. @@ -346,17 +370,60 @@ class RtpTransceiver : public RtpTransceiverInterface { void OnNegotiationUpdate(SdpType sdp_type, const MediaContentDescription* content); + // Wrappers for ChannelInterface + bool HasChannel() const { + // Accessed from multiple threads. + // See https://issues.webrtc.org/475126742 + return channel_ != nullptr; + } + + bool SetChannelRtpTransport(RtpTransportInternal* rtp_transport); + // Configures the channel with local content description. + // Pushes a multi-stage execution task into the provided + // `ScopedOperationsBatcher`. See `SetChannelContent` for details on the + // execution model. + void SetChannelLocalContent(const MediaContentDescription* content, + SdpType type, + ScopedOperationsBatcher& batcher); + // Configures the channel with remote content description. + // Pushes a multi-stage execution task into the provided + // `ScopedOperationsBatcher`. See `SetChannelContent` for details on the + // execution model. + void SetChannelRemoteContent(const MediaContentDescription* content, + SdpType type, + ScopedOperationsBatcher& batcher); + void EnableChannel(bool enable); + void ResetUnsignaledRecvStream(); + + const std::vector& channel_local_streams() const; + const std::vector& channel_remote_streams() const; + absl::string_view channel_transport_name() const; + + // Accessors for media channels. These return null if there is no channel. + MediaSendChannelInterface* media_send_channel(); + const MediaSendChannelInterface* media_send_channel() const; + MediaReceiveChannelInterface* media_receive_channel(); + const MediaReceiveChannelInterface* media_receive_channel() const; + VideoMediaSendChannelInterface* video_media_send_channel(); + VoiceMediaSendChannelInterface* voice_media_send_channel(); + VideoMediaReceiveChannelInterface* video_media_receive_channel(); + VoiceMediaReceiveChannelInterface* voice_media_receive_channel(); + private: MediaEngineInterface* media_engine() RTC_RUN_ON(context()->worker_thread()); ConnectionContext* context() const { return context_; } CodecVendor& codec_vendor() { return *codec_lookup_helper_->GetCodecVendor(); } - void OnFirstPacketReceived(); - void OnPacketReceived(scoped_refptr safety) + void OnFirstPacketReceived(uint32_t ssrc); + void OnPacketReceived(uint32_t ssrc, + scoped_refptr safety) RTC_RUN_ON(context()->network_thread()); void OnFirstPacketSent(); - void StopSendingAndReceiving(); + // Stops the receivers synchronously and returns a task that stops the + // senders. The returned task must be executed on the worker thread. + [[nodiscard]] absl_nonnull absl::AnyInvocable + GetStopSendingAndReceiving(); // Tell the senders and receivers about possibly-new media channels // in a newly created `channel_`. void PushNewMediaChannel(); @@ -366,6 +433,9 @@ class RtpTransceiver : public RtpTransceiverInterface { RTC_RUN_ON(context()->worker_thread()); void ClearMediaChannelReferences() RTC_RUN_ON(context()->worker_thread()); + VideoMediaSendChannelInterface::EncoderSwitchRequestCallback + GetEncoderSwitchRequestCallback(); + RTCError UpdateCodecPreferencesCaches( const std::vector& codecs); // Helper function for handling extensions during O/A @@ -373,14 +443,46 @@ class RtpTransceiver : public RtpTransceiverInterface { GetOfferedAndImplementedHeaderExtensions( const MediaContentDescription* content) const; + // Configures the channel with the provided content description. + // Pushes a multi-stage execution task into the provided + // `ScopedOperationsBatcher`. + // + // Signaling Thread Worker Thread + // +-------------------+ +-------------------+ + // | SetChannelContent | | | + // | - Capture SSRC | | | + // | - Push Outer -----|------------->| [Outer Lambda] | + // | | | - Run set_content | + // | | | - GetRtpSendParams| + // | [Inner Lambda] <--|--------------| - Return Inner | + // | - SetCachedParams | | | + // +-------------------+ +-------------------+ + // + // Execution Stages: + // 1. **Preparation (Signaling Thread)**: Called when this method is invoked. + // Captures current sender SSRC and cached parameters. + // + // 2. **Execution (Worker Thread)**: The pushed outer lambda is executed + // by the ScopedOperationsBatcher on the worker thread. Runs + // `set_content()`. If successful, fetches updated `RtpSendParameters` + // from the media channel and returns an inner completion lambda. + // + // 3. **Completion (Signaling Thread)**: The inner lambda returned by the + // worker thread task is collected by `ScopedOperationsBatcher::Run()`. + // After all batched operations on the worker thread are complete, + // `Run()` executes these inner lambdas on the thread that called + // `Run()` (in this case, the Signaling Thread). This stage updates + // the cached parameters on the senders. + void SetChannelContent(absl::AnyInvocable set_content, + ScopedOperationsBatcher& batcher); + const Environment env_; // Enforce that this object is created, used and destroyed on one thread. // This TQ typically represents the signaling thread. TaskQueueBase* const thread_; const bool unified_plan_; const MediaType media_type_; - scoped_refptr signaling_thread_safety_ - RTC_GUARDED_BY(thread_); + scoped_refptr signaling_thread_safety_; scoped_refptr network_thread_safety_; std::vector>> senders_; @@ -393,6 +495,8 @@ class RtpTransceiver : public RtpTransceiverInterface { std::optional current_direction_; std::optional fired_direction_; std::optional mid_; + std::optional transport_name_ RTC_GUARDED_BY(thread_) = + std::nullopt; std::optional mline_index_; bool created_by_addtrack_ = false; bool reused_for_addtrack_ = false; @@ -447,7 +551,7 @@ PROXY_CONSTMETHOD0(std::optional, fired_direction) PROXY_CONSTMETHOD0(bool, receptive) PROXY_METHOD0(RTCError, StopStandard) PROXY_METHOD0(void, StopInternal) -PROXY_METHOD1(RTCError, SetCodecPreferences, ArrayView) +PROXY_METHOD1(RTCError, SetCodecPreferences, std::span) PROXY_CONSTMETHOD0(std::vector, codec_preferences) PROXY_CONSTMETHOD0(std::vector, GetHeaderExtensionsToNegotiate) @@ -455,7 +559,7 @@ PROXY_CONSTMETHOD0(std::vector, GetNegotiatedHeaderExtensions) PROXY_METHOD1(RTCError, SetHeaderExtensionsToNegotiate, - ArrayView) + std::span) END_PROXY_MAP(RtpTransceiver) } // namespace webrtc diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc index 39d2600a6c1..bf09c33d3cd 100644 --- a/pc/rtp_transceiver_unittest.cc +++ b/pc/rtp_transceiver_unittest.cc @@ -39,13 +39,16 @@ #include "media/base/media_channel.h" #include "media/base/media_config.h" #include "media/engine/fake_webrtc_call.h" +#include "p2p/dtls/fake_dtls_transport.h" #include "pc/codec_vendor.h" #include "pc/connection_context.h" +#include "pc/dtls_transport.h" #include "pc/rtp_parameters_conversion.h" #include "pc/rtp_receiver.h" #include "pc/rtp_receiver_proxy.h" #include "pc/rtp_sender.h" #include "pc/rtp_sender_proxy.h" +#include "pc/rtp_transport.h" #include "pc/rtp_transport_internal.h" #include "pc/session_description.h" #include "pc/test/enable_fake_media.h" @@ -123,30 +126,30 @@ TEST_F(RtpTransceiverTest, CannotSetChannelOnStoppedTransceiver) { auto channel1 = std::make_unique>(); EXPECT_CALL(*channel1, media_type()).WillRepeatedly(Return(MediaType::AUDIO)); EXPECT_CALL(*channel1, mid()).WillRepeatedly(ReturnRef(content_name)); - EXPECT_CALL(*channel1, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(*channel1, SetFirstPacketReceivedCallback_n(_)); EXPECT_CALL(*channel1, SetRtpTransport(_)).WillRepeatedly(Return(true)); auto channel1_ptr = channel1.get(); transceiver->SetChannel(std::move(channel1), [&](const std::string& mid) { EXPECT_EQ(mid, content_name); return nullptr; }); - EXPECT_EQ(channel1_ptr, transceiver->channel()); + EXPECT_TRUE(transceiver->HasChannel()); // Stop the transceiver. transceiver->StopInternal(); - EXPECT_EQ(channel1_ptr, transceiver->channel()); + EXPECT_TRUE(transceiver->HasChannel()); auto channel2 = std::make_unique>(); EXPECT_CALL(*channel2, media_type()).WillRepeatedly(Return(MediaType::AUDIO)); // Clear the current channel - required to allow SetChannel() - EXPECT_CALL(*channel1_ptr, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(*channel1_ptr, SetFirstPacketReceivedCallback_n(_)); transceiver->ClearChannel(); - ASSERT_EQ(nullptr, transceiver->channel()); + ASSERT_FALSE(transceiver->HasChannel()); // Channel can no longer be set, so this call should be a no-op. transceiver->SetChannel(std::move(channel2), [](const std::string&) { return nullptr; }); - EXPECT_EQ(nullptr, transceiver->channel()); + EXPECT_FALSE(transceiver->HasChannel()); } // Checks that a channel can be unset on a stopped `RtpTransceiver` @@ -158,24 +161,67 @@ TEST_F(RtpTransceiverTest, CanUnsetChannelOnStoppedTransceiver) { auto channel = std::make_unique>(); EXPECT_CALL(*channel, media_type()).WillRepeatedly(Return(MediaType::VIDEO)); EXPECT_CALL(*channel, mid()).WillRepeatedly(ReturnRef(content_name)); - EXPECT_CALL(*channel, SetFirstPacketReceivedCallback(_)) + EXPECT_CALL(*channel, SetFirstPacketReceivedCallback_n(_)) .WillRepeatedly(testing::Return()); EXPECT_CALL(*channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); - auto channel_ptr = channel.get(); transceiver->SetChannel(std::move(channel), [&](const std::string& mid) { EXPECT_EQ(mid, content_name); return nullptr; }); - EXPECT_EQ(channel_ptr, transceiver->channel()); + EXPECT_TRUE(transceiver->HasChannel()); // Stop the transceiver. transceiver->StopInternal(); - EXPECT_EQ(channel_ptr, transceiver->channel()); + EXPECT_TRUE(transceiver->HasChannel()); // Set the channel to `nullptr`. transceiver->ClearChannel(); - EXPECT_EQ(nullptr, transceiver->channel()); + EXPECT_FALSE(transceiver->HasChannel()); +} + +TEST_F(RtpTransceiverTest, TransportNameIsUpdated) { + const std::string content_name("my_mid"); + auto transceiver = make_ref_counted( + env(), MediaType::AUDIO, context(), codec_lookup_helper(), nullptr); + EXPECT_FALSE(transceiver->transport_name().has_value()); + + auto fake_dtls = std::make_unique("test_transport", false); + auto rtp_transport = std::make_unique(/*rtcp_mux_enabled=*/true, + env().field_trials()); + rtp_transport->SetRtpPacketTransport(fake_dtls.get()); + + transceiver->set_mid(content_name); + auto channel = std::make_unique>(); + EXPECT_CALL(*channel, media_type()).WillRepeatedly(Return(MediaType::AUDIO)); + EXPECT_CALL(*channel, mid()).WillRepeatedly(ReturnRef(content_name)); + EXPECT_CALL(*channel, SetFirstPacketReceivedCallback_n(_)) + .WillRepeatedly(Return()); + EXPECT_CALL(*channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); + + auto result = transceiver->SetChannel( + std::move(channel), [&](const std::string& mid) -> RtpTransportInternal* { + return rtp_transport.get(); + }); + ASSERT_TRUE(result.ok()); + + EXPECT_TRUE(transceiver->HasChannel()); + EXPECT_EQ(transceiver->transport_name(), "test_transport"); + + auto dtls_transport = make_ref_counted(fake_dtls.get()); + transceiver->SetTransport(dtls_transport, "updated_transport"); + EXPECT_EQ(transceiver->transport_name(), "updated_transport"); + + // Setting null transport should clear the name. + transceiver->SetTransport(nullptr, std::nullopt); + EXPECT_EQ(transceiver->transport_name(), std::nullopt); + EXPECT_TRUE(transceiver->HasChannel()); + + // Clearing the channel should reset the transport name to nullopt. + transceiver->SetTransport(dtls_transport, "yadt"); + EXPECT_EQ(transceiver->transport_name(), "yadt"); + transceiver->ClearChannel(); + EXPECT_FALSE(transceiver->transport_name().has_value()); } class RtpTransceiverUnifiedPlanTest : public RtpTransceiverTest { @@ -225,7 +271,10 @@ TEST_F(RtpTransceiverUnifiedPlanTest, StopSetsDirection) { transceiver->StopStandard(); EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver->direction()); EXPECT_FALSE(transceiver->current_direction()); - transceiver->StopTransceiverProcedure(); + auto stop_task = transceiver->GetStopTransceiverProcedure(); + if (stop_task) { + std::move(stop_task)(); + } EXPECT_TRUE(transceiver->current_direction()); EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver->direction()); EXPECT_EQ(RtpTransceiverDirection::kStopped, @@ -899,7 +948,7 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, RtpParameters simulcast_parameters; simulcast_parameters.encodings.resize(2); auto simulcast_sender = MockSender(MediaType::VIDEO); - EXPECT_CALL(*simulcast_sender, GetParametersInternal()) + EXPECT_CALL(*simulcast_sender, GetParametersInternal(_, _)) .WillRepeatedly(Return(simulcast_parameters)); auto simulcast_transceiver = make_ref_counted( env(), @@ -927,7 +976,7 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, svc_parameters.encodings[0].scalability_mode = "L3T3"; auto svc_sender = MockSender(MediaType::VIDEO); - EXPECT_CALL(*svc_sender, GetParametersInternal()) + EXPECT_CALL(*svc_sender, GetParametersInternal(_, _)) .WillRepeatedly(Return(svc_parameters)); auto svc_transceiver = make_ref_counted( env(), @@ -975,16 +1024,15 @@ TEST_F(RtpTransceiverTestWithFakeCall, /*header_extensions=*/std::vector(), /*on_negotiation_needed=*/[] {}); - EXPECT_FALSE(transceiver->channel()); + EXPECT_FALSE(transceiver->HasChannel()); auto error = transceiver->CreateChannel( "0", call_.get(), MediaConfig(), /*srtp_required=*/false, CryptoOptions(), audio_options, VideoOptions(), nullptr, [](absl::string_view) -> RtpTransportInternal* { return nullptr; }); EXPECT_TRUE(error.ok()); - auto* channel = transceiver->channel(); - ASSERT_TRUE(channel); - auto* voice_channel = channel->voice_media_send_channel(); + ASSERT_TRUE(transceiver->HasChannel()); + auto* voice_channel = transceiver->voice_media_send_channel(); ASSERT_TRUE(voice_channel); auto* fake_channel = static_cast(voice_channel); EXPECT_TRUE(fake_channel->options().audio_network_adaptor); diff --git a/pc/rtp_transmission_manager.cc b/pc/rtp_transmission_manager.cc index aec4fa9b6dd..b67a3fe9e7a 100644 --- a/pc/rtp_transmission_manager.cc +++ b/pc/rtp_transmission_manager.cc @@ -52,6 +52,7 @@ #include "rtc_base/checks.h" #include "rtc_base/crypto_random.h" #include "rtc_base/logging.h" +#include "rtc_base/system/plan_b_only.h" namespace webrtc { @@ -132,38 +133,34 @@ void RtpTransmissionManager::RunWithObserver( std::move(task)(observer_); } -VoiceMediaSendChannelInterface* +PLAN_B_ONLY VoiceMediaSendChannelInterface* RtpTransmissionManager::voice_media_send_channel() const { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); - auto* voice_channel = GetAudioTransceiver()->internal()->channel(); - return voice_channel ? voice_channel->voice_media_send_channel() : nullptr; + return GetAudioTransceiver()->internal()->voice_media_send_channel(); } -VideoMediaSendChannelInterface* +PLAN_B_ONLY VideoMediaSendChannelInterface* RtpTransmissionManager::video_media_send_channel() const { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); - auto* video_channel = GetVideoTransceiver()->internal()->channel(); - return video_channel ? video_channel->video_media_send_channel() : nullptr; + return GetVideoTransceiver()->internal()->video_media_send_channel(); } -VoiceMediaReceiveChannelInterface* +PLAN_B_ONLY VoiceMediaReceiveChannelInterface* RtpTransmissionManager::voice_media_receive_channel() const { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); - auto* voice_channel = GetAudioTransceiver()->internal()->channel(); - return voice_channel ? voice_channel->voice_media_receive_channel() : nullptr; + return GetAudioTransceiver()->internal()->voice_media_receive_channel(); } -VideoMediaReceiveChannelInterface* +PLAN_B_ONLY VideoMediaReceiveChannelInterface* RtpTransmissionManager::video_media_receive_channel() const { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); - auto* video_channel = GetVideoTransceiver()->internal()->channel(); - return video_channel ? video_channel->video_media_receive_channel() : nullptr; + return GetVideoTransceiver()->internal()->video_media_receive_channel(); } -RTCErrorOr> +PLAN_B_ONLY RTCErrorOr> RtpTransmissionManager::AddTrackPlanB( scoped_refptr track, const std::vector& stream_ids, @@ -171,9 +168,9 @@ RtpTransmissionManager::AddTrackPlanB( RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); if (stream_ids.size() > 1u) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, - "AddTrack with more than one stream is not " - "supported with Plan B semantics."); + return LOG_ERROR(RTCError::UnsupportedOperation() + << "AddTrack with more than one stream is not supported " + "with Plan B semantics."); } std::vector adjusted_stream_ids; if (stream_ids.empty()) { @@ -226,8 +223,8 @@ RtpTransmissionManager::AddTrackUnifiedPlan( << MediaTypeToString(transceiver->media_type()) << " transceiver for AddTrack."; if (transceiver->stopping()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "The existing transceiver is stopping."); + return LOG_ERROR(RTCError::InvalidParameter() + << "The existing transceiver is stopping."); } if (transceiver->direction() == RtpTransceiverDirection::kRecvOnly) { @@ -287,11 +284,13 @@ RtpTransmissionManager::CreateAndAddTransceiver( RTC_DCHECK(!FindSenderById(sender_id)); std::vector header_extensions = std::move(header_extensions_to_negotiate); - if (env_.field_trials().IsEnabled("WebRTC-HeaderExtensionNegotiateMemory")) { + if (!env_.field_trials().IsDisabled( + "WebRTC-HeaderExtensionNegotiateMemory")) { // If we have already negotiated header extensions for this type, + // and it is not stopped, // reuse the negotiated state for new transceivers of the same type. for (const auto& transceiver : transceivers()->List()) { - if (transceiver->media_type() == media_type) { + if (transceiver->media_type() == media_type && !transceiver->stopping()) { header_extensions = transceiver->GetHeaderExtensionsToNegotiate(); break; } @@ -351,7 +350,9 @@ RtpTransmissionManager::GetSendersInternal() const { if (IsUnifiedPlan() && transceiver->internal()->stopped()) continue; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() auto senders = transceiver->internal()->senders(); + RTC_ALLOW_PLAN_B_DEPRECATION_END() all_senders.insert(all_senders.end(), senders.begin(), senders.end()); } return all_senders; @@ -366,14 +367,16 @@ RtpTransmissionManager::GetReceiversInternal() const { if (IsUnifiedPlan() && transceiver->internal()->stopped()) continue; + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() auto receivers = transceiver->internal()->receivers(); + RTC_ALLOW_PLAN_B_DEPRECATION_END() all_receivers.insert(all_receivers.end(), receivers.begin(), receivers.end()); } return all_receivers; } -scoped_refptr> +PLAN_B_ONLY scoped_refptr> RtpTransmissionManager::GetAudioTransceiver() const { RTC_DCHECK_RUN_ON(signaling_thread()); // This method only works with Plan B SDP, where there is a single @@ -388,7 +391,7 @@ RtpTransmissionManager::GetAudioTransceiver() const { return nullptr; } -scoped_refptr> +PLAN_B_ONLY scoped_refptr> RtpTransmissionManager::GetVideoTransceiver() const { RTC_DCHECK_RUN_ON(signaling_thread()); // This method only works with Plan B SDP, where there is a single @@ -403,8 +406,9 @@ RtpTransmissionManager::GetVideoTransceiver() const { return nullptr; } -void RtpTransmissionManager::AddTrackPlanB(MediaStreamTrackInterface* track, - MediaStreamInterface* stream) { +PLAN_B_ONLY void RtpTransmissionManager::AddTrackPlanB( + MediaStreamTrackInterface* track, + MediaStreamInterface* stream) { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(track); RTC_DCHECK(stream); @@ -443,8 +447,9 @@ void RtpTransmissionManager::AddTrackPlanB(MediaStreamTrackInterface* track, // TODO(deadbeef): Don't destroy RtpSenders here; they should be kept around // indefinitely, when we have unified plan SDP. -void RtpTransmissionManager::RemoveTrackPlanB(MediaStreamTrackInterface* track, - MediaStreamInterface* stream) { +PLAN_B_ONLY void RtpTransmissionManager::RemoveTrackPlanB( + MediaStreamTrackInterface* track, + MediaStreamInterface* stream) { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); auto sender = FindSenderForTrack(track); @@ -459,7 +464,7 @@ void RtpTransmissionManager::RemoveTrackPlanB(MediaStreamTrackInterface* track, transceiver->RemoveSenderPlanB(sender.get()); } -void RtpTransmissionManager::CreateAudioReceiverPlanB( +PLAN_B_ONLY void RtpTransmissionManager::CreateAudioReceiverPlanB( MediaStreamInterface* stream, const RtpSenderInfo& remote_sender_info) { RTC_DCHECK(!IsUnifiedPlan()); @@ -471,11 +476,11 @@ void RtpTransmissionManager::CreateAudioReceiverPlanB( auto audio_receiver = make_ref_counted( worker_thread(), remote_sender_info.sender_id, streams, false, voice_media_receive_channel()); - if (remote_sender_info.sender_id == kDefaultAudioSenderId) { - audio_receiver->SetupUnsignaledMediaChannel(); - } else { - audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); - } + auto task = (remote_sender_info.sender_id == kDefaultAudioSenderId) + ? audio_receiver->GetSetupForUnsignaledMediaChannel() + : audio_receiver->GetSetupForMediaChannel( + remote_sender_info.first_ssrc); + worker_thread()->BlockingCall([&]() mutable { std::move(task)(); }); auto receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), worker_thread(), std::move(audio_receiver)); @@ -485,7 +490,7 @@ void RtpTransmissionManager::CreateAudioReceiverPlanB( NoteUsageEvent(UsageEvent::AUDIO_ADDED); } -void RtpTransmissionManager::CreateVideoReceiverPlanB( +PLAN_B_ONLY void RtpTransmissionManager::CreateVideoReceiverPlanB( MediaStreamInterface* stream, const RtpSenderInfo& remote_sender_info) { RTC_DCHECK(!IsUnifiedPlan()); @@ -497,11 +502,12 @@ void RtpTransmissionManager::CreateVideoReceiverPlanB( auto video_receiver = make_ref_counted( worker_thread(), remote_sender_info.sender_id, streams); - video_receiver->SetupMediaChannel( + auto task = video_receiver->GetSetupForMediaChannel( remote_sender_info.sender_id == kDefaultVideoSenderId ? std::nullopt : std::optional(remote_sender_info.first_ssrc), video_media_receive_channel()); + worker_thread()->BlockingCall([&]() mutable { std::move(task)(); }); auto receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), worker_thread(), std::move(video_receiver)); @@ -513,7 +519,7 @@ void RtpTransmissionManager::CreateVideoReceiverPlanB( // TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote // description. -scoped_refptr +PLAN_B_ONLY scoped_refptr RtpTransmissionManager::RemoveAndStopReceiver( const RtpSenderInfo& remote_sender_info) { RTC_DCHECK(!IsUnifiedPlan()); @@ -531,7 +537,7 @@ RtpTransmissionManager::RemoveAndStopReceiver( return receiver; } -void RtpTransmissionManager::OnRemoteSenderAddedPlanB( +PLAN_B_ONLY void RtpTransmissionManager::OnRemoteSenderAddedPlanB( const RtpSenderInfo& sender_info, MediaStreamInterface* stream, MediaType media_type) { @@ -550,7 +556,7 @@ void RtpTransmissionManager::OnRemoteSenderAddedPlanB( } } -void RtpTransmissionManager::OnRemoteSenderRemovedPlanB( +PLAN_B_ONLY void RtpTransmissionManager::OnRemoteSenderRemovedPlanB( const RtpSenderInfo& sender_info, MediaStreamInterface* stream, MediaType media_type) { @@ -590,7 +596,7 @@ void RtpTransmissionManager::OnRemoteSenderRemovedPlanB( } } -void RtpTransmissionManager::OnLocalSenderAdded( +PLAN_B_ONLY void RtpTransmissionManager::OnLocalSenderAdded( const RtpSenderInfo& sender_info, MediaType media_type) { RTC_DCHECK_RUN_ON(signaling_thread()); @@ -613,7 +619,7 @@ void RtpTransmissionManager::OnLocalSenderAdded( sender->internal()->SetSsrc(sender_info.first_ssrc); } -void RtpTransmissionManager::OnLocalSenderRemoved( +PLAN_B_ONLY void RtpTransmissionManager::OnLocalSenderRemoved( const RtpSenderInfo& sender_info, MediaType media_type) { RTC_DCHECK_RUN_ON(signaling_thread()); @@ -655,11 +661,13 @@ RtpTransmissionManager::FindSenderForTrack( MediaStreamTrackInterface* track) const { RTC_DCHECK_RUN_ON(signaling_thread()); for (const auto& transceiver : transceivers_.List()) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (auto sender : transceiver->internal()->senders()) { if (sender->track() == track) { return sender; } } + RTC_ALLOW_PLAN_B_DEPRECATION_END() } return nullptr; } @@ -668,16 +676,22 @@ scoped_refptr> RtpTransmissionManager::FindSenderById(absl::string_view sender_id) const { RTC_DCHECK_RUN_ON(signaling_thread()); for (const auto& transceiver : transceivers_.List()) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); + // Under Unified Plan, senders() always has exactly one entry, + // and one can use sender() not senders(). + // Since this function is used both in Plan B and Unified, this is + // left as-is for now. for (auto sender : transceiver->internal()->senders()) { if (sender->id() == sender_id) { return sender; } } + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } return nullptr; } -scoped_refptr> +PLAN_B_ONLY scoped_refptr> RtpTransmissionManager::FindReceiverById(absl::string_view receiver_id) const { RTC_DCHECK_RUN_ON(signaling_thread()); for (const auto& transceiver : transceivers_.List()) { diff --git a/pc/rtp_transmission_manager.h b/pc/rtp_transmission_manager.h index 4c5d56bfabe..cd2d914ce5d 100644 --- a/pc/rtp_transmission_manager.h +++ b/pc/rtp_transmission_manager.h @@ -45,6 +45,7 @@ #include "pc/rtp_transceiver.h" #include "pc/transceiver_list.h" #include "pc/usage_pattern.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" #include "rtc_base/unique_id_generator.h" @@ -123,49 +124,49 @@ class RtpTransmissionManager : public RtpSenderBase::SetStreamsObserver { GetReceiversInternal() const; // Plan B: Get the transceiver containing all audio senders and receivers - scoped_refptr> + PLAN_B_ONLY scoped_refptr> GetAudioTransceiver() const; // Plan B: Get the transceiver containing all video senders and receivers - scoped_refptr> + PLAN_B_ONLY scoped_refptr> GetVideoTransceiver() const; // Plan B: Add an audio/video track, reusing or creating the sender. - void AddTrackPlanB(MediaStreamTrackInterface* track, - MediaStreamInterface* stream); + PLAN_B_ONLY void AddTrackPlanB(MediaStreamTrackInterface* track, + MediaStreamInterface* stream); // Plan B: Remove an audio/video track, removing the sender. - void RemoveTrackPlanB(MediaStreamTrackInterface* track, - MediaStreamInterface* stream); + PLAN_B_ONLY void RemoveTrackPlanB(MediaStreamTrackInterface* track, + MediaStreamInterface* stream); // Triggered when a remote sender has been seen for the first time in a remote // session description. It creates a remote MediaStreamTrackInterface // implementation and triggers CreateAudioReceiverPlanB or // CreateVideoReceiverPlanB. - void OnRemoteSenderAddedPlanB(const RtpSenderInfo& sender_info, - MediaStreamInterface* stream, - MediaType media_type); + PLAN_B_ONLY void OnRemoteSenderAddedPlanB(const RtpSenderInfo& sender_info, + MediaStreamInterface* stream, + MediaType media_type); // Triggered when a remote sender has been removed from a remote session // description. It removes the remote sender with id `sender_id` from a remote // MediaStream and triggers DestroyAudioReceiver or DestroyVideoReceiver. - void OnRemoteSenderRemovedPlanB(const RtpSenderInfo& sender_info, - MediaStreamInterface* stream, - MediaType media_type); + PLAN_B_ONLY void OnRemoteSenderRemovedPlanB(const RtpSenderInfo& sender_info, + MediaStreamInterface* stream, + MediaType media_type); // Triggered when a local sender has been seen for the first time in a local // session description. // This method triggers CreateAudioSender or CreateVideoSender if the rtp // streams in the local SessionDescription can be mapped to a MediaStreamTrack // in a MediaStream in `local_streams_` - void OnLocalSenderAdded(const RtpSenderInfo& sender_info, - MediaType media_type); + PLAN_B_ONLY void OnLocalSenderAdded(const RtpSenderInfo& sender_info, + MediaType media_type); // Triggered when a local sender has been removed from a local session // description. // This method triggers DestroyAudioSender or DestroyVideoSender if a stream // has been removed from the local SessionDescription and the stream can be // mapped to a MediaStreamTrack in a MediaStream in `local_streams_`. - void OnLocalSenderRemoved(const RtpSenderInfo& sender_info, - MediaType media_type); + PLAN_B_ONLY void OnLocalSenderRemoved(const RtpSenderInfo& sender_info, + MediaType media_type); std::vector* GetRemoteSenderInfos(MediaType media_type); std::vector* GetLocalSenderInfos(MediaType media_type); @@ -179,7 +180,7 @@ class RtpTransmissionManager : public RtpSenderBase::SetStreamsObserver { absl::string_view sender_id) const; // Return the RtpReceiver with the given id, or null if none exists. - scoped_refptr> + PLAN_B_ONLY scoped_refptr> FindReceiverById(absl::string_view receiver_id) const; TransceiverList* transceivers() { return &transceivers_; } @@ -187,12 +188,14 @@ class RtpTransmissionManager : public RtpSenderBase::SetStreamsObserver { // Plan B helpers for getting the voice/video media channels for the single // audio/video transceiver, if it exists. - VoiceMediaSendChannelInterface* voice_media_send_channel() const; - VideoMediaSendChannelInterface* video_media_send_channel() const; - VoiceMediaReceiveChannelInterface* voice_media_receive_channel() const; - VideoMediaReceiveChannelInterface* video_media_receive_channel() const; - - RTCErrorOr> AddTrackPlanB( + PLAN_B_ONLY VoiceMediaSendChannelInterface* voice_media_send_channel() const; + PLAN_B_ONLY VideoMediaSendChannelInterface* video_media_send_channel() const; + PLAN_B_ONLY VoiceMediaReceiveChannelInterface* voice_media_receive_channel() + const; + PLAN_B_ONLY VideoMediaReceiveChannelInterface* video_media_receive_channel() + const; + + PLAN_B_ONLY RTCErrorOr> AddTrackPlanB( scoped_refptr track, const std::vector& stream_ids, const std::vector* init_send_encodings); @@ -226,15 +229,15 @@ class RtpTransmissionManager : public RtpSenderBase::SetStreamsObserver { const std::vector* init_send_encodings); // Create an RtpReceiver that sources an audio track. - void CreateAudioReceiverPlanB(MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) - RTC_RUN_ON(signaling_thread()); + PLAN_B_ONLY void CreateAudioReceiverPlanB( + MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) RTC_RUN_ON(signaling_thread()); // Create an RtpReceiver that sources a video track. - void CreateVideoReceiverPlanB(MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) - RTC_RUN_ON(signaling_thread()); - scoped_refptr RemoveAndStopReceiver( + PLAN_B_ONLY void CreateVideoReceiverPlanB( + MediaStreamInterface* stream, + const RtpSenderInfo& remote_sender_info) RTC_RUN_ON(signaling_thread()); + PLAN_B_ONLY scoped_refptr RemoveAndStopReceiver( const RtpSenderInfo& remote_sender_info) RTC_RUN_ON(signaling_thread()); void RunWithObserver( diff --git a/pc/rtp_transport.cc b/pc/rtp_transport.cc index d49678c7bac..086ea728698 100644 --- a/pc/rtp_transport.cc +++ b/pc/rtp_transport.cc @@ -10,13 +10,21 @@ #include "pc/rtp_transport.h" +#include +#include +#include #include #include #include #include #include +#include -#include "api/array_view.h" +#include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" +#include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/transport/ecn_marking.h" @@ -24,6 +32,7 @@ #include "call/rtp_demuxer.h" #include "media/base/rtp_utils.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "p2p/base/packet_transport_internal.h" #include "pc/session_description.h" @@ -39,6 +48,41 @@ #include "rtc_base/trace_event.h" namespace webrtc { +namespace { + +void RemoveExtensionMapForMid( + absl::string_view mid, + std::vector>& extensions) { + auto it = std::find_if(extensions.begin(), extensions.end(), + [mid](const auto& kv) { return kv.first == mid; }); + if (it != extensions.end()) { + extensions.erase(it); + } +} + +RTCError VerifyExtensionIds(const RtpHeaderExtensions& extensions) { + using ExtensionsUsed = std::bitset<1 + RtpExtension::kMaxId>; + ExtensionsUsed id_used; + for (const auto& extension : extensions) { + if (extension.id == 0) { + continue; + } + if (extension.id < RtpExtension::kMinId || + extension.id > RtpExtension::kMaxId) { + return RTCError::InvalidParameter() + << "Bad extension ID: " << extension.ToString(); + } + ExtensionsUsed::reference entry = id_used[extension.id]; + if (entry) { + return RTCError::InvalidParameter() + << "Duplicate extension ID: " << extension.ToString(); + } + entry = true; + } + return RTCError::OK(); +} + +} // namespace void RtpTransport::SetRtcpMuxEnabled(bool enable) { rtcp_mux_enabled_ = enable; @@ -185,14 +229,118 @@ bool RtpTransport::SendPacket(bool rtcp, return true; } -void RtpTransport::UpdateRtpHeaderExtensionMap( - const RtpHeaderExtensions& header_extensions) { - header_extension_map_ = RtpHeaderExtensionMap(header_extensions); +RTCError RtpTransport::VerifyRtpHeaderExtensionMap( + const RtpHeaderExtensions& extensions) const { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + + RTCError error = VerifyExtensionIds(extensions); + if (!error.ok()) { + return error; + } + + for (const auto& new_extension : extensions) { + if (new_extension.id == 0) { + continue; + } + // TODO: bugs.webrtc.org/503013383 - Introduce checking against IDs that are + // currently not present in the SDP, but have been used in previous + // negotiation rounds. Reusing extensions with a different ID is a protocol + // violation, but we cannot check this until we check against the same + // protocol violation on the sender side. + for (const auto& [mid, active_extensions] : header_extensions_by_mid_) { + auto it = absl::c_find_if( + active_extensions, + [&](const RtpExtension& ext) { return ext.id == new_extension.id; }); + if (it != active_extensions.end() && it->uri != new_extension.uri) { + return RTCError::InvalidParameter() + << "RTP extension ID reassignment not supported (collision on " + "active MID " + << mid << ", id=" << new_extension.id << ", old_uri=\"" + << it->uri << "\", new_uri=\"" << new_extension.uri << "\")."; + } + } + } + + return RTCError::OK(); +} + +RTCError RtpTransport::RegisterRtpHeaderExtensionMap( + absl::string_view mid, + const RtpHeaderExtensions& extensions) { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + + RTCError error = VerifyRtpHeaderExtensionMap(extensions); + if (!error.ok()) { + return error; + } + + auto existing_extensions = + absl::c_find_if(header_extensions_by_mid_, + [mid](const auto& kv) { return kv.first == mid; }); + if (existing_extensions != header_extensions_by_mid_.end() && + existing_extensions->second == extensions) { + return RTCError::OK(); + } + + RemoveExtensionMapForMid(mid, header_extensions_by_mid_); + header_extensions_by_mid_.emplace_back(std::string(mid), extensions); + + RebuildMergedMap(); + return RTCError::OK(); +} + +void RtpTransport::UnregisterRtpHeaderExtensionMap(absl::string_view mid) { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + RemoveExtensionMapForMid(mid, header_extensions_by_mid_); + + RebuildMergedMap(); +} + +void RtpTransport::RebuildMergedMap() { + RTC_DCHECK_RUN_ON(&network_thread_checker_); + RtpHeaderExtensionMap merged_map; + + // RFC 8843 (BUNDLE) Section 7.1.3 requires that the same local identifier + // MUST be used for a given RTP header extension across all m-sections in a + // BUNDLE group. + // + // However, during negotiation, we may encounter transient states with + // conflicting IDs. This can occur with buggy endpoints or during "forked" + // signaling (e.g., an Offer followed by a PR-Answer from one endpoint, + // then a final Answer from a different endpoint). While most browsers + // send identical extension sets, different endpoints ringing simultaneously + // could theoretically provide differing maps. + // + // To handle this gracefully, we merge the maps. Because + // `header_extensions_by_mid_` preserves registration order, we iterate in + // reverse (newest first). This ensures tie-breaking is deterministic: + // more recently registered or updated MIDs (like those in a final Answer) + // take precedence over older or provisional ones. + + for (auto rit = header_extensions_by_mid_.rbegin(); + rit != header_extensions_by_mid_.rend(); ++rit) { + for (const auto& extension : rit->second) { + if (extension.id == RtpHeaderExtensionMap::kInvalidId) { + continue; + } + // Only register if the ID is not already in use. + RTPExtensionType type = merged_map.GetType(extension.id); + if (type == kRtpExtensionNone) { + merged_map.RegisterByUri(extension.id, extension.uri); + } + } + } + header_extension_map_ = std::move(merged_map); +} + +void RtpTransport::SetActivePayloadTypeDemuxing(bool enabled) { + rtp_demuxer_.set_use_payload_type_demuxing(enabled); } bool RtpTransport::RegisterRtpDemuxerSink(const RtpDemuxerCriteria& criteria, RtpPacketSinkInterface* sink) { rtp_demuxer_.RemoveSink(sink); + if (!rtp_demuxer_.AddSink(criteria, sink)) { RTC_LOG(LS_ERROR) << "Failed to register the sink for RTP demuxer."; return false; diff --git a/pc/rtp_transport.h b/pc/rtp_transport.h index 2ea5b00339e..185201586cf 100644 --- a/pc/rtp_transport.h +++ b/pc/rtp_transport.h @@ -17,8 +17,13 @@ #include #include #include +#include +#include +#include "absl/strings/string_view.h" #include "api/field_trials_view.h" +#include "api/rtc_error.h" +#include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/transport/ecn_marking.h" #include "api/units/timestamp.h" @@ -35,6 +40,8 @@ #include "rtc_base/network/sent_packet.h" #include "rtc_base/network_route.h" #include "rtc_base/socket.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -86,14 +93,27 @@ class RtpTransport : public RtpTransportInternal { bool IsSrtpActive() const override { return false; } - void UpdateRtpHeaderExtensionMap( - const RtpHeaderExtensions& header_extensions) override; + RTCError VerifyRtpHeaderExtensionMap( + const RtpHeaderExtensions& extensions) const override; + + RTCError RegisterRtpHeaderExtensionMap( + absl::string_view mid, + const RtpHeaderExtensions& extensions) override; + + // Currently only used for testing. In production, unregistration isn't needed + // because leaving the registered extensions in `RtpTransport` is harmless + // when a channel/transceiver is stopped or disconnected. The negotiated + // extension IDs are typically stable for the lifetime of the transport, and + // the transport itself will be destroyed when the PeerConnection is closed. + void UnregisterRtpHeaderExtensionMap(absl::string_view mid) override; bool RegisterRtpDemuxerSink(const RtpDemuxerCriteria& criteria, RtpPacketSinkInterface* sink) override; bool UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) override; + void SetActivePayloadTypeDemuxing(bool enabled) override; + protected: // These methods will be used in the subclasses. void DemuxPacket(CopyOnWriteBuffer packet, @@ -131,6 +151,8 @@ class RtpTransport : public RtpTransportInternal { bool IsTransportWritable(); + void RebuildMergedMap() RTC_RUN_ON(network_thread_checker_); + bool rtcp_mux_enabled_; PacketTransportInternal* rtp_packet_transport_ = nullptr; @@ -146,9 +168,22 @@ class RtpTransport : public RtpTransportInternal { RtpDemuxer rtp_demuxer_; // Used for identifying the MID for RtpDemuxer. - RtpHeaderExtensionMap header_extension_map_; + RtpHeaderExtensionMap header_extension_map_ + RTC_GUARDED_BY(network_thread_checker_); + // Stores the registered RTP header extensions by MID. + // We use a std::vector to preserve the chronological registration order. + // In BUNDLE scenarios, RFC 8843 requires consistent extension IDs across + // all MIDs. If different MIDs request the same ID for different URIs, + // we resolve the conflict by giving precedence to the most recently + // registered MID. Preserving the insertion order allows + // UnregisterRtpHeaderExtensionMap to correctly fall back to the newest + // remaining registration when rebuilding the map. + std::vector> + header_extensions_by_mid_ RTC_GUARDED_BY(network_thread_checker_); + // Guard against recursive "ready to send" signals bool processing_ready_to_send_ = false; + RTC_NO_UNIQUE_ADDRESS SequenceChecker network_thread_checker_; ScopedTaskSafety safety_; }; diff --git a/pc/rtp_transport_internal.h b/pc/rtp_transport_internal.h index a6df696f316..bb4b66961b6 100644 --- a/pc/rtp_transport_internal.h +++ b/pc/rtp_transport_internal.h @@ -16,6 +16,8 @@ #include #include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "api/rtc_error.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/transport/ecn_marking.h" #include "api/units/timestamp.h" @@ -133,8 +135,14 @@ class RtpTransportInternal { // UpdateSendEncryptedHeaderExtensionIds, // UpdateRecvEncryptedHeaderExtensionIds, // CacheRtpAbsSendTimeHeaderExtension, - virtual void UpdateRtpHeaderExtensionMap( - const RtpHeaderExtensions& header_extensions) = 0; + virtual RTCError RegisterRtpHeaderExtensionMap( + absl::string_view mid, + const RtpHeaderExtensions& extensions) = 0; + + virtual RTCError VerifyRtpHeaderExtensionMap( + const RtpHeaderExtensions& extensions) const = 0; + + virtual void UnregisterRtpHeaderExtensionMap(absl::string_view mid) = 0; virtual bool IsSrtpActive() const = 0; @@ -143,6 +151,8 @@ class RtpTransportInternal { virtual bool UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) = 0; + virtual void SetActivePayloadTypeDemuxing(bool enabled) = 0; + protected: void SendReadyToSend(bool arg) { callback_list_ready_to_send_.Send(arg); } void SendRtcpPacketReceived(CopyOnWriteBuffer packet, diff --git a/pc/rtp_transport_unittest.cc b/pc/rtp_transport_unittest.cc index 4b1b4099d60..07430e64274 100644 --- a/pc/rtp_transport_unittest.cc +++ b/pc/rtp_transport_unittest.cc @@ -14,20 +14,24 @@ #include #include +#include "api/rtc_error.h" +#include "api/rtp_parameters.h" #include "api/test/rtc_error_matchers.h" #include "api/transport/ecn_marking.h" #include "api/units/time_delta.h" #include "call/rtp_demuxer.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "p2p/base/packet_transport_internal.h" #include "p2p/test/fake_packet_transport.h" +#include "pc/session_description.h" #include "pc/test/rtp_transport_test_util.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/buffer.h" #include "rtc_base/containers/flat_set.h" #include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/logging.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/network_route.h" -#include "rtc_base/thread.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" @@ -40,6 +44,7 @@ namespace { using ::testing::Eq; using ::testing::Ge; +using ::testing::HasSubstr; constexpr bool kMuxDisabled = false; constexpr bool kMuxEnabled = true; @@ -234,7 +239,7 @@ TEST(RtpTransportTest, SetRtcpTransportWithNetworkRouteChanged) { TEST(RtpTransportTest, RtcpPacketSentOverCorrectTransport) { // If the RTCP-mux is not enabled, RTCP packets are expected to be sent over // the RtcpPacketTransport. - AutoThread thread; + test::RunLoop thread; RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); FakePacketTransport fake_rtcp("fake_rtcp"); FakePacketTransport fake_rtp("fake_rtp"); @@ -284,10 +289,133 @@ TEST(RtpTransportTest, ChangingReadyToSendStateOnlySignalsWhenChanged) { EXPECT_EQ(observer.ready_to_send_signal_count(), 2); } +TEST(RtpTransportTest, RegisterAndUnregisterRtpHeaderExtensionMap) { + test::RunLoop thread; + RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); + RtpHeaderExtensions extensions1 = { + RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 1)}; + RtpHeaderExtensions extensions2 = { + RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 1), + RtpExtension("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", + 2)}; + + // Register the first map. + transport.RegisterRtpHeaderExtensionMap("audio", extensions1); + + // Parse a packet with an extension from the first map. + const unsigned char kRtpData1[] = {0x90, 0x11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xBE, 0xDE, 0, 1, + // ID=1, len=0 (1 byte payload) + 0x10, 0x00, 0x00, 0x00}; + CopyOnWriteBuffer rtp_packet1(kRtpData1, sizeof(kRtpData1)); + RtpPacketReceived parsed_packet1; + FakePacketTransport fake_rtp("fake_rtp"); + fake_rtp.SetDestination(&fake_rtp, true); + transport.SetRtpPacketTransport(&fake_rtp); + TransportObserver observer(&transport); + + // Register sink so the packet can be "demuxed" and surfaced to the observer. + RtpDemuxerCriteria demuxer_criteria; + demuxer_criteria.payload_types().insert(0x11); + transport.RegisterRtpDemuxerSink(demuxer_criteria, &observer); + + // Send the packet. + fake_rtp.SendPacket(rtp_packet1.data(), rtp_packet1.size(), + AsyncSocketPacketOptions(), 0); + EXPECT_THAT(WaitUntil([&] { return observer.rtp_count(); }, Eq(1)), + IsRtcOk()); + RTC_LOG(LS_INFO) << "Packet 1 received: " << observer.rtp_count(); + RTC_LOG(LS_INFO) + << "Packet 1 has AudioLevelExtension: " + << observer.last_recv_rtp_packet().HasExtension(); + EXPECT_TRUE( + observer.last_recv_rtp_packet().HasExtension()); + + // Register the second map (simulating BUNDLE). + transport.RegisterRtpHeaderExtensionMap("video", extensions2); + + // Parse a packet with an extension from the second map. + const unsigned char kRtpData2[] = {0x90, 0x11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xBE, 0xDE, 0, 1, + // ID=2, len=2 (3 byte payload) + 0x22, 0x01, 0x02, 0x03}; + CopyOnWriteBuffer rtp_packet2(kRtpData2, sizeof(kRtpData2)); + + fake_rtp.SendPacket(rtp_packet2.data(), rtp_packet2.size(), + AsyncSocketPacketOptions(), 0); + EXPECT_THAT(WaitUntil([&] { return observer.rtp_count(); }, Eq(2)), + IsRtcOk()); + RTC_LOG(LS_INFO) << "Packet 2 received: " << observer.rtp_count(); + RTC_LOG(LS_INFO) + << "Packet 2 has AbsoluteSendTime: " + << observer.last_recv_rtp_packet().HasExtension(); + EXPECT_TRUE(observer.last_recv_rtp_packet().HasExtension()); + + // Unregister the second map. + transport.UnregisterRtpHeaderExtensionMap("video"); + + // A packet with the second map's extension should no longer parse the + // extension. + fake_rtp.SendPacket(rtp_packet2.data(), rtp_packet2.size(), + AsyncSocketPacketOptions(), 0); + EXPECT_THAT(WaitUntil([&] { return observer.rtp_count(); }, Eq(3)), + IsRtcOk()); + EXPECT_FALSE( + observer.last_recv_rtp_packet().HasExtension()); + + transport.UnregisterRtpDemuxerSink(&observer); +} + +TEST(RtpTransportTest, VerifyRtpHeaderExtensionMapRejectsIdReassignment) { + test::RunLoop loop; + RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); + RtpHeaderExtensions extensions1 = { + RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 1)}; + RtpHeaderExtensions extensions2 = {RtpExtension( + "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", 1)}; + + // Registering the first map should succeed. + EXPECT_TRUE( + transport.RegisterRtpHeaderExtensionMap("audio", extensions1).ok()); + + // Verifying a map that tries to reassign ID 1 to a different URI should fail. + RTCError error = transport.VerifyRtpHeaderExtensionMap(extensions2); + EXPECT_FALSE(error.ok()); + EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER); + EXPECT_THAT(error.message(), HasSubstr("RTP extension ID reassignment")); + + // Registering the second map should also fail. + error = transport.RegisterRtpHeaderExtensionMap("video", extensions2); + EXPECT_FALSE(error.ok()); + EXPECT_EQ(error.type(), RTCErrorType::INVALID_PARAMETER); +} + +TEST(RtpTransportTest, + VerifyRtpHeaderExtensionMapAllowsIdReuseAfterUnregister) { + test::RunLoop loop; + RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); + RtpHeaderExtensions extensions1 = { + RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 1)}; + RtpHeaderExtensions extensions2 = {RtpExtension( + "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", 1)}; + + // Registering the first map should succeed. + EXPECT_TRUE( + transport.RegisterRtpHeaderExtensionMap("audio", extensions1).ok()); + + // Unregister the first map. + transport.UnregisterRtpHeaderExtensionMap("audio"); + + // Registering the second map with same ID but different URI should now + // succeed! + EXPECT_TRUE( + transport.RegisterRtpHeaderExtensionMap("video", extensions2).ok()); +} + // Test that SignalPacketReceived fires with rtcp=true when a RTCP packet is // received. TEST(RtpTransportTest, SignalDemuxedRtcp) { - AutoThread thread; + test::RunLoop thread; RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); FakePacketTransport fake_rtp("fake_rtp"); fake_rtp.SetDestination(&fake_rtp, true); @@ -311,7 +439,7 @@ const int kRtpLen = 12; // Test that SignalPacketReceived fires with rtcp=false when a RTP packet with a // handled payload type is received. TEST(RtpTransportTest, SignalHandledRtpPayloadType) { - AutoThread thread; + test::RunLoop thread; RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); FakePacketTransport fake_rtp("fake_rtp"); fake_rtp.SetDestination(&fake_rtp, true); @@ -336,7 +464,7 @@ TEST(RtpTransportTest, SignalHandledRtpPayloadType) { } TEST(RtpTransportTest, ReceivedPacketEcnMarkingPropagatedToDemuxedPacket) { - AutoThread thread; + test::RunLoop thread; RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); // Setup FakePacketTransport to send packets to itself. FakePacketTransport fake_rtp("fake_rtp"); @@ -361,7 +489,7 @@ TEST(RtpTransportTest, ReceivedPacketEcnMarkingPropagatedToDemuxedPacket) { } TEST(RtpTransportTest, RtcpSentAsEct1IfReceivedRtpPacketAsEct1) { - AutoThread thread; + test::RunLoop thread; RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); // Setup FakePacketTransport to send packets to itself. FakePacketTransport fake_rtp("fake_rtp"); @@ -397,7 +525,7 @@ TEST(RtpTransportTest, RtcpSentAsEct1IfReceivedRtpPacketAsEct1) { // Test that SignalPacketReceived does not fire when a RTP packet with an // unhandled payload type is received. TEST(RtpTransportTest, DontSignalUnhandledRtpPayloadType) { - AutoThread thread; + test::RunLoop thread; RtpTransport transport(kMuxDisabled, CreateTestFieldTrials()); FakePacketTransport fake_rtp("fake_rtp"); fake_rtp.SetDestination(&fake_rtp, true); @@ -423,7 +551,7 @@ TEST(RtpTransportTest, DontSignalUnhandledRtpPayloadType) { TEST(RtpTransportTest, DontChangeReadyToSendStateOnSendFailure) { // ReadyToSendState should only care about if transport is writable. - AutoThread thread; + test::RunLoop thread; RtpTransport transport(kMuxEnabled, CreateTestFieldTrials()); TransportObserver observer(&transport); diff --git a/pc/scoped_operations_batcher.cc b/pc/scoped_operations_batcher.cc new file mode 100644 index 00000000000..ad6ccc5b55b --- /dev/null +++ b/pc/scoped_operations_batcher.cc @@ -0,0 +1,102 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "pc/scoped_operations_batcher.h" + +#include +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/rtc_error.h" +#include "api/sequence_checker.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" + +namespace webrtc { + +ScopedOperationsBatcher::ScopedOperationsBatcher(Thread* target_thread) + : target_thread_(target_thread) { + RTC_DCHECK(target_thread_); +} + +ScopedOperationsBatcher::~ScopedOperationsBatcher() { + RTCError error = Run(); + if (!error.ok()) { + RTC_LOG(LS_ERROR) << "Batcher failed: " << error.message(); + } +} + +RTCError ScopedOperationsBatcher::Run() { + RTC_DCHECK_RUN_ON(&sequence_checker_); + std::vector return_tasks; + + size_t task_idx = 0; + bool target_thread_is_current = target_thread_->IsCurrent(); + + RTCError error = RTCError::OK(); + while (task_idx < tasks_.size()) { + target_thread_->BlockingCall([&] { + while (task_idx < tasks_.size()) { + if (auto* void_task = std::get_if(&tasks_[task_idx])) { + std::move (*void_task)(); + } else { + auto* returning_task = + std::get_if(&tasks_[task_idx]); + RTC_DCHECK(returning_task); + auto ret = std::move(*returning_task)(); + if (ret.ok()) { + if (ret.value()) { + return_tasks.push_back(std::move(ret.value())); + } + } else { + error = ret.MoveError(); + } + } + ++task_idx; + if (!error.ok()) { + return; + } + if (!target_thread_is_current && target_thread_->HasPendingTasks()) { + return; + } + } + }); + if (!error.ok()) { + break; + } + } + + tasks_.clear(); + + for (auto& task : return_tasks) { + std::move(task)(); + } + + return error; +} + +void ScopedOperationsBatcher::Add(SimpleBatchTask task) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + if (task) { + tasks_.emplace_back(std::move(task)); + } +} + +void ScopedOperationsBatcher::AddWithFinalizer(BatchTaskWithFinalizer task) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + if (task) { + tasks_.emplace_back(std::move(task)); + } +} + +} // namespace webrtc diff --git a/pc/scoped_operations_batcher.h b/pc/scoped_operations_batcher.h new file mode 100644 index 00000000000..2c481d44071 --- /dev/null +++ b/pc/scoped_operations_batcher.h @@ -0,0 +1,67 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef PC_SCOPED_OPERATIONS_BATCHER_H_ +#define PC_SCOPED_OPERATIONS_BATCHER_H_ + +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/rtc_error.h" +#include "api/sequence_checker.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread.h" + +namespace webrtc { + +// Batches operations to be executed synchronously on a target thread. +// +// ScopedOperationsBatcher must only be created on the signaling thread. +// +// When the batcher goes out of scope (or `Run()` is explicitly called), it +// executes all queued tasks in one or more `BlockingCall`s to the target +// thread. If the target thread requests a yield during batch execution, the +// batcher will cooperatively yield the thread and resume execution in a +// subsequent `BlockingCall`. +// +// Tasks can either have a `void` return type, or return a new task or return an +// error. +// Any tasks returned by the executed worker thread tasks are collected +// and subsequently executed on the calling thread (typically the signaling +// thread) after the worker thread operations have completed. +class ScopedOperationsBatcher { + public: + using SimpleBatchTask = absl::AnyInvocable; + using FinalizerTask = absl::AnyInvocable; + using BatchTaskWithFinalizer = + absl::AnyInvocable() &&>; + + explicit ScopedOperationsBatcher(Thread* target_thread); + ~ScopedOperationsBatcher(); + + RTCError Run(); + + // Queues non-nullptr tasks to be executed on the target thread when the + // ScopedOperationsBatcher goes out of scope. + void Add(SimpleBatchTask task); + void AddWithFinalizer(BatchTaskWithFinalizer task); + + private: + using BatchedTask = std::variant; + + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; + Thread* const target_thread_; + std::vector tasks_; +}; + +} // namespace webrtc + +#endif // PC_SCOPED_OPERATIONS_BATCHER_H_ diff --git a/pc/scoped_operations_batcher_unittest.cc b/pc/scoped_operations_batcher_unittest.cc new file mode 100644 index 00000000000..afe36b70845 --- /dev/null +++ b/pc/scoped_operations_batcher_unittest.cc @@ -0,0 +1,154 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "pc/scoped_operations_batcher.h" + +#include +#include +#include + +#include "absl/functional/any_invocable.h" +#include "api/rtc_error.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +TEST(ScopedOperationsBatcherTest, ExecutesTasksOnTargetThread) { + auto target_thread = Thread::Create(); + target_thread->Start(); + + bool task_executed = false; + bool target_checked = false; + + { + ScopedOperationsBatcher batcher(target_thread.get()); + batcher.Add([&] { + task_executed = true; + target_checked = target_thread->IsCurrent(); + }); + } + + EXPECT_TRUE(task_executed); + EXPECT_TRUE(target_checked); +} + +TEST(ScopedOperationsBatcherTest, ExecutesReturnedTasksOnCallingThread) { + // Use current thread as the calling (signaling) thread. This test runs + // without an explicit RunLoop, because the returned tasks are executed + // synchronously within the caller's context of `Run()` or + // `~ScopedOperationsBatcher()`. + auto signaling_thread = Thread::Current(); + + auto target_thread = Thread::Create(); + target_thread->Start(); + + bool return_task_executed = false; + Thread* return_task_thread = nullptr; + bool task_executed = false; + Thread* task_thread = nullptr; + + { + ScopedOperationsBatcher batcher(target_thread.get()); + ScopedOperationsBatcher::BatchTaskWithFinalizer task = + [&]() -> RTCErrorOr { + task_executed = true; + task_thread = Thread::Current(); + return ScopedOperationsBatcher::FinalizerTask([&]() { + return_task_executed = true; + return_task_thread = Thread::Current(); + }); + }; + batcher.AddWithFinalizer(std::move(task)); + } + + EXPECT_TRUE(task_executed); + EXPECT_EQ(task_thread, target_thread.get()); + EXPECT_TRUE(return_task_executed); + EXPECT_EQ(return_task_thread, signaling_thread); +} + +TEST(ScopedOperationsBatcherTest, YieldsToOtherTasks) { + auto target_thread = Thread::Create(); + target_thread->Start(); + + std::vector execution_order; + + { + ScopedOperationsBatcher batcher(target_thread.get()); + batcher.Add([&] { execution_order.push_back(1); }); + batcher.Add([&] { + execution_order.push_back(2); + // Post a task that should interrupt the batch since we now yield to any + // pending task. + target_thread->PostTask([&] { execution_order.push_back(3); }); + }); + batcher.Add([&] { execution_order.push_back(4); }); + batcher.Add([&] { execution_order.push_back(5); }); + } + + // Expect the task (3) to execute immediately after the task + // that posted it (2). The operations remaining in the batch (4, 5) + // should resume after the thread has processed the new task. + EXPECT_EQ(execution_order, std::vector({1, 2, 3, 4, 5})); +} + +TEST(ScopedOperationsBatcherTest, StopsExecutionOnError) { + auto target_thread = Thread::Create(); + target_thread->Start(); + + bool task1_executed = false; + bool task2_executed = false; + bool task3_executed = false; + // There's no finalizer for task2. + bool finalizer1_executed = false; + bool finalizer3_executed = false; + + RTCError error = RTCError::OK(); + { + ScopedOperationsBatcher batcher(target_thread.get()); + // Task 1. + batcher.AddWithFinalizer( + [&]() -> RTCErrorOr { + task1_executed = true; + return ScopedOperationsBatcher::FinalizerTask( + [&] { finalizer1_executed = true; }); + }); + // Task 2. + batcher.AddWithFinalizer( + [&]() -> RTCErrorOr { + task2_executed = true; + return RTCError(RTCErrorType::INVALID_STATE, "Failed"); + }); + // Task 3. + batcher.AddWithFinalizer( + [&]() -> RTCErrorOr { + task3_executed = true; + return ScopedOperationsBatcher::FinalizerTask( + [&] { finalizer3_executed = true; }); + }); + + error = batcher.Run(); + } + + EXPECT_FALSE(error.ok()); + EXPECT_EQ(error.type(), RTCErrorType::INVALID_STATE); + EXPECT_STREQ(error.message(), "Failed"); + + EXPECT_TRUE(task1_executed); + EXPECT_TRUE(finalizer1_executed); + EXPECT_TRUE(task2_executed); + EXPECT_FALSE(task3_executed); + EXPECT_FALSE(finalizer3_executed); +} + +} // namespace +} // namespace webrtc diff --git a/pc/sctp_data_channel.cc b/pc/sctp_data_channel.cc index 13d01f27460..a230ae7ca19 100644 --- a/pc/sctp_data_channel.cc +++ b/pc/sctp_data_channel.cc @@ -344,16 +344,14 @@ SctpDataChannel::SctpDataChannel( negotiated_(config.negotiated), ordered_(config.ordered), observer_(nullptr), - controller_(std::move(controller)) { + controller_(std::move(controller)), + connected_to_transport_(connected_to_transport) { RTC_DCHECK_RUN_ON(network_thread_); // Since we constructed on the network thread we can't (yet) check the // `controller_` pointer since doing so will trigger a thread check. RTC_UNUSED(network_thread_); RTC_DCHECK(config.IsValid()); - if (connected_to_transport) - network_safety_->SetAlive(); - switch (config.open_handshake_role) { case InternalDataChannelInit::kNone: // pre-negotiated handshake_state_ = kHandshakeReady; @@ -608,14 +606,17 @@ void SctpDataChannel::SendAsync( // thread. So we always post to the network thread (even if the current thread // might be the network thread - in theory a call could even come from within // the `on_complete` callback). - network_thread_->PostTask(SafeTask( - network_safety_, [this, buffer = std::move(buffer), - on_complete = std::move(on_complete)]() mutable { - RTC_DCHECK_RUN_ON(network_thread_); - RTCError err = SendImpl(std::move(buffer)); - if (on_complete) - std::move(on_complete)(err); - })); + scoped_refptr me(this); + network_thread_->PostTask([me = std::move(me), buffer = std::move(buffer), + on_complete = std::move(on_complete)]() mutable { + RTC_DCHECK_RUN_ON(me->network_thread_); + if (!me->connected_to_transport()) { + return; + } + RTCError err = me->SendImpl(std::move(buffer)); + if (on_complete) + std::move(on_complete)(err); + }); } void SctpDataChannel::SetSctpSid_n(StreamId sid) { @@ -658,7 +659,7 @@ void SctpDataChannel::OnClosingProcedureComplete() { void SctpDataChannel::OnTransportChannelCreated() { RTC_DCHECK_RUN_ON(network_thread_); - network_safety_->SetAlive(); + connected_to_transport_ = true; } void SctpDataChannel::OnTransportChannelClosed(RTCError error) { @@ -770,7 +771,7 @@ void SctpDataChannel::CloseAbruptlyWithError(RTCError error) { return; } - network_safety_->SetNotAlive(); + connected_to_transport_ = false; // Still go to "kClosing" before "kClosed", since observers may be expecting // that. diff --git a/pc/sctp_data_channel.h b/pc/sctp_data_channel.h index 540b3423c30..5164306cbf8 100644 --- a/pc/sctp_data_channel.h +++ b/pc/sctp_data_channel.h @@ -270,7 +270,7 @@ class SctpDataChannel : public DataChannelInterface { RTC_RUN_ON(network_thread_); bool connected_to_transport() const RTC_RUN_ON(network_thread_) { - return network_safety_->alive(); + return connected_to_transport_; } void MaybeSendOnBufferedAmountChanged() RTC_RUN_ON(network_thread_); @@ -302,9 +302,8 @@ class SctpDataChannel : public DataChannelInterface { kHandshakeInit; // Did we already start the graceful SCTP closing procedure? bool started_closing_procedure_ RTC_GUARDED_BY(network_thread_) = false; + bool connected_to_transport_ RTC_GUARDED_BY(network_thread_) = false; PacketQueue queued_received_data_ RTC_GUARDED_BY(network_thread_); - scoped_refptr network_safety_ = - PendingTaskSafetyFlag::CreateDetachedInactive(); }; } // namespace webrtc diff --git a/pc/data_channel_unittest.cc b/pc/sctp_data_channel_unittest.cc similarity index 98% rename from pc/data_channel_unittest.cc rename to pc/sctp_data_channel_unittest.cc index 82f4d852cda..c4e30ee0b65 100644 --- a/pc/data_channel_unittest.cc +++ b/pc/sctp_data_channel_unittest.cc @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "pc/sctp_data_channel.h" + #include #include #include @@ -25,7 +27,6 @@ #include "api/task_queue/pending_task_safety_flag.h" #include "api/test/rtc_error_matchers.h" #include "api/transport/data_channel_transport_interface.h" -#include "pc/sctp_data_channel.h" #include "pc/sctp_utils.h" #include "pc/test/fake_data_channel_controller.h" #include "rtc_base/checks.h" @@ -612,6 +613,22 @@ TEST_F(SctpDataChannelTest, TransportDestroyedWhileDataBuffered) { EXPECT_EQ(RTCErrorDetailType::SCTP_FAILURE, channel_->error().error_detail()); } +TEST_F(SctpDataChannelTest, ChannelDeletedWhileDataBuffered) { + AddObserver(); + SetChannelReady(); + + CopyOnWriteBuffer buffer(100 * 1024); + memset(buffer.MutableData(), 0, buffer.size()); + DataBuffer packet(buffer, true); + + // Send a very large packet, forcing the message to become buffered. + channel_->SendAsync(packet, nullptr); + + // Delete the channel, expect no crashes. + inner_channel_ = nullptr; + channel_ = nullptr; +} + TEST_F(SctpDataChannelTest, TransportGotErrorCode) { SetChannelReady(); diff --git a/pc/sctp_transport.cc b/pc/sctp_transport.cc index 063c855f35b..abca8a199c9 100644 --- a/pc/sctp_transport.cc +++ b/pc/sctp_transport.cc @@ -176,7 +176,7 @@ void SctpTransport::Clear() { UpdateInformation(SctpTransportState::kClosed); } -void SctpTransport::Start(const SctpOptions& options) { +bool SctpTransport::Start(const SctpOptions& options) { RTC_DCHECK_RUN_ON(owner_thread_); info_ = SctpTransportInformation(info_.state(), info_.dtls_transport(), @@ -185,7 +185,9 @@ void SctpTransport::Start(const SctpOptions& options) { if (!internal()->Start(options)) { RTC_LOG(LS_ERROR) << "Failed to push down SCTP parameters, closing."; UpdateInformation(SctpTransportState::kClosed); + return false; } + return true; } void SctpTransport::UpdateInformation(SctpTransportState state) { diff --git a/pc/sctp_transport.h b/pc/sctp_transport.h index 32014aa0f81..c6908a90149 100644 --- a/pc/sctp_transport.h +++ b/pc/sctp_transport.h @@ -65,9 +65,9 @@ class SctpTransport : public SctpTransportInterface, // Internal functions void Clear(); - // Initialize the webrtc::SctpTransport. This can be called from + // Initialize the webrtc::SctpTransport. This must be called from // the signaling thread. - void Start(const SctpOptions& options); + bool Start(const SctpOptions& options); // TODO(https://bugs.webrtc.org/10629): Move functions that need // internal() to be functions on the SctpTransport interface, diff --git a/pc/sctp_transport_unittest.cc b/pc/sctp_transport_unittest.cc index c7a8a4bd97d..d3b796e02f1 100644 --- a/pc/sctp_transport_unittest.cc +++ b/pc/sctp_transport_unittest.cc @@ -32,9 +32,9 @@ #include "p2p/dtls/fake_dtls_transport.h" #include "pc/dtls_transport.h" #include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -161,7 +161,7 @@ class SctpTransportTest : public ::testing::Test { return static_cast(transport_->internal()); } - AutoThread main_thread_; + test::RunLoop main_thread_; scoped_refptr transport_; scoped_refptr dtls_transport_; std::unique_ptr internal_transport_; @@ -169,7 +169,7 @@ class SctpTransportTest : public ::testing::Test { }; TEST(SctpTransportSimpleTest, CreateClearDelete) { - AutoThread main_thread; + test::RunLoop main_thread; std::unique_ptr internal_transport = std::make_unique("audio", ICE_CANDIDATE_COMPONENT_RTP); scoped_refptr dtls_transport = diff --git a/pc/sdp_munging_detector_unittest.cc b/pc/sdp_munging_detector_unittest.cc index 09d7e9f6320..369837d060c 100644 --- a/pc/sdp_munging_detector_unittest.cc +++ b/pc/sdp_munging_detector_unittest.cc @@ -59,12 +59,14 @@ #include "pc/test/fake_rtc_certificate_generator.h" #include "pc/test/integration_test_helpers.h" #include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/event.h" #include "rtc_base/strings/string_format.h" #include "rtc_base/thread.h" #include "system_wrappers/include/metrics.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" // This file contains unit tests that relate to the behavior of the @@ -148,7 +150,7 @@ class SdpMungingTest : public ::testing::Test { scoped_refptr pc_factory_; private: - AutoThread main_thread_; + test::RunLoop main_thread_; }; TEST_F(SdpMungingTest, DISABLED_ReportUMAMetricsWithNoMunging) { @@ -545,14 +547,19 @@ TEST_F(SdpMungingTest, IceUfragRestrictedAddresses) { std::optional result; const std::string candidate = StringFormat( tmpl, absl::StrReplaceAll(address_test.first, {{":", " "}}).c_str()); + + Event event; caller->pc()->AddIceCandidate( std::unique_ptr( CreateIceCandidate("", 0, candidate, nullptr)), - [&result](RTCError error) { result = error; }); + [&](RTCError error) { + result = error; + event.Set(); + }); + + ASSERT_TRUE(event.Wait(kDefaultTimeout)); + EXPECT_TRUE(result.has_value()); - ASSERT_THAT( - WaitUntil([&] { return result.has_value(); }, ::testing::IsTrue()), - IsRtcOk()); if (address_test.second == true) { EXPECT_TRUE(result.value().ok()); } else { diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc index 03fda68386f..7a9348071f5 100644 --- a/pc/sdp_offer_answer.cc +++ b/pc/sdp_offer_answer.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,6 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/candidate.h" #include "api/crypto/crypto_options.h" #include "api/environment/environment.h" @@ -39,6 +39,7 @@ #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" #include "api/media_types.h" +#include "api/payload_type.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" @@ -53,7 +54,6 @@ #include "api/uma_metrics.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" #include "api/video/video_codec_constants.h" -#include "call/payload_type.h" #include "media/base/codec.h" #include "media/base/codec_comparators.h" #include "media/base/media_constants.h" @@ -86,6 +86,8 @@ #include "pc/rtp_sender_proxy.h" #include "pc/rtp_transceiver.h" #include "pc/rtp_transmission_manager.h" +#include "pc/rtp_transport_internal.h" +#include "pc/scoped_operations_batcher.h" #include "pc/sdp_munging_detector.h" #include "pc/session_description.h" #include "pc/simulcast_description.h" @@ -103,28 +105,21 @@ #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/trace_event.h" #include "rtc_base/weak_ptr.h" #include "system_wrappers/include/metrics.h" -using ::webrtc::ContentInfo; -using ::webrtc::ContentInfos; -using webrtc::MediaContentDescription; -using ::webrtc::MediaProtocolType; -using ::webrtc::RidDescription; -using ::webrtc::RidDirection; -using ::webrtc::SessionDescription; -using ::webrtc::SimulcastDescription; -using ::webrtc::SimulcastLayer; -using ::webrtc::SimulcastLayerList; -using ::webrtc::StreamParams; -using ::webrtc::TransportInfo; - namespace webrtc { - namespace { + +struct DtlsTransportAndName { + scoped_refptr transport; + std::optional transport_name; +}; + typedef PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions; // Error messages @@ -179,7 +174,7 @@ flat_map GetBundleGroupsByMid( // Helper function to look up DTLS transports for all transceivers in a single // blocking call to the network thread. -flat_map> GetDtlsTransports( +flat_map GetDtlsTransports( const TransceiverList& transceivers, Thread* network_thread, JsepTransportController* transport_controller) { @@ -192,18 +187,26 @@ flat_map> GetDtlsTransports( } } if (mids_to_lookup.empty()) { - return flat_map>(); + return flat_map(); } return network_thread->BlockingCall([&] { RTC_DCHECK_RUN_ON(network_thread); - std::vector>> entries; + std::vector> entries; entries.reserve(mids_to_lookup.size()); for (const auto& mid : mids_to_lookup) { + // Here we essentially look up the same transport twice because + // the `transport_controller` doesn't have a public method that allows us + // to look up the JsepTransport object. This could be improved. + auto transport = transport_controller->LookupDtlsTransportByMid_n(mid); + auto internal_transport = transport_controller->GetDtlsTransport(mid); entries.emplace_back( - mid, transport_controller->LookupDtlsTransportByMid_n(mid)); + mid, DtlsTransportAndName{ + transport, internal_transport + ? std::optional( + internal_transport->transport_name()) + : std::nullopt}); } - return flat_map>( - std::move(entries)); + return flat_map(std::move(entries)); }); } @@ -252,7 +255,7 @@ std::string GetSetDescriptionErrorMessage(ContentSource source, return oss.Release(); } -std::string GetStreamIdsString(ArrayView stream_ids) { +std::string GetStreamIdsString(std::span stream_ids) { std::string output = "streams=["; const char* separator = ""; for (const auto& stream_id : stream_ids) { @@ -368,13 +371,13 @@ RTCError VerifyDirectionsInAnswer(const SessionDescription* local_offer, if (!RtpTransceiverDirectionHasRecv(local_direction) && RtpTransceiverDirectionHasSend(remote_direction)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Incompatible receive direction"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Incompatible receive direction"); } if (!RtpTransceiverDirectionHasSend(local_direction) && RtpTransceiverDirectionHasRecv(remote_direction)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Incompatible send direction"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Incompatible send direction"); } } return RTCError::OK(); @@ -411,15 +414,17 @@ RTCError VerifyCrypto( const TransportInfo* tinfo = desc->GetTransportInfoByName(mid); if (!media || !tinfo) { // Something is not right. - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << kInvalidSdp); } if (dtls_enabled) { if (!tinfo->description.identity_fingerprint) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kSdpWithoutDtlsFingerprint); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << kSdpWithoutDtlsFingerprint); } } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kSdpWithoutCrypto); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << kSdpWithoutCrypto); } } return RTCError::OK(); @@ -467,17 +472,17 @@ RTCError ValidateMids(const SessionDescription& description) { flat_set mids; for (const ContentInfo& content : description.contents()) { if (content.mid().empty()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "A media section is missing a MID attribute."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "A media section is missing a MID attribute."); } if (content.mid().size() > kMidMaxSize) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "The MID attribute exceeds the maximum supported " - "length of 16 characters."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "The MID attribute exceeds the maximum supported " + "length of 16 characters."); } if (!mids.insert(content.mid()).second) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Duplicate a=mid value '" + content.mid() + "'."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Duplicate a=mid value '" << content.mid() << "'."); } } return RTCError::OK(); @@ -497,7 +502,7 @@ RTCError FindDuplicateCodecParameters( << ". All codecs must share the same type, " "encoding name, clock rate and parameters."; - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, sb.Release()); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) << sb.Release()); } payload_to_codec_parameters.insert( std::make_pair(codec_parameters.payload_type, codec_parameters)); @@ -518,9 +523,9 @@ RTCError ValidateBundledPayloadTypes(const SessionDescription& description) { const ContentInfo* content_description = description.GetContentByName(content_name); if (!content_description) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "A BUNDLE group contains a MID='" + content_name + - "' matching no m= section."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "A BUNDLE group contains a MID='" << content_name + << "' matching no m= section."); } const MediaContentDescription* media_description = content_description->media_description(); @@ -551,12 +556,12 @@ RTCError FindDuplicateHeaderExtensionIds( if (existing_extension != id_to_extension.end() && !(extension.uri == existing_extension->second.uri && extension.encrypt == existing_extension->second.encrypt)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "A BUNDLE group contains a codec collision for " - "header extension id=" + - absl::StrCat(extension.id) + - ". The id must be the same across all bundled media descriptions"); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "A BUNDLE group contains a codec collision for " + "header extension id=" + << extension.id + << ". The id must be the same across all bundled media descriptions"); } id_to_extension.insert(std::make_pair(extension.id, extension)); return RTCError::OK(); @@ -575,9 +580,9 @@ RTCError ValidateBundledRtpHeaderExtensions( const ContentInfo* content_description = description.GetContentByName(content_name); if (!content_description) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "A BUNDLE group contains a MID='" + content_name + - "' matching no m= section."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "A BUNDLE group contains a MID='" << content_name + << "' matching no m= section."); } const MediaContentDescription* media_description = content_description->media_description(); @@ -614,10 +619,10 @@ RTCError ValidateRtpHeaderExtensionsForSpecSimulcast( return ext.uri == RtpExtension::kRidUri; }); if (it == extensions.end()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "The media section with MID='" + content.mid() + - "' negotiates simulcast but does not negotiate " - "the RID RTP header extension."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "The media section with MID='" << content.mid() + << "' negotiates simulcast but does not negotiate " + "the RID RTP header extension."); } } return RTCError::OK(); @@ -638,11 +643,11 @@ RTCError ValidateSsrcGroups(const SessionDescription& description) { group.ssrcs.size() != 2) || (group.semantics == kSimSsrcGroupSemantics && group.ssrcs.size() > kMaxSimulcastStreams)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "The media section with MID='" + content.mid() + - "' has a ssrc-group with semantics " + - group.semantics + - " and an unexpected number of SSRCs."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "The media section with MID='" << content.mid() + << "' has a ssrc-group with semantics " + << group.semantics + << " and an unexpected number of SSRCs."); } } } @@ -665,12 +670,12 @@ RTCError ValidatePayloadTypes(const SessionDescription& description) { if (type == MediaType::AUDIO || type == MediaType::VIDEO) { for (const auto& codec : media_description->codecs()) { if (!PayloadType::IsValid(codec.id, media_description->rtcp_mux())) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "The media section with MID='" + content.mid() + - "' used an invalid payload type " + absl::StrCat(codec.id) + - " for codec '" + codec.name + ", rtcp-mux:" + - (media_description->rtcp_mux() ? "enabled" : "disabled")); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "The media section with MID='" << content.mid() + << "' used an invalid payload type " << codec.id << " for codec '" + << codec.name << ", rtcp-mux:" + << (media_description->rtcp_mux() ? "enabled" : "disabled")); } } } @@ -704,6 +709,12 @@ std::vector GetSendEncodingsFromRemoteDescription( // This is a remote description, the parameters we are after should appear // as receive streams. for (const auto& alternatives : simulcast.receive_layers()) { + if (result.size() >= kMaxSimulcastStreams) { + RTC_LOG(LS_WARNING) + << "Excessive simulcast layers in remote description. Clamping to " + << kMaxSimulcastStreams; + break; + } RTC_DCHECK(!alternatives.empty()); // There is currently no way to specify or choose from alternatives. // We will always use the first alternative, which is the most preferred. @@ -792,9 +803,7 @@ RTCError DisableSimulcastInSender(scoped_refptr sender) { // The SDP parser used to populate these values by default for the 'content // name' if an a=mid line was absent. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -std::string GetDefaultMidForPlanB(MediaType media_type) { +PLAN_B_ONLY std::string GetDefaultMidForPlanB(MediaType media_type) { switch (media_type) { case MediaType::AUDIO: return CN_AUDIO; @@ -811,10 +820,9 @@ std::string GetDefaultMidForPlanB(MediaType media_type) { RTC_DCHECK_NOTREACHED(); return ""; } -#pragma clang diagnostic pop // Add options to |[audio/video]_media_description_options| from `senders`. -void AddPlanBRtpSenderOptions( +PLAN_B_ONLY void AddPlanBRtpSenderOptions( const std::vector< scoped_refptr>>& senders, MediaDescriptionOptions* audio_media_description_options, @@ -1157,7 +1165,9 @@ class SdpOfferAnswerHandler::RemoteDescriptionOperation { if (type_ == SdpType::kOffer) { // TODO(mallinath) - Handle CreateChannel failure, as new local // description is applied. Restore back to old description. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); error_ = handler_->CreateChannels(*session_desc); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Remove unused channels if MediaContentDescription is rejected. handler_->RemoveUnusedChannels(session_desc); @@ -1701,19 +1711,18 @@ void SdpOfferAnswerHandler::CreateOffer( } void SdpOfferAnswerHandler::SetLocalDescription( - SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc_ptr) { + scoped_refptr observer, + std::unique_ptr desc) { RTC_LOG_THREAD_BLOCK_COUNT(); RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(desc_ptr); + RTC_DCHECK(desc); RTC_DCHECK(observer); // Chain this operation. If asynchronous operations are pending on the chain, // this operation will be queued to be invoked, otherwise the contents of the // lambda will execute immediately. operations_chain_->ChainOperation( [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = scoped_refptr(observer), - desc = std::unique_ptr(desc_ptr)]( + observer_refptr = observer, desc = std::move(desc)]( std::function operations_chain_callback) mutable { // Abort early if `this_weak_ptr` is no longer valid. if (!this_weak_ptr) { @@ -1769,13 +1778,13 @@ void SdpOfferAnswerHandler::SetLocalDescription( } void SdpOfferAnswerHandler::SetLocalDescription( - SetSessionDescriptionObserver* observer) { + scoped_refptr observer) { RTC_LOG_THREAD_BLOCK_COUNT(); RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(observer); - SetLocalDescription(make_ref_counted( - weak_ptr_factory_.GetWeakPtr(), - scoped_refptr(observer))); + scoped_refptr adapter = + make_ref_counted( + weak_ptr_factory_.GetWeakPtr(), std::move(observer)); + SetLocalDescription(std::move(adapter)); } void SdpOfferAnswerHandler::SetLocalDescription( @@ -1901,10 +1910,9 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( if (ConfiguredForMedia()) { std::vector> remove_list; std::vector> removed_streams; - flat_map> - dtls_transports_by_mid = - GetDtlsTransports(*transceivers(), context_->network_thread(), - transport_controller_s()); + flat_map dtls_transports_by_mid = + GetDtlsTransports(*transceivers(), context_->network_thread(), + transport_controller_s()); for (const auto& transceiver_ext : transceivers()->List()) { auto transceiver = transceiver_ext->internal(); @@ -1918,8 +1926,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( if (transceiver->mid()) { auto it = dtls_transports_by_mid.find(*transceiver->mid()); RTC_DCHECK(it != dtls_transports_by_mid.end()); - transceiver->sender_internal()->set_transport(it->second); - transceiver->receiver_internal()->set_transport(it->second); + transceiver->SetTransport(it->second.transport, + it->second.transport_name); } const ContentInfo* content = @@ -1966,12 +1974,28 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( if (type == SdpType::kOffer) { // TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local // description is applied. Restore back to old description. + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); error = CreateChannels(*local_description()->description()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); if (!error.ok()) { RTC_LOG(LS_ERROR) << error.message() << " (" << type << ")"; return error; } } + // Plan B transport synchronization. + flat_map dtls_transports_by_mid = + GetDtlsTransports(*transceivers(), context_->network_thread(), + transport_controller_s()); + for (const auto& transceiver_ext : transceivers()->List()) { + auto transceiver = transceiver_ext->internal(); + if (transceiver->mid()) { + auto it = dtls_transports_by_mid.find(*transceiver->mid()); + if (it != dtls_transports_by_mid.end()) { + transceiver->SetTransport(it->second.transport, + it->second.transport_name); + } + } + } // Remove unused channels if MediaContentDescription is rejected. RemoveUnusedChannels(local_description()->description()); } @@ -1988,7 +2012,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( pending_ice_restarts_.clear(); if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << GetSessionErrorMsg()); } // Validate SSRCs, we do not allow duplicates. @@ -1999,9 +2024,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( for (uint32_t ssrc : stream.ssrcs) { auto result = used_ssrcs.insert(ssrc); if (!result.second) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Duplicate ssrc " + absl::StrCat(ssrc) + " is not allowed"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Duplicate ssrc " << ssrc << " is not allowed"); } } } @@ -2022,8 +2046,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( if (!content) { continue; } - ChannelInterface* channel = transceiver->channel(); - if (content->rejected || !channel || channel->local_streams().empty()) { + if (content->rejected || !transceiver->HasChannel() || + transceiver->channel_local_streams().empty()) { // 0 is a special value meaning "this sender has no associated send // stream". Need to call this so the sender won't attempt to configure // a no longer existing stream and run into DCHECKs in the lower @@ -2031,7 +2055,8 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( transceiver->sender_internal()->SetSsrc(0); } else { // Get the StreamParams from the channel which could generate SSRCs. - const std::vector& streams = channel->local_streams(); + const std::vector& streams = + transceiver->channel_local_streams(); transceiver->sender_internal()->set_stream_ids( streams[0].stream_ids()); auto encodings = @@ -2053,6 +2078,7 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( const ContentInfo* audio_content = GetFirstAudioContent(local_description()->description()); if (audio_content) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); if (audio_content->rejected) { RemoveSenders(MediaType::AUDIO); } else { @@ -2060,11 +2086,13 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( audio_content->media_description(); UpdateLocalSendersPlanB(audio_desc->streams(), audio_desc->type()); } + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } const ContentInfo* video_content = GetFirstVideoContent(local_description()->description()); if (video_content) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); if (video_content->rejected) { RemoveSenders(MediaType::VIDEO); } else { @@ -2072,6 +2100,7 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( video_content->media_description(); UpdateLocalSendersPlanB(video_desc->streams(), video_desc->type()); } + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } } @@ -2092,36 +2121,15 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( } void SdpOfferAnswerHandler::SetRemoteDescription( - SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc_ptr) { + scoped_refptr observer, + std::unique_ptr desc) { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(observer); - RTC_DCHECK(desc_ptr); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = scoped_refptr(observer), - desc = std::unique_ptr(desc_ptr)]( - std::function operations_chain_callback) mutable { - // Abort early if `this_weak_ptr` is no longer valid. - if (!this_weak_ptr) { - // For consistency with SetSessionDescriptionObserverAdapter whose - // posted messages doesn't get processed when the PC is destroyed, we - // do not inform `observer_refptr` that the operation failed. - operations_chain_callback(); - return; - } - // SetSessionDescriptionObserverAdapter takes care of making sure the - // `observer_refptr` is invoked in a posted message. - this_weak_ptr->DoSetRemoteDescription( - std::make_unique( - this_weak_ptr.get(), std::move(desc), - make_ref_counted( - this_weak_ptr, observer_refptr), - std::move(operations_chain_callback))); - }); + RTC_DCHECK(desc); + SetRemoteDescription( + std::move(desc), + make_ref_counted( + weak_ptr_factory_.GetWeakPtr(), std::move(observer))); } void SdpOfferAnswerHandler::SetRemoteDescription( @@ -2265,11 +2273,28 @@ void SdpOfferAnswerHandler::ApplyRemoteDescription( kMsidSignalingNotUsed; if (!operation->unified_plan()) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); PlanBUpdateSendersAndReceivers( GetFirstAudioContent(remote_description()->description()), GetFirstAudioContentDescription(remote_description()->description()), GetFirstVideoContent(remote_description()->description()), GetFirstVideoContentDescription(remote_description()->description())); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); + + // Plan B transport synchronization. + flat_map dtls_transports_by_mid = + GetDtlsTransports(*transceivers(), context_->network_thread(), + transport_controller_s()); + for (const auto& transceiver_ext : transceivers()->List()) { + auto transceiver = transceiver_ext->internal(); + if (transceiver->mid()) { + auto it = dtls_transports_by_mid.find(*transceiver->mid()); + if (it != dtls_transports_by_mid.end()) { + transceiver->SetTransport(it->second.transport, + it->second.transport_name); + } + } + } } if (operation->type() == SdpType::kAnswer) { @@ -2299,7 +2324,8 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( std::vector> remove_list; std::vector> added_streams; std::vector> removed_streams; - flat_map> dtls_transports_by_mid = + ScopedOperationsBatcher worker_tasks(context_->worker_thread()); + flat_map dtls_transports_by_mid = GetDtlsTransports(*transceivers(), context_->network_thread(), transport_controller_s()); @@ -2380,8 +2406,8 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( if (transceiver->mid()) { auto it = dtls_transports_by_mid.find(*transceiver->mid()); RTC_DCHECK(it != dtls_transports_by_mid.end()); - transceiver->sender_internal()->set_transport(it->second); - transceiver->receiver_internal()->set_transport(it->second); + transceiver->SetTransport(it->second.transport, + it->second.transport_name); } } // 2.2.8.1.12: If the media description is rejected, and transceiver is @@ -2389,18 +2415,23 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( if (content->rejected && !transceiver->stopped()) { RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->mid() << " since the media section was rejected."; - transceiver->StopTransceiverProcedure(); + worker_tasks.Add(transceiver->GetStopTransceiverProcedure()); } if (!content->rejected && RtpTransceiverDirectionHasRecv(local_direction)) { if (!media_desc->streams().empty() && media_desc->streams()[0].has_ssrcs()) { uint32_t ssrc = media_desc->streams()[0].first_ssrc(); - transceiver->receiver_internal()->SetupMediaChannel(ssrc); + worker_tasks.Add( + transceiver->receiver_internal()->GetSetupForMediaChannel(ssrc)); } else { - transceiver->receiver_internal()->SetupUnsignaledMediaChannel(); + worker_tasks.Add(transceiver->receiver_internal() + ->GetSetupForUnsignaledMediaChannel()); } } } + + worker_tasks.Run(); + // Once all processing has finished, fire off callbacks. pc_->RunWithObserver([&](auto observer) { for (const auto& transceiver : now_receiving_transceivers) { @@ -3381,11 +3412,9 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { auto state = signaling_state(); if (state != PeerConnectionInterface::kHaveLocalOffer && state != PeerConnectionInterface::kHaveRemoteOffer) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_STATE, - (StringBuilder("Called in wrong signalingState: ") - << (PeerConnectionInterface::AsString(signaling_state()))) - .Release()); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_STATE) + << "Called in wrong signalingState: " + << PeerConnectionInterface::AsString(signaling_state())); } RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(IsUnifiedPlan()); @@ -3394,6 +3423,7 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { std::vector> all_added_streams; std::vector> all_removed_streams; std::vector> removed_receivers; + ScopedOperationsBatcher worker_tasks(context_->worker_thread()); for (auto&& transceivers_stable_state_pair : transceivers()->StableStates()) { auto transceiver = transceivers_stable_state_pair.first; @@ -3455,16 +3485,17 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { if (transceiver->internal()->reused_for_addtrack()) { transceiver->internal()->set_created_by_addtrack(true); } else { - transceiver->internal()->StopTransceiverProcedure(); + worker_tasks.Add( + transceiver->internal()->GetStopTransceiverProcedure()); transceivers()->Remove(transceiver); } } + auto sender_internal = transceiver->internal()->sender_internal(); if (stable_state.init_send_encodings()) { - transceiver->internal()->sender_internal()->set_init_send_encodings( + sender_internal->set_init_send_encodings( stable_state.init_send_encodings().value()); } - transceiver->internal()->sender_internal()->set_transport(nullptr); - transceiver->internal()->receiver_internal()->set_transport(nullptr); + transceiver->internal()->SetTransport(nullptr, std::nullopt); if (stable_state.has_m_section()) { transceiver->internal()->set_mid(stable_state.mid()); transceiver->internal()->set_mline_index(stable_state.mline_index()); @@ -3835,17 +3866,15 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( RTC_DCHECK_EQ(SessionError::kNone, session_error()); if (!sdesc || !sdesc->description()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) << kInvalidSdp); } SdpType type = sdesc->GetType(); if ((source == CS_LOCAL && !ExpectSetLocalDescription(type)) || (source == CS_REMOTE && !ExpectSetRemoteDescription(type))) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_STATE, - (StringBuilder("Called in wrong state: ") - << PeerConnectionInterface::AsString(signaling_state())) - .Release()); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_STATE) + << "Called in wrong state: " + << PeerConnectionInterface::AsString(signaling_state())); } RTCError error = ValidateMids(*sdesc->description()); @@ -3864,8 +3893,8 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( // Verify ice-ufrag and ice-pwd. if (!VerifyIceUfragPwdPresent(sdesc->description(), bundle_groups_by_mid)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kSdpWithoutIceUfragPwd); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << kSdpWithoutIceUfragPwd); } // Validate that there are no collisions of bundled payload types. @@ -3888,8 +3917,8 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( if (!pc_->ValidateBundleSettings(sdesc->description(), bundle_groups_by_mid)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kBundleWithoutRtcpMux); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << kBundleWithoutRtcpMux); } error = ValidatePayloadTypes(*sdesc->description()); @@ -3910,8 +3939,8 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) || !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(), type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInAnswer); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << kMlineMismatchInAnswer); } } else { // The re-offers should respect the order of m= sections in current @@ -3935,8 +3964,8 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( if (current_desc && !MediaSectionsInSameOrder(*current_desc, secondary_current_desc, *sdesc->description(), type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInSubsequentOffer); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << kMlineMismatchInSubsequentOffer); } } @@ -3951,10 +3980,10 @@ RTCError SdpOfferAnswerHandler::ValidateSessionDescription( if ((desc.type() == MediaType::AUDIO || desc.type() == MediaType::VIDEO) && desc.streams().size() > 1u) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Media section has more than one track specified with a=ssrc lines " - "which is not supported with Unified Plan."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Media section has more than one track specified " + "with a=ssrc lines which is not supported with " + "Unified Plan."); } } // Validate spec-simulcast which only works if the remote end negotiated the @@ -3998,12 +4027,13 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels( if (pc_->configuration()->bundle_policy == PeerConnectionInterface::kBundlePolicyMaxBundle && bundle_groups_by_mid.empty()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "max-bundle configured but session description has no BUNDLE group"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "max-bundle configured but session description has " + "no BUNDLE group"); } } + ScopedOperationsBatcher worker_tasks(context_->worker_thread()); const ContentInfos& new_contents = new_session.description()->contents(); for (size_t i = 0; i < new_contents.size(); ++i) { const ContentInfo& new_content = new_contents[i]; @@ -4065,7 +4095,8 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels( // is not already stopped, SDP munging has happened and we need to // ensure the transceiver is stopped. if (!transceiver->internal()->stopped()) { - transceiver->internal()->StopTransceiverProcedure(); + worker_tasks.Add( + transceiver->internal()->GetStopTransceiverProcedure()); } RTC_DCHECK(transceiver->internal()->stopped()); } @@ -4089,8 +4120,8 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels( } else if (media_type == MediaType::UNSUPPORTED) { RTC_LOG(LS_INFO) << "Ignoring unsupported media type"; } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Unknown section type."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Unknown section type."); } } @@ -4134,8 +4165,8 @@ SdpOfferAnswerHandler::AssociateTransceiver( } if (!transceiver) { // This may happen normally when media sections are rejected. - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Transceiver not found based on m-line index"); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Transceiver not found based on m-line index"); } } else { RTC_DCHECK_EQ(source, CS_REMOTE); @@ -4193,9 +4224,9 @@ SdpOfferAnswerHandler::AssociateTransceiver( } if (transceiver->media_type() != media_desc->type()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Transceiver type does not match media description type."); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "Transceiver type does not match media description type."); } if (media_desc->HasSimulcast()) { @@ -4238,13 +4269,12 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel( TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::UpdateTransceiverChannel"); RTC_DCHECK(IsUnifiedPlan()); RTC_DCHECK(transceiver); - ChannelInterface* channel = transceiver->internal()->channel(); if (content.rejected) { - if (channel) { + if (transceiver->internal()->HasChannel()) { transceiver->internal()->ClearChannel(); } } else { - if (!channel) { + if (!transceiver->internal()->HasChannel()) { auto error = transceiver->internal()->CreateChannel( content.mid(), pc_->call_ptr(), pc_->configuration()->media_config, pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(), @@ -4275,8 +4305,8 @@ RTCError SdpOfferAnswerHandler::UpdateDataChannelTransport( error.set_error_detail(RTCErrorDetailType::DATA_CHANNEL_FAILURE); pc_->DestroyDataChannelTransport(error); } else if (!pc_->CreateDataChannelTransport(content.mid())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create data channel."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to create data channel."); } return RTCError::OK(); } @@ -4335,7 +4365,9 @@ void SdpOfferAnswerHandler::FillInMissingRemoteMids( source_explanation = "generated just now"; } } else { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); new_mid = GetDefaultMidForPlanB(content.media_description()->type()); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); source_explanation = "to match pre-existing behavior"; } RTC_DCHECK(!new_mid.empty()); @@ -4396,7 +4428,9 @@ void SdpOfferAnswerHandler::GetOptionsForOffer( if (IsUnifiedPlan()) { GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options); } else { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); GetOptionsForPlanBOffer(offer_answer_options, session_options); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Apply ICE restart flag and renomination flag. @@ -4691,7 +4725,9 @@ void SdpOfferAnswerHandler::GetOptionsForAnswer( if (IsUnifiedPlan()) { GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options); } else { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); GetOptionsForPlanBAnswer(offer_answer_options, session_options); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } // Apply ICE renomination flag. @@ -4858,8 +4894,8 @@ RTCError SdpOfferAnswerHandler::HandleLegacyOfferOptions( } else if (options.offer_to_receive_audio == 1) { AddUpToOneReceivingTransceiverOfType(MediaType::AUDIO); } else if (options.offer_to_receive_audio > 1) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, - "offer_to_receive_audio > 1 is not supported."); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_PARAMETER) + << "offer_to_receive_audio > 1 is not supported."); } if (options.offer_to_receive_video == 0) { @@ -4867,8 +4903,8 @@ RTCError SdpOfferAnswerHandler::HandleLegacyOfferOptions( } else if (options.offer_to_receive_video == 1) { AddUpToOneReceivingTransceiverOfType(MediaType::VIDEO); } else if (options.offer_to_receive_video > 1) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, - "offer_to_receive_video > 1 is not supported."); + return LOG_ERROR(RTCError(RTCErrorType::UNSUPPORTED_PARAMETER) + << "offer_to_receive_video > 1 is not supported."); } return RTCError::OK(); @@ -5113,9 +5149,8 @@ void SdpOfferAnswerHandler::EnableSending() { return; } for (const auto& transceiver : transceivers()->ListInternal()) { - ChannelInterface* channel = transceiver->channel(); - if (channel) { - channel->Enable(true); + if (transceiver->HasChannel()) { + transceiver->EnableChannel(true); } } } @@ -5131,19 +5166,13 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription( RTC_DCHECK(sdesc); if (ConfiguredForMedia()) { - // Note: This will perform a BlockingCall over to the worker thread, which + // Note: This may perform a BlockingCall over to the network thread, which // we'll also do in a loop below. - if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) { - // Note that this is never expected to fail, since RtpDemuxer doesn't - // return an error when changing payload type demux criteria, which is all - // this does. - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to update payload type demuxing state."); - } + UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid); // Push down the new SDP media section for each audio/video transceiver. auto rtp_transceivers = transceivers()->ListInternal(); - std::vector> + std::vector> channels; std::optional preferred_rtcp_cc_ack_type; bool all_rtp_have_same_cc_ack_type = true; @@ -5153,8 +5182,8 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription( for (const auto& transceiver : rtp_transceivers) { const ContentInfo* content_info = FindMediaSectionForTransceiver(transceiver, sdesc); - ChannelInterface* channel = transceiver->channel(); - if (!channel || !content_info || content_info->rejected) { + if (!transceiver->HasChannel() || !content_info || + content_info->rejected) { continue; } const MediaContentDescription* content_desc = @@ -5176,7 +5205,7 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription( } transceiver->OnNegotiationUpdate(type, content_desc); - channels.push_back(std::make_pair(channel, content_desc)); + channels.push_back(std::make_pair(transceiver, content_desc)); } if (any_rtp_has_cc_ack_type && all_rtp_have_same_cc_ack_type) { @@ -5186,27 +5215,22 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription( "types, ignoring all."; } - // This for-loop of invokes helps audio impairment during re-negotiations. - // One of the causes is that downstairs decoder creation is synchronous at - // the moment, and that a decoder is created for each codec listed in the - // SDP. - // - // TODO(bugs.webrtc.org/12840): consider merging the invokes again after - // these projects have shipped: - // - bugs.webrtc.org/12462 - // - crbug.com/1157227 - // - crbug.com/1187289 - for (const auto& entry : channels) { - std::string error; - bool success = context_->worker_thread()->BlockingCall([&]() { - return (source == CS_LOCAL) - ? entry.first->SetLocalContent(entry.second, type, error) - : entry.first->SetRemoteContent(entry.second, type, error); - }); - if (!success) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error); + // Batch up the calls to set the local/remote channel content. + ScopedOperationsBatcher batcher(context_->worker_thread()); + + for (const auto& [transceiver, content] : channels) { + if (source == CS_LOCAL) { + transceiver->SetChannelLocalContent(content, type, batcher); + } else { + transceiver->SetChannelRemoteContent(content, type, batcher); } } + + RTCError error = batcher.Run(); + if (!error.ok()) { + return error; + } + // If local and remote are both set, we assume that it's safe to trigger // CCFB. if (pc_->trials().IsEnabled("WebRTC-RFC8888CongestionControlFeedback")) { @@ -5376,12 +5400,16 @@ void SdpOfferAnswerHandler::RemoveUnusedChannels( // voice channel. const ContentInfo* video_info = GetFirstVideoContent(desc); if (!video_info || video_info->rejected) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel(); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } const ContentInfo* audio_info = GetFirstAudioContent(desc); if (!audio_info || audio_info->rejected) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN(); rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel(); + RTC_ALLOW_PLAN_B_DEPRECATION_END(); } } const ContentInfo* data_info = GetFirstDataContent(desc); @@ -5554,10 +5582,10 @@ RTCErrorOr SdpOfferAnswerHandler::FindContentInfo( return content_info.mid() == candidate->sdp_mid(); }); if (it == contents.end()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Mid " + candidate->sdp_mid() + - " specified but no media section with that mid found."); + return LOG_ERROR( + RTCError(RTCErrorType::INVALID_PARAMETER) + << "Mid " << candidate->sdp_mid() + << " specified but no media section with that mid found."); } else { return &*it; } @@ -5568,16 +5596,15 @@ RTCErrorOr SdpOfferAnswerHandler::FindContentInfo( if (mediacontent_index < content_size) { return &description->description()->contents()[mediacontent_index]; } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, - "Media line index (" + - absl::StrCat(candidate->sdp_mline_index()) + - ") out of range (number of mlines: " + - absl::StrCat(content_size) + ")."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_RANGE) + << "Media line index (" << candidate->sdp_mline_index() + << ") out of range (number of mlines: " << content_size + << ")."); } } - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Neither sdp_mline_index nor sdp_mid specified."); + return LOG_ERROR(RTCError(RTCErrorType::INVALID_PARAMETER) + << "Neither sdp_mline_index nor sdp_mid specified."); } RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { @@ -5589,7 +5616,7 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { RTC_DCHECK_RUN_ON(signaling_thread()); const ContentInfo* voice = GetFirstAudioContent(&desc); if (voice && !voice->rejected && - !rtp_manager()->GetAudioTransceiver()->internal()->channel()) { + !rtp_manager()->GetAudioTransceiver()->internal()->HasChannel()) { auto error = rtp_manager()->GetAudioTransceiver()->internal()->CreateChannel( voice->mid(), pc_->call_ptr(), pc_->configuration()->media_config, @@ -5606,7 +5633,7 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { const ContentInfo* video = GetFirstVideoContent(&desc); if (video && !video->rejected && - !rtp_manager()->GetVideoTransceiver()->internal()->channel()) { + !rtp_manager()->GetVideoTransceiver()->internal()->HasChannel()) { auto error = rtp_manager()->GetVideoTransceiver()->internal()->CreateChannel( video->mid(), pc_->call_ptr(), pc_->configuration()->media_config, @@ -5625,8 +5652,8 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { const ContentInfo* data = GetFirstDataContent(&desc); if (data && !data->rejected && !pc_->CreateDataChannelTransport(data->mid())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create data channel."); + return LOG_ERROR(RTCError(RTCErrorType::INTERNAL_ERROR) + << "Failed to create data channel."); } return RTCError::OK(); @@ -5646,8 +5673,9 @@ void SdpOfferAnswerHandler::GetMediaChannelTeardownTasks( if (auto task = transceiver->internal()->GetClearChannelNetworkTask()) network_tasks.push_back(std::move(task)); if (auto task = transceiver->internal()->GetDeleteChannelWorkerTask( - /*stop_senders=*/true)) + /*stop_senders=*/true)) { worker_tasks.push_back(std::move(task)); + } } } for (const auto& transceiver : list) { @@ -5745,7 +5773,7 @@ SdpOfferAnswerHandler::GetMediaDescriptionOptionsForRejectedData( return options; } -bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( +void SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( ContentSource source, const flat_map& bundle_groups_by_mid) { TRACE_EVENT0("webrtc", @@ -5846,18 +5874,17 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( mid_header_extension_missing_video || pt_demuxing_has_been_used_video_; - // Gather all updates ahead of time so that all channels can be updated in a + // Gather all updates ahead of time so that all transports can be updated in a // single BlockingCall; necessary due to thread guards. - std::vector> channels_to_update; - for (const auto& transceiver : transceivers()->ListInternal()) { - ChannelInterface* channel = transceiver->channel(); + flat_map transports_to_update; + for (RtpTransceiver* transceiver : transceivers()->ListInternal()) { const ContentInfo* content = FindMediaSectionForTransceiver(transceiver, sdesc); - if (!channel || !content) { + if (!content) { continue; } - const MediaType media_type = channel->media_type(); + const MediaType media_type = transceiver->media_type(); if (media_type != MediaType::AUDIO && media_type != MediaType::VIDEO) { continue; } @@ -5891,28 +5918,53 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( } } - channels_to_update.emplace_back(pt_demux_enabled, transceiver->channel()); + // Accumulate the pt_demux_enabled state per transport (represented by mid). + // If any transceiver associated with the transport requires PT demuxing, + // the transport will have it enabled. + transports_to_update[content->mid()] |= pt_demux_enabled; } - if (channels_to_update.empty()) { - return true; + if (transports_to_update.empty()) { + return; } - // TODO(bugs.webrtc.org/11993): This BlockingCall() will also block on the - // network thread for every demuxer sink that needs to be updated. The demuxer - // state needs to be fully (and only) managed on the network thread and once - // that's the case, there's no need to stop by on the worker. Ideally we could - // also do this without blocking. - return context_->worker_thread()->BlockingCall([&channels_to_update]() { - for (const auto& it : channels_to_update) { - if (!it.second->SetPayloadTypeDemuxingEnabled(it.first)) { - // Note that the state has already been irrevocably changed at this - // point. Is it useful to stop the loop? - return false; + context_->network_thread()->BlockingCall([&]() { + JsepTransportController* transport_controller = + pc_->transport_controller_n(); + for (const auto& [mid, pt_demux_enabled] : transports_to_update) { + RtpTransportInternal* rtp_transport = + transport_controller->GetRtpTransport(mid); + if (rtp_transport != nullptr) { + rtp_transport->SetActivePayloadTypeDemuxing(pt_demux_enabled); } } - return true; }); + + // If payload type demuxing is disabled for a transport, clear out any + // unsignaled streams that might have been created previously based on + // payload type matches. This prevents SSRC collisions if those SSRCs + // are later reused. + // TODO(tommi): Could/should we rather issue the calls to the transceivers + // on the network thread? This will post tasks per transceiver to the worker + // thread. A common configuration will have joined worker and network threads, + // so bundling with the network thread may be more efficient. + for (RtpTransceiver* transceiver : transceivers()->ListInternal()) { + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, sdesc); + if (!content) { + continue; + } + auto it = transports_to_update.find(content->mid()); + if (it != transports_to_update.end() && !it->second) { + // TODO: bugs.webrtc.org/42221580 - This will remove *all* unsignaled + // streams (those without an explicitly signaled SSRC), which may include + // streams that were matched to this channel by MID or RID. Ideally we'd + // remove only the streams that were matched based on payload type alone, + // but currently there is no straightforward way to identify those + // streams. + transceiver->ResetUnsignaledRecvStream(); + } + } } bool SdpOfferAnswerHandler::ConfiguredForMedia() const { diff --git a/pc/sdp_offer_answer.h b/pc/sdp_offer_answer.h index 702bec16445..802f518fcf8 100644 --- a/pc/sdp_offer_answer.h +++ b/pc/sdp_offer_answer.h @@ -64,6 +64,7 @@ #include "rtc_base/operations_chain.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" #include "rtc_base/unique_id_generator.h" @@ -147,15 +148,18 @@ class SdpOfferAnswerHandler : public SdpStateProvider { scoped_refptr observer); void SetLocalDescription( scoped_refptr observer); - void SetLocalDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc); - void SetLocalDescription(SetSessionDescriptionObserver* observer); + void SetLocalDescription( + scoped_refptr observer, + std::unique_ptr desc); + void SetLocalDescription( + scoped_refptr observer); void SetRemoteDescription( std::unique_ptr desc, scoped_refptr observer); - void SetRemoteDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc); + void SetRemoteDescription( + scoped_refptr observer, + std::unique_ptr desc); PeerConnectionInterface::RTCConfiguration GetConfiguration(); RTCError SetConfiguration( @@ -170,8 +174,8 @@ class SdpOfferAnswerHandler : public SdpStateProvider { const std::vector& candidates); bool ShouldFireNegotiationNeededEvent(uint32_t event_id); - bool AddStream(MediaStreamInterface* local_stream); - void RemoveStream(MediaStreamInterface* local_stream); + PLAN_B_ONLY bool AddStream(MediaStreamInterface* local_stream); + PLAN_B_ONLY void RemoveStream(MediaStreamInterface* local_stream); std::optional is_caller() const; bool HasNewIceCredentials(); @@ -191,8 +195,8 @@ class SdpOfferAnswerHandler : public SdpStateProvider { std::vector>& network_tasks, std::vector>& worker_tasks); - scoped_refptr local_streams(); - scoped_refptr remote_streams(); + PLAN_B_ONLY scoped_refptr local_streams(); + PLAN_B_ONLY scoped_refptr remote_streams(); bool initial_offerer() { RTC_DCHECK_RUN_ON(signaling_thread()); @@ -278,7 +282,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider { void ApplyRemoteDescriptionUpdateTransceiverState(SdpType sdp_type); // Part of ApplyRemoteDescription steps specific to plan b. - void PlanBUpdateSendersAndReceivers( + PLAN_B_ONLY void PlanBUpdateSendersAndReceivers( const ContentInfo* audio_content, const AudioContentDescription* audio_desc, const ContentInfo* video_content, @@ -316,17 +320,17 @@ class SdpOfferAnswerHandler : public SdpStateProvider { bool IsUnifiedPlan() const; // Signals from MediaStreamObserver. - void OnAudioTrackAdded(AudioTrackInterface* track, - MediaStreamInterface* stream) + PLAN_B_ONLY void OnAudioTrackAdded(AudioTrackInterface* track, + MediaStreamInterface* stream) RTC_RUN_ON(signaling_thread()); - void OnAudioTrackRemoved(AudioTrackInterface* track, - MediaStreamInterface* stream) + PLAN_B_ONLY void OnAudioTrackRemoved(AudioTrackInterface* track, + MediaStreamInterface* stream) RTC_RUN_ON(signaling_thread()); - void OnVideoTrackAdded(VideoTrackInterface* track, - MediaStreamInterface* stream) + PLAN_B_ONLY void OnVideoTrackAdded(VideoTrackInterface* track, + MediaStreamInterface* stream) RTC_RUN_ON(signaling_thread()); - void OnVideoTrackRemoved(VideoTrackInterface* track, - MediaStreamInterface* stream) + PLAN_B_ONLY void OnVideoTrackRemoved(VideoTrackInterface* track, + MediaStreamInterface* stream) RTC_RUN_ON(signaling_thread()); // | desc_type | is the type of the description that caused the rollback. @@ -414,7 +418,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider { void GetOptionsForOffer(const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, MediaSessionOptions* session_options); - void GetOptionsForPlanBOffer( + PLAN_B_ONLY void GetOptionsForPlanBOffer( const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, MediaSessionOptions* session_options) RTC_RUN_ON(signaling_thread()); @@ -428,7 +432,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider { void GetOptionsForAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, MediaSessionOptions* session_options); - void GetOptionsForPlanBAnswer( + PLAN_B_ONLY void GetOptionsForPlanBAnswer( const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, MediaSessionOptions* session_options) RTC_RUN_ON(signaling_thread()); @@ -475,14 +479,15 @@ class SdpOfferAnswerHandler : public SdpStateProvider { // Remove all local and remote senders of type `media_type`. // Called when a media type is rejected (m-line set to port 0). - void RemoveSenders(webrtc::MediaType media_type); + PLAN_B_ONLY void RemoveSenders(webrtc::MediaType media_type); // Loops through the vector of `streams` and finds added and removed // StreamParams since last time this method was called. // For each new or removed StreamParam, OnLocalSenderSeen or // OnLocalSenderRemoved is invoked. - void UpdateLocalSendersPlanB(const std::vector& streams, - webrtc::MediaType media_type); + PLAN_B_ONLY void UpdateLocalSendersPlanB( + const std::vector& streams, + webrtc::MediaType media_type); // Makes sure a MediaStreamTrack is created for each StreamParam in `streams`, // and existing MediaStreamTracks are removed if there is no corresponding @@ -490,10 +495,11 @@ class SdpOfferAnswerHandler : public SdpStateProvider { // is created if it doesn't exist; if false, it's removed if it exists. // `media_type` is the type of the `streams` and can be either audio or video. // If a new MediaStream is created it is added to `new_streams`. - void UpdateRemoteSendersListPlanB(const std::vector& streams, - bool default_track_needed, - webrtc::MediaType media_type, - StreamCollection* new_streams); + PLAN_B_ONLY void UpdateRemoteSendersListPlanB( + const std::vector& streams, + bool default_track_needed, + webrtc::MediaType media_type, + StreamCollection* new_streams); // Enables media channels to allow sending of media. // This enables media to flow on all configured audio/video channels. @@ -543,7 +549,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider { // Allocates media channels based on the `desc`. If `desc` doesn't have // the BUNDLE option, this method will disable BUNDLE in PortAllocator. // This method will also delete any existing media channels before creating. - RTCError CreateChannels(const SessionDescription& desc); + PLAN_B_ONLY RTCError CreateChannels(const SessionDescription& desc); // Generates MediaDescriptionOptions for the `session_opts` based on existing // local description or remote description. @@ -568,7 +574,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider { // Based on number of transceivers per media type, enabled or disable // payload type based demuxing in the affected channels. - bool UpdatePayloadTypeDemuxingState( + void UpdatePayloadTypeDemuxingState( ContentSource source, const flat_map& bundle_groups_by_mid); diff --git a/pc/sdp_offer_answer_unittest.cc b/pc/sdp_offer_answer_unittest.cc index 14d81a170e0..0749978dcd0 100644 --- a/pc/sdp_offer_answer_unittest.cc +++ b/pc/sdp_offer_answer_unittest.cc @@ -57,6 +57,7 @@ #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" // This file contains unit tests that relate to the behavior of the // SdpOfferAnswer module. @@ -153,7 +154,7 @@ class SdpOfferAnswerTest : public ::testing::Test { scoped_refptr pc_factory_; private: - AutoThread main_thread_; + test::RunLoop main_thread_; }; TEST_F(SdpOfferAnswerTest, OnTrackReturnsProxiedObject) { @@ -823,6 +824,46 @@ TEST_F(SdpOfferAnswerTest, SimulcastAnswerWithPayloadType) { EXPECT_TRUE(pc->SetLocalDescription(std::move(answer))); } +TEST_F(SdpOfferAnswerTest, SimulcastOfferWithExcessiveRidsClamped) { + auto pc = CreatePeerConnection(); + + std::string sdp = + "v=0\r\n" + "o=- 4131505339648218884 3 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "t=0 0\r\n" + "a=ice-ufrag:zGWFZ+fVXDeN6UoI/136\r\n" + "a=ice-pwd:9AUNgUqRNI5LSIrC1qFD2iTR\r\n" + "a=fingerprint:sha-256 " + "AD:52:52:E0:B1:37:34:21:0E:15:8E:B7:56:56:7B:B4:39:0E:6D:1C:F5:84:A7:EE:" + "B5:27:3E:30:B1:7D:69:42\r\n" + "a=setup:passive\r\n" + "m=video 9 UDP/TLS/RTP/SAVPF 96\r\n" + "c=IN IP4 0.0.0.0\r\n" + "a=rtcp:9 IN IP4 0.0.0.0\r\n" + "a=mid:0\r\n" + "a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\n" + "a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n" + "a=recvonly\r\n" + "a=rtcp-mux\r\n" + "a=rtcp-rsize\r\n" + "a=rtpmap:96 VP8/90000\r\n"; + + for (int i = 1; i <= 9; ++i) { + sdp += "a=rid:" + std::to_string(i) + " recv\r\n"; + } + sdp += "a=simulcast:recv 1;2;3;4;5;6;7;8;9\r\n"; + + std::unique_ptr offer = + CreateSessionDescription(SdpType::kOffer, sdp); + EXPECT_TRUE(pc->SetRemoteDescription(std::move(offer))); + + auto transceiver = pc->pc()->GetTransceivers()[0]; + // Verify that the number of send encodings is clamped to 3 + // (kMaxSimulcastStreams) + EXPECT_THAT(transceiver->sender()->GetParameters().encodings, SizeIs(3)); +} + TEST_F(SdpOfferAnswerTest, ExpectAllSsrcsSpecifiedInSsrcGroupFid) { auto pc = CreatePeerConnection(); std::string sdp = @@ -1971,30 +2012,30 @@ TEST_F(SdpOfferAnswerTest, SctpInitDisabled) { auto pc2 = CreatePeerConnection("WebRTC-Sctp-Snap/Disabled/"); EXPECT_TRUE(pc1->pc()->CreateDataChannelOrError("dc", nullptr).ok()); auto offer = pc1->CreateOfferAndSetAsLocal(); - ASSERT_NE(offer, nullptr); + ASSERT_THAT(offer, NotNull()); { auto& contents = offer->description()->contents(); - ASSERT_EQ(contents.size(), 1u); + ASSERT_THAT(contents, SizeIs(1)); auto* media_description = contents[0].media_description(); - ASSERT_TRUE(media_description); + ASSERT_THAT(media_description, NotNull()); auto* sctp_description = media_description->as_sctp(); - ASSERT_TRUE(sctp_description); + ASSERT_THAT(sctp_description, NotNull()); EXPECT_FALSE(sctp_description->sctp_init()); } RTCError error; EXPECT_TRUE(pc2->SetRemoteDescription(std::move(offer))); auto answer = pc2->CreateAnswerAndSetAsLocal(); - ASSERT_NE(answer, nullptr); + ASSERT_THAT(answer, NotNull()); { auto& contents = answer->description()->contents(); - ASSERT_EQ(contents.size(), 1u); + ASSERT_THAT(contents, SizeIs(1)); auto* media_description = contents[0].media_description(); - ASSERT_TRUE(media_description); + ASSERT_THAT(media_description, NotNull()); auto* sctp_description = media_description->as_sctp(); - ASSERT_TRUE(sctp_description); + ASSERT_THAT(sctp_description, NotNull()); EXPECT_FALSE(sctp_description->sctp_init()); } @@ -2006,7 +2047,7 @@ TEST_F(SdpOfferAnswerTest, SctpInitWithTrial) { auto pc2 = CreatePeerConnection("WebRTC-Sctp-Snap/Enabled/"); EXPECT_TRUE(pc1->pc()->CreateDataChannelOrError("dc", nullptr).ok()); auto offer = pc1->CreateOfferAndSetAsLocal(); - ASSERT_NE(offer, nullptr); + ASSERT_THAT(offer, NotNull()); { auto& contents = offer->description()->contents(); @@ -2021,7 +2062,7 @@ TEST_F(SdpOfferAnswerTest, SctpInitWithTrial) { RTCError error; EXPECT_TRUE(pc2->SetRemoteDescription(std::move(offer))); auto answer = pc2->CreateAnswerAndSetAsLocal(); - ASSERT_NE(answer, nullptr); + ASSERT_THAT(answer, NotNull()); { auto& contents = answer->description()->contents(); @@ -2061,7 +2102,7 @@ TEST_F(SdpOfferAnswerTest, AnswerNoSctpInitInOffer) { EXPECT_TRUE(pc->SetRemoteDescription(std::move(desc))); auto answer = pc->CreateAnswerAndSetAsLocal(); - ASSERT_NE(answer, nullptr); + ASSERT_THAT(answer, NotNull()); EXPECT_TRUE(answer->ToString(&sdp)); auto& contents = answer->description()->contents(); @@ -2094,6 +2135,31 @@ TEST_F(SdpOfferAnswerTest, AnswerNonBase64SctpInit) { auto desc = CreateSessionDescription(SdpType::kOffer, sdp); EXPECT_EQ(desc, nullptr); } + +TEST_F(SdpOfferAnswerTest, + AnswerFromNewPeerAfterProvisionalAnswerFailsSnapSctpInit) { + auto pc1 = CreatePeerConnection("WebRTC-Sctp-Snap/Enabled/"); + auto pc2 = CreatePeerConnection("WebRTC-Sctp-Snap/Enabled/"); + auto pc3 = CreatePeerConnection("WebRTC-Sctp-Snap/Enabled/"); + EXPECT_TRUE(pc1->pc()->CreateDataChannelOrError("dc", nullptr).ok()); + auto offer = pc1->CreateOfferAndSetAsLocal(); + ASSERT_THAT(offer, NotNull()); + + EXPECT_TRUE(pc2->SetRemoteDescription(offer->Clone())); + EXPECT_TRUE(pc3->SetRemoteDescription(std::move(offer))); + + auto answer2 = pc2->CreateAnswerAndSetAsLocal(); + ASSERT_THAT(answer2, NotNull()); + std::string sdp; + answer2->ToString(&sdp); + auto pranswer2 = CreateSessionDescription(SdpType::kPrAnswer, sdp); + EXPECT_TRUE(pc1->SetRemoteDescription(std::move(pranswer2))); + + // Changing the sctp-init is not supported currently. + auto answer3 = pc3->CreateAnswerAndSetAsLocal(); + ASSERT_THAT(answer3, NotNull()); + EXPECT_FALSE(pc1->SetRemoteDescription(std::move(answer3))); +} #endif // WEBRTC_HAVE_SCTP class SdpOfferAnswerDirectionTest diff --git a/pc/sdp_payload_type_suggester.cc b/pc/sdp_payload_type_suggester.cc index acaca73b838..3b11666f37a 100644 --- a/pc/sdp_payload_type_suggester.cc +++ b/pc/sdp_payload_type_suggester.cc @@ -16,12 +16,15 @@ #include "absl/strings/string_view.h" #include "api/jsep.h" +#include "api/payload_type.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" #include "call/payload_type.h" #include "call/payload_type_picker.h" #include "media/base/codec.h" #include "pc/session_description.h" #include "rtc_base/checks.h" +#include "rtc_base/logging.h" #include "rtc_base/thread.h" #include "rtc_base/trace_event.h" @@ -45,6 +48,8 @@ RTCErrorOr SdpPayloadTypeSuggester::SuggestPayloadType( RTCErrorOr remote_result = remote_recorder.LookupPayloadType(codec); if (remote_result.ok()) { + RTC_LOG(LS_INFO) << "SuggestPayloadType: Found remote mapping for " << codec + << " to PT " << remote_result.value(); RTCErrorOr local_codec = local_recorder.LookupCodec(remote_result.value()); if (!local_codec.ok()) { @@ -55,6 +60,9 @@ RTCErrorOr SdpPayloadTypeSuggester::SuggestPayloadType( } // If we get here, PT is already in use, possibly for something else. // Fall through to SuggestMapping. + } else { + RTC_LOG(LS_INFO) << "SuggestPayloadType: FAILED to find remote mapping for " + << codec; } return payload_type_picker_.SuggestMapping(codec, &local_recorder); } @@ -67,6 +75,34 @@ RTCError SdpPayloadTypeSuggester::AddLocalMapping(absl::string_view mid, return recorder.AddMapping(payload_type, codec); } +RTCErrorOr SdpPayloadTypeSuggester::SuggestRtpHeaderExtensionId( + absl::string_view mid, + const RtpExtension& extension, + RtpTransceiverIdDomain id_domain) { + RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); + BundleTypeRecorder& bundle_recorder = LookupBundleRecorder(mid); + auto result = bundle_recorder.header_extensions().LookupId(extension.uri, + extension.encrypt); + if (result.ok()) { + return result; + } + return rtp_header_extension_picker_.SuggestMapping( + extension.uri, extension.encrypt, extension.id, id_domain, + &bundle_recorder.header_extensions()); +} + +RTCError SdpPayloadTypeSuggester::AddRtpHeaderExtensionMapping( + absl::string_view mid, + const RtpExtension& extension, + bool local) { + RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS(); + BundleTypeRecorder& bundle_recorder = LookupBundleRecorder(mid); + rtp_header_extension_picker_.AddMapping(extension.id, extension.uri, + extension.encrypt); + return bundle_recorder.header_extensions().AddMapping( + extension.id, extension.uri, extension.encrypt); +} + RTCError SdpPayloadTypeSuggester::Update(const SessionDescription* description, bool local, SdpType type) { @@ -91,6 +127,16 @@ RTCError SdpPayloadTypeSuggester::Update(const SessionDescription* description, if (!error.ok()) { return error; } + + BundleTypeRecorder& bundle_recorder = LookupBundleRecorder(content.mid()); + for (const auto& extension : + content.media_description()->rtp_header_extensions()) { + bundle_recorder.header_extensions().AddMapping( + extension.id, extension.uri, extension.encrypt); + } + if (type == SdpType::kAnswer) { + bundle_recorder.header_extensions().Commit(); + } } return RTCError::OK(); } @@ -98,6 +144,13 @@ RTCError SdpPayloadTypeSuggester::Update(const SessionDescription* description, PayloadTypeRecorder& SdpPayloadTypeSuggester::LookupRecorder( absl::string_view mid, bool local) { + BundleTypeRecorder& recorder = LookupBundleRecorder(mid); + return local ? recorder.local_payload_types() + : recorder.remote_payload_types(); +} + +SdpPayloadTypeSuggester::BundleTypeRecorder& +SdpPayloadTypeSuggester::LookupBundleRecorder(absl::string_view mid) { const ContentGroup* group = bundle_manager_.LookupGroupByMid(mid); std::string transport_mapped_name; if (group) { @@ -112,9 +165,7 @@ PayloadTypeRecorder& SdpPayloadTypeSuggester::LookupRecorder( recorder_by_mid_.emplace(std::make_pair( transport_mapped_name, BundleTypeRecorder(payload_type_picker_))); } - BundleTypeRecorder& recorder = recorder_by_mid_.at(transport_mapped_name); - return local ? recorder.local_payload_types() - : recorder.remote_payload_types(); + return recorder_by_mid_.at(transport_mapped_name); } } // namespace webrtc diff --git a/pc/sdp_payload_type_suggester.h b/pc/sdp_payload_type_suggester.h index 76c10a97bd8..a10286d6bd2 100644 --- a/pc/sdp_payload_type_suggester.h +++ b/pc/sdp_payload_type_suggester.h @@ -19,8 +19,10 @@ #include "absl/strings/string_view.h" #include "api/jsep.h" +#include "api/payload_type.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" #include "call/payload_type.h" #include "call/payload_type_picker.h" #include "media/base/codec.h" @@ -41,6 +43,16 @@ class SdpPayloadTypeSuggester : public PayloadTypeSuggester { RTCError AddLocalMapping(absl::string_view mid, PayloadType payload_type, const Codec& codec) override; + // Suggest an ID for a given RTP header extension on a given media section. + RTCErrorOr SuggestRtpHeaderExtensionId( + absl::string_view mid, + const RtpExtension& extension, + RtpTransceiverIdDomain id_domain) override; + // Register an RTP header extension ID as mapped to a specific extension. + [[nodiscard]] RTCError AddRtpHeaderExtensionMapping( + absl::string_view mid, + const RtpExtension& extension, + bool local) override; // Updating the bundle mappings and recording PT assignments RTCError Update(const SessionDescription* description, bool local, @@ -57,13 +69,19 @@ class SdpPayloadTypeSuggester : public PayloadTypeSuggester { PayloadTypeRecorder& remote_payload_types() { return remote_payload_types_; } + RtpHeaderExtensionRecorder& header_extensions() { + return header_extensions_; + } private: PayloadTypeRecorder local_payload_types_; PayloadTypeRecorder remote_payload_types_; + RtpHeaderExtensionRecorder header_extensions_; }; PayloadTypeRecorder& LookupRecorder(absl::string_view mid, bool local); + BundleTypeRecorder& LookupBundleRecorder(absl::string_view mid); PayloadTypePicker payload_type_picker_; + RtpHeaderExtensionPicker rtp_header_extension_picker_; // Record of bundle groups, used for looking up payload type suggesters. // This class also exists on the network thread, in JsepTransportController. BundleManager bundle_manager_; diff --git a/pc/sdp_payload_type_suggester_unittest.cc b/pc/sdp_payload_type_suggester_unittest.cc index d543d1b1fbd..16108226156 100644 --- a/pc/sdp_payload_type_suggester_unittest.cc +++ b/pc/sdp_payload_type_suggester_unittest.cc @@ -16,9 +16,9 @@ #include "absl/strings/string_view.h" #include "api/jsep.h" +#include "api/payload_type.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" -#include "call/payload_type.h" #include "media/base/codec.h" #include "media/base/media_constants.h" #include "pc/session_description.h" @@ -48,7 +48,8 @@ class SdpPayloadTypeSuggesterTest : public testing::Test { }; TEST_F(SdpPayloadTypeSuggesterTest, SuggestPayloadTypeBasic) { - Codec pcmu_codec = CreateAudioCodec(-1, kPcmuCodecName, 8000, 1); + Codec pcmu_codec = + CreateAudioCodec(PayloadType::NotSet(), kPcmuCodecName, 8000, 1); RTCErrorOr pcmu_pt = suggester_.SuggestPayloadType("mid", pcmu_codec); ASSERT_TRUE(pcmu_pt.ok()); @@ -63,7 +64,8 @@ TEST_F(SdpPayloadTypeSuggesterTest, SuggestPayloadTypeReusesRemotePayloadType) { offer->contents()[0].media_description()->set_codecs({remote_lyra_codec}); EXPECT_TRUE( suggester_.Update(offer.get(), /* local= */ false, SdpType::kOffer).ok()); - Codec local_lyra_codec = CreateAudioCodec(-1, "lyra", 8000, 1); + Codec local_lyra_codec = + CreateAudioCodec(PayloadType::NotSet(), "lyra", 8000, 1); RTCErrorOr lyra_pt = suggester_.SuggestPayloadType(kAudioMid1, local_lyra_codec); ASSERT_TRUE(lyra_pt.ok()); @@ -81,12 +83,14 @@ TEST_F(SdpPayloadTypeSuggesterTest, EXPECT_TRUE( suggester_.Update(offer.get(), /* local= */ false, SdpType::kOffer).ok()); // Check that we get the Opus codec back with the remote PT - Codec local_opus_codec = CreateAudioCodec(-1, "opus", 48000, 2); + Codec local_opus_codec = + CreateAudioCodec(PayloadType::NotSet(), "opus", 48000, 2); RTCErrorOr local_opus_pt = suggester_.SuggestPayloadType(kAudioMid1, local_opus_codec); EXPECT_EQ(local_opus_pt.value(), remote_opus_pt); // Check that we don't get 110 allocated for DTMF, since it's in use for opus - Codec local_other_codec = CreateAudioCodec(-1, kDtmfCodecName, 48000, 1); + Codec local_other_codec = + CreateAudioCodec(PayloadType::NotSet(), kDtmfCodecName, 48000, 1); RTCErrorOr other_pt = suggester_.SuggestPayloadType(kAudioMid1, local_other_codec); ASSERT_TRUE(other_pt.ok()); diff --git a/pc/session_description.h b/pc/session_description.h index 451a74264fd..f72cfba11b4 100644 --- a/pc/session_description.h +++ b/pc/session_description.h @@ -116,19 +116,32 @@ class MediaContentDescription { // This is a transport-wide property, but is signalled in SDP // at the m-line level; its mux category is IDENTICAL-PER-PT, // and only wildcard is allowed. RFC 8888 section 6. + // In an answer, only one of rtcp_fb_ack_transport_cc() and rtcp_fb_ack_ccfb() + // can be true. bool rtcp_fb_ack_ccfb() const { return rtcp_fb_ack_ccfb_; } void set_rtcp_fb_ack_ccfb(bool enable) { rtcp_fb_ack_ccfb_ = enable; } + // Support of Transport-wide congestion control feedback. + // https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01 + // In an answer, only one of rtcp_fb_ack_transport_cc() and rtcp_fb_ack_ccfb() + // can be true. + bool rtcp_fb_ack_transport_cc() const { + for (const auto& codec : codecs_) { + if (codec.feedback_params.Has(FeedbackParam(kRtcpFbParamTransportCc))) { + return true; + } + } + return false; + } + // Returns the preferred RTCP ack type used for congestion control for this // media content or `std::nullopt` if no supported type exists. std::optional preferred_rtcp_cc_ack_type() const { if (rtcp_fb_ack_ccfb_) { return RtcpFeedbackType::CCFB; } - for (const auto& codec : codecs_) { - if (codec.feedback_params.Has(FeedbackParam(kRtcpFbParamTransportCc))) { - return RtcpFeedbackType::TRANSPORT_CC; - } + if (rtcp_fb_ack_transport_cc()) { + return RtcpFeedbackType::TRANSPORT_CC; } return std::nullopt; } diff --git a/pc/simulcast_sdp_serializer.cc b/pc/simulcast_sdp_serializer.cc index 9f3539923dd..68aea17af26 100644 --- a/pc/simulcast_sdp_serializer.cc +++ b/pc/simulcast_sdp_serializer.cc @@ -19,6 +19,7 @@ #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" +#include "api/payload_type.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "media/base/codec.h" @@ -302,7 +303,7 @@ std::string SimulcastSdpSerializer::SerializeRidDescription( if (it == media_desc.codecs().end()) { break; } - if (it->id == Codec::kIdNotSet) { + if (it->id == PayloadType::NotSet()) { RTC_DCHECK_NOTREACHED(); break; } diff --git a/pc/srtp_session.cc b/pc/srtp_session.cc index d1c533c91fc..def6bfab9be 100644 --- a/pc/srtp_session.cc +++ b/pc/srtp_session.cc @@ -12,28 +12,23 @@ #include #include -#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "modules/rtp_rtcp/source/rtp_util.h" -#include "pc/external_hmac.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/ip_address.h" #include "rtc_base/logging.h" -#include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/string_encode.h" #include "rtc_base/synchronization/mutex.h" +#include "rtc_base/text2pcap.h" #include "rtc_base/thread_annotations.h" #include "rtc_base/time_utils.h" #include "system_wrappers/include/metrics.h" #include "third_party/libsrtp/include/srtp.h" -#include "third_party/libsrtp/include/srtp_priv.h" #ifndef SRTP_SRCTP_INDEX_LEN #define SRTP_SRCTP_INDEX_LEN 4 @@ -120,12 +115,6 @@ bool LibSrtpInitializer::IncrementLibsrtpUsageCountAndMaybeInit( RTC_LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err; return false; } - - err = external_crypto_init(); - if (err != srtp_err_status_ok) { - RTC_LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err; - return false; - } } ++usage_count_; return true; @@ -231,41 +220,6 @@ bool SrtpSession::ProtectRtp(CopyOnWriteBuffer& buffer) { return true; } -bool SrtpSession::ProtectRtp(void* p, int in_len, int max_len, int* out_len) { - RTC_DCHECK(thread_checker_.IsCurrent()); - if (!session_) { - RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: no SRTP Session"; - return false; - } - - // Note: the need_len differs from the libsrtp recommendatіon to ensure - // SRTP_MAX_TRAILER_LEN bytes of free space after the data. WebRTC - // never includes a MKI, therefore the amount of bytes added by the - // srtp_protect call is known in advance and depends on the cipher suite. - int need_len = in_len + rtp_auth_tag_len_; // NOLINT - if (max_len < need_len) { - RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet: The buffer length " - << max_len << " is less than the needed " << need_len; - return false; - } - if (dump_plain_rtp_) { - DumpPacket(p, in_len, /*outbound=*/true); - } - - *out_len = in_len; - int err = srtp_protect(session_, p, out_len); - int seq_num = ParseRtpSequenceNumber( - MakeArrayView(reinterpret_cast(p), in_len)); - if (err != srtp_err_status_ok) { - RTC_LOG(LS_WARNING) << "Failed to protect SRTP packet, seqnum=" << seq_num - << ", err=" << err - << ", last seqnum=" << last_send_seq_num_; - return false; - } - last_send_seq_num_ = seq_num; - return true; -} - bool SrtpSession::ProtectRtp(CopyOnWriteBuffer& buffer, int64_t* index) { if (!ProtectRtp(buffer)) { return false; @@ -273,19 +227,6 @@ bool SrtpSession::ProtectRtp(CopyOnWriteBuffer& buffer, int64_t* index) { return (index) ? GetSendStreamPacketIndex(buffer, index) : true; } -bool SrtpSession::ProtectRtp(void* data, - int in_len, - int max_len, - int* out_len, - int64_t* index) { - CopyOnWriteBuffer buffer(static_cast(data), in_len, max_len); - if (!ProtectRtp(buffer)) { - return false; - } - *out_len = buffer.size(); - return (index) ? GetSendStreamPacketIndex(buffer, index) : true; -} - bool SrtpSession::ProtectRtcp(CopyOnWriteBuffer& buffer) { RTC_DCHECK(thread_checker_.IsCurrent()); if (!session_) { @@ -319,35 +260,6 @@ bool SrtpSession::ProtectRtcp(CopyOnWriteBuffer& buffer) { return true; } -bool SrtpSession::ProtectRtcp(void* p, int in_len, int max_len, int* out_len) { - RTC_DCHECK(thread_checker_.IsCurrent()); - if (!session_) { - RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: no SRTP Session"; - return false; - } - - // Note: the need_len differs from the libsrtp recommendatіon to ensure - // SRTP_MAX_TRAILER_LEN bytes of free space after the data. WebRTC - // never includes a MKI, therefore the amount of bytes added by the - // srtp_protect_rtp call is known in advance and depends on the cipher suite. - int need_len = in_len + sizeof(uint32_t) + rtcp_auth_tag_len_; // NOLINT - if (max_len < need_len) { - RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet: The buffer length " - << max_len << " is less than the needed " << need_len; - return false; - } - if (dump_plain_rtp_) { - DumpPacket(p, in_len, /*outbound=*/true); - } - - *out_len = in_len; - int err = srtp_protect_rtcp(session_, p, out_len); - if (err != srtp_err_status_ok) { - RTC_LOG(LS_WARNING) << "Failed to protect SRTCP packet, err=" << err; - return false; - } - return true; -} bool SrtpSession::UnprotectRtp(CopyOnWriteBuffer& buffer) { RTC_DCHECK(thread_checker_.IsCurrent()); @@ -379,35 +291,6 @@ bool SrtpSession::UnprotectRtp(CopyOnWriteBuffer& buffer) { return true; } -bool SrtpSession::UnprotectRtp(void* p, int in_len, int* out_len) { - RTC_DCHECK(thread_checker_.IsCurrent()); - if (!session_) { - RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet: no SRTP Session"; - return false; - } - - *out_len = in_len; - int err = srtp_unprotect(session_, p, out_len); - if (err != srtp_err_status_ok) { - // Limit the error logging to avoid excessive logs when there are lots of - // bad packets. - const int kFailureLogThrottleCount = 100; - if (decryption_failure_count_ % kFailureLogThrottleCount == 0) { - RTC_LOG(LS_WARNING) << "Failed to unprotect SRTP packet, err=" << err - << ", previous failure count: " - << decryption_failure_count_; - } - ++decryption_failure_count_; - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtpUnprotectError", - static_cast(err), kSrtpErrorCodeBoundary); - return false; - } - if (dump_plain_rtp_) { - DumpPacket(p, *out_len, /*outbound=*/false); - } - return true; -} - bool SrtpSession::UnprotectRtcp(CopyOnWriteBuffer& buffer) { RTC_DCHECK(thread_checker_.IsCurrent()); if (!session_) { @@ -430,72 +313,10 @@ bool SrtpSession::UnprotectRtcp(CopyOnWriteBuffer& buffer) { return true; } -bool SrtpSession::UnprotectRtcp(void* p, int in_len, int* out_len) { - RTC_DCHECK(thread_checker_.IsCurrent()); - if (!session_) { - RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet: no SRTP Session"; - return false; - } - - *out_len = in_len; - int err = srtp_unprotect_rtcp(session_, p, out_len); - if (err != srtp_err_status_ok) { - RTC_LOG(LS_WARNING) << "Failed to unprotect SRTCP packet, err=" << err; - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SrtcpUnprotectError", - static_cast(err), kSrtpErrorCodeBoundary); - return false; - } - if (dump_plain_rtp_) { - DumpPacket(p, *out_len, /*outbound=*/false); - } - return true; -} - -bool SrtpSession::GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len) { - RTC_DCHECK(thread_checker_.IsCurrent()); - RTC_DCHECK(IsExternalAuthActive()); - if (!IsExternalAuthActive()) { - return false; - } - - ExternalHmacContext* external_hmac = nullptr; - // stream_template will be the reference context for other streams. - // Let's use it for getting the keys. - srtp_stream_ctx_t* srtp_context = session_->stream_template; - if (srtp_context && srtp_context->session_keys && - srtp_context->session_keys->rtp_auth) { - external_hmac = reinterpret_cast( - srtp_context->session_keys->rtp_auth->state); - } - - if (!external_hmac) { - RTC_LOG(LS_ERROR) << "Failed to get auth keys from libsrtp!."; - return false; - } - - *key = external_hmac->key; - *key_len = external_hmac->key_length; - *tag_len = rtp_auth_tag_len_; - return true; -} - int SrtpSession::GetSrtpOverhead() const { return rtp_auth_tag_len_; } -void SrtpSession::EnableExternalAuth() { - RTC_DCHECK(!session_); - external_auth_enabled_ = true; -} - -bool SrtpSession::IsExternalAuthEnabled() const { - return external_auth_enabled_; -} - -bool SrtpSession::IsExternalAuthActive() const { - return external_auth_active_; -} - bool SrtpSession::RemoveSsrcFromSession(uint32_t ssrc) { RTC_DCHECK(session_); // libSRTP expects the SSRC to be in network byte order. @@ -551,16 +372,6 @@ bool SrtpSession::DoSetKey(int type, // TODO(astor) parse window size from WSH session-param policy.window_size = 1024; policy.allow_repeat_tx = 1; - // If external authentication option is enabled, supply custom auth module - // id EXTERNAL_HMAC_SHA1 in the policy structure. - // We want to set this option only for rtp packets. - // By default policy structure is initialized to HMAC_SHA1. - // Enable external HMAC authentication only for outgoing streams and only - // for cipher suites that support it (i.e. only non-GCM cipher suites). - if (type == ssrc_any_outbound && IsExternalAuthEnabled() && - !IsGcmCryptoSuite(crypto_suite)) { - policy.rtp.auth_type = EXTERNAL_HMAC_SHA1; - } if (!extension_ids.empty()) { policy.enc_xtn_hdr = const_cast(&extension_ids[0]); policy.enc_xtn_hdr_count = static_cast(extension_ids.size()); @@ -585,7 +396,6 @@ bool SrtpSession::DoSetKey(int type, rtp_auth_tag_len_ = policy.rtp.auth_tag_len; rtcp_auth_tag_len_ = policy.rtcp.auth_tag_len; - external_auth_active_ = (policy.rtp.auth_type == EXTERNAL_HMAC_SHA1); return true; } @@ -669,25 +479,10 @@ void SrtpSession::HandleEventThunk(srtp_event_data_t* ev) { // The resulting file can be replayed using the WebRTC video_replay tool and // be inspected in Wireshark using the RTP, VP8 and H264 dissectors. void SrtpSession::DumpPacket(const CopyOnWriteBuffer& buffer, bool outbound) { - int64_t time_of_day = TimeUTCMillis() % (24 * 3600 * 1000); - int64_t hours = time_of_day / (3600 * 1000); - int64_t minutes = (time_of_day / (60 * 1000)) % 60; - int64_t seconds = (time_of_day / 1000) % 60; - int64_t millis = time_of_day % 1000; - RTC_LOG(LS_VERBOSE) - << "\n" - << (outbound ? "O" : "I") << " " << std::setfill('0') << std::setw(2) - << hours << ":" << std::setfill('0') << std::setw(2) << minutes << ":" - << std::setfill('0') << std::setw(2) << seconds << "." - << std::setfill('0') << std::setw(3) << millis << " " << "000000 " - << hex_encode_with_delimiter( - absl::string_view(buffer.data(), buffer.size()), ' ') - << " # RTP_DUMP"; -} - -void SrtpSession::DumpPacket(const void* buf, int len, bool outbound) { - const CopyOnWriteBuffer buffer(static_cast(buf), len, len); - DumpPacket(buffer, outbound); + RTC_LOG(LS_VERBOSE) << "\n" + << Text2Pcap::DumpPacket(outbound, buffer, + TimeUTCMillis()) + << " # RTP_DUMP"; } } // namespace webrtc diff --git a/pc/srtp_session.h b/pc/srtp_session.h index 18918549f15..3e4581ca838 100644 --- a/pc/srtp_session.h +++ b/pc/srtp_session.h @@ -62,53 +62,17 @@ class SrtpSession { // Encrypts/signs an individual RTP/RTCP packet, in-place. // If an HMAC is used, this will increase the packet size. - [[deprecated("Pass CopyOnWriteBuffer")]] bool ProtectRtp(void* data, - int in_len, - int max_len, - int* out_len); - bool ProtectRtp(CopyOnWriteBuffer& buffer); - // Overloaded version, outputs packet index. - [[deprecated("Pass CopyOnWriteBuffer")]] bool ProtectRtp(void* data, - int in_len, - int max_len, - int* out_len, - int64_t* index); bool ProtectRtp(CopyOnWriteBuffer& buffer, int64_t* index); + bool ProtectRtp(CopyOnWriteBuffer& buffer); - [[deprecated("Pass CopyOnWriteBuffer")]] bool ProtectRtcp(void* data, - int in_len, - int max_len, - int* out_len); bool ProtectRtcp(CopyOnWriteBuffer& buffer); // Decrypts/verifies an invidiual RTP/RTCP packet. // If an HMAC is used, this will decrease the packet size. - [[deprecated("Pass CopyOnWriteBuffer")]] bool UnprotectRtp(void* data, - int in_len, - int* out_len); bool UnprotectRtp(CopyOnWriteBuffer& buffer); - [[deprecated("Pass CopyOnWriteBuffer")]] bool UnprotectRtcp(void* data, - int in_len, - int* out_len); bool UnprotectRtcp(CopyOnWriteBuffer& buffer); - // Helper method to get authentication params. - bool GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len); - int GetSrtpOverhead() const; - // If external auth is enabled, SRTP will write a dummy auth tag that then - // later must get replaced before the packet is sent out. Only supported for - // non-GCM cipher suites and can be checked through "IsExternalAuthActive" - // if it is actually used. This method is only valid before the RTP params - // have been set. - void EnableExternalAuth(); - bool IsExternalAuthEnabled() const; - - // A SRTP session supports external creation of the auth tag if a non-GCM - // cipher is used. This method is only valid after the RTP params have - // been set. - bool IsExternalAuthActive() const; - // Removes a SSRC from the underlying libSRTP session. // Note: this should only be done for SSRCs that are received. // Removing SSRCs that were sent and then reusing them leads to @@ -136,9 +100,6 @@ class SrtpSession { // Writes unencrypted packets in text2pcap format to the log file // for debugging. void DumpPacket(const CopyOnWriteBuffer& buffer, bool outbound); - [[deprecated("Pass CopyOnWriteBuffer")]] void DumpPacket(const void* buf, - int len, - bool outbound); void HandleEvent(const srtp_event_data_t* ev); static void HandleEventThunk(srtp_event_data_t* ev); @@ -155,8 +116,6 @@ class SrtpSession { bool inited_ = false; int last_send_seq_num_ = -1; - bool external_auth_active_ = false; - bool external_auth_enabled_ = false; int decryption_failure_count_ = 0; bool dump_plain_rtp_ = false; }; diff --git a/pc/srtp_session_unittest.cc b/pc/srtp_session_unittest.cc index 79a87ec9c55..743fd12d18c 100644 --- a/pc/srtp_session_unittest.cc +++ b/pc/srtp_session_unittest.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "api/field_trials.h" @@ -213,35 +214,50 @@ TEST_F(SrtpSessionTest, TestReplay) { kEncryptedHeaderExtensionIds)); // Initial sequence number. - SetBE16(rtp_packet_.MutableData() + 2, seqnum_big); + SetBE16( + std::span(rtp_packet_.MutableData(), rtp_packet_.size()) + .subspan(2, 2), + seqnum_big); EXPECT_TRUE(s1_.ProtectRtp(rtp_packet_)); rtp_packet_.SetData(kPcmuFrame, sizeof(kPcmuFrame)); // Replay within the 1024 window should succeed. - SetBE16(rtp_packet_.MutableData() + 2, - seqnum_big - replay_window + 1); + SetBE16( + std::span(rtp_packet_.MutableData(), rtp_packet_.size()) + .subspan(2, 2), + seqnum_big - replay_window + 1); EXPECT_TRUE(s1_.ProtectRtp(rtp_packet_)); rtp_packet_.SetData(kPcmuFrame, sizeof(kPcmuFrame)); // Replay out side of the 1024 window should fail. - SetBE16(rtp_packet_.MutableData() + 2, - seqnum_big - replay_window - 1); + SetBE16( + std::span(rtp_packet_.MutableData(), rtp_packet_.size()) + .subspan(2, 2), + seqnum_big - replay_window - 1); EXPECT_FALSE(s1_.ProtectRtp(rtp_packet_)); rtp_packet_.SetData(kPcmuFrame, sizeof(kPcmuFrame)); // Increment sequence number to a small number. - SetBE16(rtp_packet_.MutableData() + 2, seqnum_small); + SetBE16( + std::span(rtp_packet_.MutableData(), rtp_packet_.size()) + .subspan(2, 2), + seqnum_small); EXPECT_TRUE(s1_.ProtectRtp(rtp_packet_)); // Replay around 0 but out side of the 1024 window should fail. - SetBE16(rtp_packet_.MutableData() + 2, - kMaxSeqnum + seqnum_small - replay_window - 1); + SetBE16( + std::span(rtp_packet_.MutableData(), rtp_packet_.size()) + .subspan(2, 2), + kMaxSeqnum + seqnum_small - replay_window - 1); EXPECT_FALSE(s1_.ProtectRtp(rtp_packet_)); rtp_packet_.SetData(kPcmuFrame, sizeof(kPcmuFrame)); // Replay around 0 but within the 1024 window should succeed. for (uint16_t seqnum = 65000; seqnum < 65003; ++seqnum) { - SetBE16(rtp_packet_.MutableData() + 2, seqnum); + SetBE16(std::span(rtp_packet_.MutableData(), + rtp_packet_.size()) + .subspan(2, 2), + seqnum); EXPECT_TRUE(s1_.ProtectRtp(rtp_packet_)); rtp_packet_.SetData(kPcmuFrame, sizeof(kPcmuFrame)); } @@ -251,7 +267,10 @@ TEST_F(SrtpSessionTest, TestReplay) { // without the fix, the loop above would keep incrementing local sequence // number in libsrtp, eventually the new sequence number would go out side // of the window. - SetBE16(rtp_packet_.MutableData() + 2, seqnum_small + 1); + SetBE16( + std::span(rtp_packet_.MutableData(), rtp_packet_.size()) + .subspan(2, 2), + seqnum_small + 1); EXPECT_TRUE(s1_.ProtectRtp(rtp_packet_)); } diff --git a/pc/srtp_transport.cc b/pc/srtp_transport.cc index 5c2c3c930ad..ae843d6110c 100644 --- a/pc/srtp_transport.cc +++ b/pc/srtp_transport.cc @@ -15,7 +15,6 @@ #include #include -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/units/timestamp.h" #include "call/rtp_demuxer.h" @@ -49,39 +48,8 @@ bool SrtpTransport::SendRtpPacket(CopyOnWriteBuffer* packet, << "Failed to send the packet because SRTP transport is inactive."; return false; } - AsyncSocketPacketOptions updated_options = options; TRACE_EVENT0("webrtc", "SRTP Encode"); - // If ENABLE_EXTERNAL_AUTH flag is on then packet authentication is not done - // inside libsrtp for a RTP packet. A external HMAC module will be writing - // a fake HMAC value. This is ONLY done for a RTP packet. - // Socket layer will update rtp sendtime extension header if present in - // packet with current time before updating the HMAC. - bool res; -#if !defined(ENABLE_EXTERNAL_AUTH) - res = ProtectRtp(*packet); -#else - if (!IsExternalAuthActive()) { - res = ProtectRtp(*packet); - } else { - updated_options.packet_time_params.rtp_sendtime_extension_id = - rtp_abs_sendtime_extn_id_; - res = ProtectRtp(*packet, - &updated_options.packet_time_params.srtp_packet_index); - // If protection succeeds, let's get auth params from srtp. - if (res) { - uint8_t* auth_key = nullptr; - int key_len = 0; - res = GetRtpAuthParams( - &auth_key, &key_len, - &updated_options.packet_time_params.srtp_auth_tag_len); - if (res) { - updated_options.packet_time_params.srtp_auth_key.resize(key_len); - updated_options.packet_time_params.srtp_auth_key.assign( - auth_key, auth_key + key_len); - } - } - } -#endif + bool res = ProtectRtp(*packet); if (!res) { uint16_t seq_num = ParseRtpSequenceNumber(*packet); uint32_t ssrc = ParseRtpSsrc(*packet); @@ -90,7 +58,7 @@ bool SrtpTransport::SendRtpPacket(CopyOnWriteBuffer* packet, return false; } - return RtpTransport::SendRtpPacket(packet, updated_options, flags); + return RtpTransport::SendRtpPacket(packet, options, flags); } bool SrtpTransport::SendRtcpPacket(CopyOnWriteBuffer* packet, @@ -276,9 +244,6 @@ void SrtpTransport::ResetParams() { void SrtpTransport::CreateSrtpSessions() { send_session_.reset(new SrtpSession(field_trials_)); recv_session_.reset(new SrtpSession(field_trials_)); - if (external_auth_enabled_) { - send_session_->EnableExternalAuth(); - } } bool SrtpTransport::ProtectRtp(CopyOnWriteBuffer& buffer) { @@ -334,18 +299,6 @@ bool SrtpTransport::UnprotectRtcp(CopyOnWriteBuffer& buffer) { } } -bool SrtpTransport::GetRtpAuthParams(uint8_t** key, - int* key_len, - int* tag_len) { - if (!IsSrtpActive()) { - RTC_LOG(LS_WARNING) << "Failed to GetRtpAuthParams: SRTP not active"; - return false; - } - - RTC_CHECK(send_session_); - return send_session_->GetRtpAuthParams(key, key_len, tag_len); -} - bool SrtpTransport::GetSrtpOverhead(int* srtp_overhead) const { if (!IsSrtpActive()) { RTC_LOG(LS_WARNING) << "Failed to GetSrtpOverhead: SRTP not active"; @@ -357,26 +310,6 @@ bool SrtpTransport::GetSrtpOverhead(int* srtp_overhead) const { return true; } -void SrtpTransport::EnableExternalAuth() { - RTC_DCHECK(!IsSrtpActive()); - external_auth_enabled_ = true; -} - -bool SrtpTransport::IsExternalAuthEnabled() const { - return external_auth_enabled_; -} - -bool SrtpTransport::IsExternalAuthActive() const { - if (!IsSrtpActive()) { - RTC_LOG(LS_WARNING) - << "Failed to check IsExternalAuthActive: SRTP not active"; - return false; - } - - RTC_CHECK(send_session_); - return send_session_->IsExternalAuthActive(); -} - void SrtpTransport::MaybeUpdateWritableState() { bool writable = IsWritable(/*rtcp=*/true) && IsWritable(/*rtcp=*/false); // Only fire the signal if the writable state changes. diff --git a/pc/srtp_transport.h b/pc/srtp_transport.h index e14ebb41209..e8b10d78c01 100644 --- a/pc/srtp_transport.h +++ b/pc/srtp_transport.h @@ -77,31 +77,9 @@ class SrtpTransport : public RtpTransport { void ResetParams(); - // If external auth is enabled, SRTP will write a dummy auth tag that then - // later must get replaced before the packet is sent out. Only supported for - // non-GCM crypto suites and can be checked through "IsExternalAuthActive" - // if it is actually used. This method is only valid before the RTP params - // have been set. - void EnableExternalAuth(); - bool IsExternalAuthEnabled() const; - - // A SrtpTransport supports external creation of the auth tag if a non-GCM - // cipher is used. This method is only valid after the RTP params have - // been set. - bool IsExternalAuthActive() const; - // Returns srtp overhead for rtp packets. bool GetSrtpOverhead(int* srtp_overhead) const; - // Returns rtp auth params from srtp context. - bool GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len); - - // Cache RTP Absoulute SendTime extension header ID. This is only used when - // external authentication is enabled. - void CacheRtpAbsSendTimeHeaderExtension(int rtp_abs_sendtime_extn_id) { - rtp_abs_sendtime_extn_id_ = rtp_abs_sendtime_extn_id; - } - // In addition to unregistering the sink, the SRTP transport // disassociates all SSRCs of the sink from libSRTP. bool UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) override; @@ -136,6 +114,8 @@ class SrtpTransport : public RtpTransport { std::unique_ptr send_session_; std::unique_ptr recv_session_; + // Non-muxed RTCP requires different SRTP sessions as it leads to + // separate DTLS handshakes. std::unique_ptr send_rtcp_session_; std::unique_ptr recv_rtcp_session_; @@ -146,10 +126,6 @@ class SrtpTransport : public RtpTransport { bool writable_ = false; - bool external_auth_enabled_ = false; - - int rtp_abs_sendtime_extn_id_ = -1; - int decryption_failure_count_ = 0; const FieldTrialsView& field_trials_; diff --git a/pc/srtp_transport_unittest.cc b/pc/srtp_transport_unittest.cc index eb89889e50f..163c5095db6 100644 --- a/pc/srtp_transport_unittest.cc +++ b/pc/srtp_transport_unittest.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -29,13 +30,12 @@ #include "rtc_base/async_packet_socket.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" -#include "rtc_base/checks.h" #include "rtc_base/containers/flat_set.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/thread.h" #include "test/create_test_field_trials.h" #include "test/gtest.h" +#include "test/run_loop.h" using ::webrtc::kSrtpAeadAes128Gcm; using ::webrtc::kTestKey1; @@ -105,34 +105,6 @@ class SrtpTransportTest : public ::testing::Test { } } - // With external auth enabled, SRTP doesn't write the auth tag and - // unprotect would fail. Check accessing the information about the - // tag instead, similar to what the actual code would do that relies - // on external auth. - void TestRtpAuthParams(SrtpTransport* transport, int crypto_suite) { - int overhead; - EXPECT_TRUE(transport->GetSrtpOverhead(&overhead)); - switch (crypto_suite) { - case kSrtpAes128CmSha1_32: - EXPECT_EQ(32 / 8, overhead); // 32-bit tag. - break; - case kSrtpAes128CmSha1_80: - EXPECT_EQ(80 / 8, overhead); // 80-bit tag. - break; - default: - RTC_DCHECK_NOTREACHED(); - break; - } - - uint8_t* auth_key = nullptr; - int key_len = 0; - int tag_len = 0; - EXPECT_TRUE(transport->GetRtpAuthParams(&auth_key, &key_len, &tag_len)); - EXPECT_NE(nullptr, auth_key); - EXPECT_EQ(160 / 8, key_len); // Length of SHA-1 is 160 bits. - EXPECT_EQ(overhead, tag_len); - } - void TestSendRecvRtpPacket(int crypto_suite) { size_t rtp_len = sizeof(kPcmuFrame); size_t packet_size = rtp_len + rtp_auth_tag_len(crypto_suite); @@ -141,7 +113,7 @@ class SrtpTransportTest : public ::testing::Test { memcpy(rtp_packet_data, kPcmuFrame, rtp_len); // In order to be able to run this test function multiple times we can not // use the same sequence number twice. Increase the sequence number by one. - SetBE16(reinterpret_cast(rtp_packet_data) + 2, + SetBE16(std::span(rtp_packet_buffer).subspan(2), ++sequence_number_); CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len, packet_size); CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len, packet_size); @@ -154,34 +126,26 @@ class SrtpTransportTest : public ::testing::Test { // that the packet can be successfully received and decrypted. ASSERT_TRUE(srtp_transport1_->SendRtpPacket(&rtp_packet1to2, options, PF_SRTP_BYPASS)); - if (srtp_transport1_->IsExternalAuthActive()) { - TestRtpAuthParams(srtp_transport1_.get(), crypto_suite); - } else { - ASSERT_TRUE(rtp_sink2_.last_recv_rtp_packet().data()); - EXPECT_EQ(0, memcmp(rtp_sink2_.last_recv_rtp_packet().data(), - original_rtp_data, rtp_len)); - // Get the encrypted packet from underneath packet transport and verify - // the data is actually encrypted. - auto fake_rtp_packet_transport = static_cast( - srtp_transport1_->rtp_packet_transport()); - EXPECT_NE(0, memcmp(fake_rtp_packet_transport->last_sent_packet()->data(), - original_rtp_data, rtp_len)); - } + ASSERT_TRUE(rtp_sink2_.last_recv_rtp_packet().data()); + EXPECT_EQ(0, memcmp(rtp_sink2_.last_recv_rtp_packet().data(), + original_rtp_data, rtp_len)); + // Get the encrypted packet from underneath packet transport and verify + // the data is actually encrypted. + auto fake_rtp_packet_transport = static_cast( + srtp_transport1_->rtp_packet_transport()); + EXPECT_NE(0, memcmp(fake_rtp_packet_transport->last_sent_packet()->data(), + original_rtp_data, rtp_len)); // Do the same thing in the opposite direction; ASSERT_TRUE(srtp_transport2_->SendRtpPacket(&rtp_packet2to1, options, PF_SRTP_BYPASS)); - if (srtp_transport2_->IsExternalAuthActive()) { - TestRtpAuthParams(srtp_transport2_.get(), crypto_suite); - } else { - ASSERT_TRUE(rtp_sink1_.last_recv_rtp_packet().data()); - EXPECT_EQ(0, memcmp(rtp_sink1_.last_recv_rtp_packet().data(), - original_rtp_data, rtp_len)); - auto fake_rtp_packet_transport = static_cast( - srtp_transport2_->rtp_packet_transport()); - EXPECT_NE(0, memcmp(fake_rtp_packet_transport->last_sent_packet()->data(), - original_rtp_data, rtp_len)); - } + ASSERT_TRUE(rtp_sink1_.last_recv_rtp_packet().data()); + EXPECT_EQ(0, memcmp(rtp_sink1_.last_recv_rtp_packet().data(), + original_rtp_data, rtp_len)); + fake_rtp_packet_transport = static_cast( + srtp_transport2_->rtp_packet_transport()); + EXPECT_NE(0, memcmp(fake_rtp_packet_transport->last_sent_packet()->data(), + original_rtp_data, rtp_len)); } void TestSendRecvRtcpPacket(int crypto_suite) { @@ -222,15 +186,10 @@ class SrtpTransportTest : public ::testing::Test { rtcp_packet_data, rtcp_len)); } - void TestSendRecvPacket(bool enable_external_auth, - int crypto_suite, + void TestSendRecvPacket(int crypto_suite, const ZeroOnFreeBuffer& key1, const ZeroOnFreeBuffer& key2) { EXPECT_EQ(key1.size(), key2.size()); - if (enable_external_auth) { - srtp_transport1_->EnableExternalAuth(); - srtp_transport2_->EnableExternalAuth(); - } std::vector extension_ids; EXPECT_TRUE(srtp_transport1_->SetRtpParams( crypto_suite, key1, extension_ids, crypto_suite, key2, extension_ids)); @@ -242,13 +201,6 @@ class SrtpTransportTest : public ::testing::Test { crypto_suite, key2, extension_ids, crypto_suite, key1, extension_ids)); EXPECT_TRUE(srtp_transport1_->IsSrtpActive()); EXPECT_TRUE(srtp_transport2_->IsSrtpActive()); - if (IsGcmCryptoSuite(crypto_suite)) { - EXPECT_FALSE(srtp_transport1_->IsExternalAuthActive()); - EXPECT_FALSE(srtp_transport2_->IsExternalAuthActive()); - } else if (enable_external_auth) { - EXPECT_TRUE(srtp_transport1_->IsExternalAuthActive()); - EXPECT_TRUE(srtp_transport2_->IsExternalAuthActive()); - } TestSendRecvRtpPacket(crypto_suite); TestSendRecvRtcpPacket(crypto_suite); } @@ -263,7 +215,7 @@ class SrtpTransportTest : public ::testing::Test { memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len); // In order to be able to run this test function multiple times we can not // use the same sequence number twice. Increase the sequence number by one. - SetBE16(reinterpret_cast(rtp_packet_data) + 2, + SetBE16(std::span(rtp_packet_buffer).subspan(2), ++sequence_number_); CopyOnWriteBuffer rtp_packet1to2(rtp_packet_data, rtp_len, packet_size); CopyOnWriteBuffer rtp_packet2to1(rtp_packet_data, rtp_len, packet_size); @@ -325,12 +277,10 @@ class SrtpTransportTest : public ::testing::Test { key1, encrypted_headers)); EXPECT_TRUE(srtp_transport1_->IsSrtpActive()); EXPECT_TRUE(srtp_transport2_->IsSrtpActive()); - EXPECT_FALSE(srtp_transport1_->IsExternalAuthActive()); - EXPECT_FALSE(srtp_transport2_->IsExternalAuthActive()); TestSendRecvPacketWithEncryptedHeaderExtension(crypto_suite, encrypted_headers); } - AutoThread main_thread; + test::RunLoop main_thread; std::unique_ptr srtp_transport1_; std::unique_ptr srtp_transport2_; @@ -345,15 +295,8 @@ class SrtpTransportTest : public ::testing::Test { FieldTrials field_trials_ = CreateTestFieldTrials(); }; -class SrtpTransportTestWithExternalAuth - : public SrtpTransportTest, - public ::testing::WithParamInterface {}; - -TEST_P(SrtpTransportTestWithExternalAuth, - SendAndRecvPacket_AES_CM_128_HMAC_SHA1_80) { - bool enable_external_auth = GetParam(); - TestSendRecvPacket(enable_external_auth, kSrtpAes128CmSha1_80, kTestKey1, - kTestKey2); +TEST_F(SrtpTransportTest, SendAndRecvPacket_AES_CM_128_HMAC_SHA1_80) { + TestSendRecvPacket(kSrtpAes128CmSha1_80, kTestKey1, kTestKey2); } TEST_F(SrtpTransportTest, @@ -362,11 +305,8 @@ TEST_F(SrtpTransportTest, kTestKey2); } -TEST_P(SrtpTransportTestWithExternalAuth, - SendAndRecvPacket_AES_CM_128_HMAC_SHA1_32) { - bool enable_external_auth = GetParam(); - TestSendRecvPacket(enable_external_auth, kSrtpAes128CmSha1_32, kTestKey1, - kTestKey2); +TEST_F(SrtpTransportTest, SendAndRecvPacket_AES_CM_128_HMAC_SHA1_32) { + TestSendRecvPacket(kSrtpAes128CmSha1_32, kTestKey1, kTestKey2); } TEST_F(SrtpTransportTest, @@ -375,11 +315,8 @@ TEST_F(SrtpTransportTest, kTestKey2); } -TEST_P(SrtpTransportTestWithExternalAuth, - SendAndRecvPacket_kSrtpAeadAes128Gcm) { - bool enable_external_auth = GetParam(); - TestSendRecvPacket(enable_external_auth, kSrtpAeadAes128Gcm, kTestKeyGcm128_1, - kTestKeyGcm128_2); +TEST_F(SrtpTransportTest, SendAndRecvPacket_kSrtpAeadAes128Gcm) { + TestSendRecvPacket(kSrtpAeadAes128Gcm, kTestKeyGcm128_1, kTestKeyGcm128_2); } TEST_F(SrtpTransportTest, @@ -388,11 +325,8 @@ TEST_F(SrtpTransportTest, kTestKeyGcm128_2); } -TEST_P(SrtpTransportTestWithExternalAuth, - SendAndRecvPacket_kSrtpAeadAes256Gcm) { - bool enable_external_auth = GetParam(); - TestSendRecvPacket(enable_external_auth, kSrtpAeadAes256Gcm, kTestKeyGcm256_1, - kTestKeyGcm256_2); +TEST_F(SrtpTransportTest, SendAndRecvPacket_kSrtpAeadAes256Gcm) { + TestSendRecvPacket(kSrtpAeadAes256Gcm, kTestKeyGcm256_1, kTestKeyGcm256_2); } TEST_F(SrtpTransportTest, @@ -401,11 +335,6 @@ TEST_F(SrtpTransportTest, kTestKeyGcm256_2); } -// Run all tests both with and without external auth enabled. -INSTANTIATE_TEST_SUITE_P(ExternalAuth, - SrtpTransportTestWithExternalAuth, - ::testing::Values(true, false)); - // Test directly setting the params with bogus keys. TEST_F(SrtpTransportTest, TestSetParamsKeyTooShort) { std::vector extension_ids; diff --git a/pc/test/fake_audio_capture_module.cc b/pc/test/fake_audio_capture_module.cc index e55a8af65e3..29b57ef63dd 100644 --- a/pc/test/fake_audio_capture_module.cc +++ b/pc/test/fake_audio_capture_module.cc @@ -403,24 +403,16 @@ bool FakeAudioCaptureModule::Initialize() { } void FakeAudioCaptureModule::SetSendBuffer(int value) { - Sample* buffer_ptr = reinterpret_cast(send_buffer_); - const size_t buffer_size_in_samples = - sizeof(send_buffer_) / kNumberBytesPerSample; - for (size_t i = 0; i < buffer_size_in_samples; ++i) { - buffer_ptr[i] = value; - } + send_buffer_.fill(value); } void FakeAudioCaptureModule::ResetRecBuffer() { - memset(rec_buffer_, 0, sizeof(rec_buffer_)); + rec_buffer_.fill(0); } bool FakeAudioCaptureModule::CheckRecBuffer(int value) { - const Sample* buffer_ptr = reinterpret_cast(rec_buffer_); - const size_t buffer_size_in_samples = - sizeof(rec_buffer_) / kNumberBytesPerSample; - for (size_t i = 0; i < buffer_size_in_samples; ++i) { - if (buffer_ptr[i] >= value) + for (Sample sample : rec_buffer_) { + if (sample >= value) return true; } return false; @@ -498,7 +490,7 @@ void FakeAudioCaptureModule::ReceiveFrameP() { int64_t ntp_time_ms = 0; if (audio_callback_->NeedMorePlayData(kNumberSamples, kNumberBytesPerSample, kNumberOfChannels, kSamplesPerSecond, - rec_buffer_, nSamplesOut, + rec_buffer_.data(), nSamplesOut, &elapsed_time_ms, &ntp_time_ms) != 0) { RTC_DCHECK_NOTREACHED(); } @@ -523,7 +515,7 @@ void FakeAudioCaptureModule::SendFrameP() { bool key_pressed = false; uint32_t current_mic_level = current_mic_level_; if (audio_callback_->RecordedDataIsAvailable( - send_buffer_, kNumberSamples, kNumberBytesPerSample, + send_buffer_.data(), kNumberSamples, kNumberBytesPerSample, kNumberOfChannels, kSamplesPerSecond, kTotalDelayMs, kClockDriftMs, current_mic_level, key_pressed, current_mic_level) != 0) { RTC_DCHECK_NOTREACHED(); diff --git a/pc/test/fake_audio_capture_module.h b/pc/test/fake_audio_capture_module.h index 06a71c937e9..0e5578d93dd 100644 --- a/pc/test/fake_audio_capture_module.h +++ b/pc/test/fake_audio_capture_module.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -215,9 +216,9 @@ class FakeAudioCaptureModule : public webrtc::AudioDeviceModule { std::unique_ptr process_thread_; // Buffer for storing samples received from the webrtc::AudioTransport. - char rec_buffer_[kNumberSamples * kNumberBytesPerSample]; + std::array rec_buffer_; // Buffer for samples to send to the webrtc::AudioTransport. - char send_buffer_[kNumberSamples * kNumberBytesPerSample]; + std::array send_buffer_; // Counter of frames received that have samples of high enough amplitude to // indicate that the frames are not faked somewhere in the audio pipeline diff --git a/pc/test/fake_audio_capture_module_unittest.cc b/pc/test/fake_audio_capture_module_unittest.cc index 42d1a7202e9..63d35804324 100644 --- a/pc/test/fake_audio_capture_module_unittest.cc +++ b/pc/test/fake_audio_capture_module_unittest.cc @@ -19,9 +19,9 @@ #include "api/test/rtc_error_matchers.h" #include "api/units/time_delta.h" #include "rtc_base/synchronization/mutex.h" -#include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" class FakeAdmTest : public ::testing::Test, public webrtc::AudioTransport { @@ -127,7 +127,7 @@ class FakeAdmTest : public ::testing::Test, public webrtc::AudioTransport { return min_buffer_size; } - webrtc::AutoThread main_thread_; + webrtc::test::RunLoop main_thread_; mutable webrtc::Mutex mutex_; diff --git a/pc/test/fake_audio_track.h b/pc/test/fake_audio_track.h new file mode 100644 index 00000000000..42b001686eb --- /dev/null +++ b/pc/test/fake_audio_track.h @@ -0,0 +1,79 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef PC_TEST_FAKE_AUDIO_TRACK_H_ +#define PC_TEST_FAKE_AUDIO_TRACK_H_ + +#include + +#include "api/make_ref_counted.h" +#include "api/media_stream_interface.h" +#include "api/media_stream_track.h" +#include "api/scoped_refptr.h" + +namespace webrtc { + +class FakeAudioProcessor : public AudioProcessorInterface { + public: + FakeAudioProcessor() = default; + ~FakeAudioProcessor() override = default; + + AudioProcessorInterface::AudioProcessorStatistics GetStats( + bool /*has_recv_streams*/) override { + return return_stats ? stats + : AudioProcessorInterface::AudioProcessorStatistics(); + } + + AudioProcessorInterface::AudioProcessorStatistics stats; + bool return_stats = true; +}; + +class FakeAudioTrack : public MediaStreamTrack { + public: + static scoped_refptr Create( + const std::string& id, + MediaStreamTrackInterface::TrackState state, + scoped_refptr processor = nullptr) { + auto audio_track = make_ref_counted(id, processor); + audio_track->set_state(state); + return audio_track; + } + + explicit FakeAudioTrack(const std::string& id, + scoped_refptr processor = nullptr) + : MediaStreamTrack(id), + processor_(processor ? processor + : make_ref_counted()) {} + + std::string kind() const override { + return MediaStreamTrackInterface::kAudioKind; + } + AudioSourceInterface* GetSource() const override { return nullptr; } + void AddSink(AudioTrackSinkInterface* sink) override {} + void RemoveSink(AudioTrackSinkInterface* sink) override {} + bool GetSignalLevel(int* level) override { + *level = 1; + return true; + } + scoped_refptr GetAudioProcessor() override { + return processor_; + } + + void set_processor(scoped_refptr processor) { + processor_ = processor; + } + + private: + scoped_refptr processor_; +}; + +} // namespace webrtc + +#endif // PC_TEST_FAKE_AUDIO_TRACK_H_ diff --git a/pc/test/fake_peer_connection_base.h b/pc/test/fake_peer_connection_base.h index 43088fdb7e7..bcea4102be5 100644 --- a/pc/test/fake_peer_connection_base.h +++ b/pc/test/fake_peer_connection_base.h @@ -67,6 +67,7 @@ #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" namespace webrtc { @@ -79,18 +80,23 @@ class FakePeerConnectionBase : public PeerConnectionInternal { public: // PeerConnectionInterface implementation. FakePeerConnectionBase() : env_(CreateEnvironment()) {} + explicit FakePeerConnectionBase(const Environment& env) : env_(env) {} - scoped_refptr local_streams() override { + PLAN_B_ONLY scoped_refptr local_streams() + override { return nullptr; } - scoped_refptr remote_streams() override { + PLAN_B_ONLY scoped_refptr remote_streams() + override { return nullptr; } - bool AddStream(MediaStreamInterface* stream) override { return false; } + PLAN_B_ONLY bool AddStream(MediaStreamInterface* stream) override { + return false; + } - void RemoveStream(MediaStreamInterface* stream) override {} + PLAN_B_ONLY void RemoveStream(MediaStreamInterface* stream) override {} RTCErrorOr> AddTrack( scoped_refptr track, @@ -132,7 +138,7 @@ class FakePeerConnectionBase : public PeerConnectionInternal { return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); } - scoped_refptr CreateSender( + PLAN_B_ONLY scoped_refptr CreateSender( const std::string& kind, const std::string& stream_id) override { return nullptr; @@ -152,9 +158,9 @@ class FakePeerConnectionBase : public PeerConnectionInternal { return {}; } - bool GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track, - StatsOutputLevel level) override { + [[deprecated]] bool GetStats(StatsObserver* observer, + MediaStreamTrackInterface* track, + StatsOutputLevel level) override { return false; } @@ -224,10 +230,11 @@ class FakePeerConnectionBase : public PeerConnectionInternal { return true; } - RTCConfiguration GetConfiguration() override { return RTCConfiguration(); } + RTCConfiguration GetConfiguration() override { return config_; } RTCError SetConfiguration( const PeerConnectionInterface::RTCConfiguration& config) override { + config_ = config; return RTCError(); } @@ -350,7 +357,7 @@ class FakePeerConnectionBase : public PeerConnectionInternal { const PeerConnectionInterface::RTCConfiguration* configuration() const override { - return nullptr; + return &config_; } void ReportSdpBundleUsage( @@ -423,6 +430,7 @@ class FakePeerConnectionBase : public PeerConnectionInternal { protected: Environment env_; PayloadTypePicker payload_type_picker_; + RTCConfiguration config_; }; static_assert( diff --git a/pc/test/fake_peer_connection_for_stats.h b/pc/test/fake_peer_connection_for_stats.h index 4e66989d017..c2f0f07dda1 100644 --- a/pc/test/fake_peer_connection_for_stats.h +++ b/pc/test/fake_peer_connection_for_stats.h @@ -20,12 +20,12 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "api/audio/audio_device.h" #include "api/audio_options.h" #include "api/crypto/crypto_options.h" #include "api/environment/environment.h" -#include "api/environment/environment_factory.h" #include "api/ice_transport_interface.h" #include "api/jsep.h" #include "api/make_ref_counted.h" @@ -36,6 +36,7 @@ #include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" +#include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/transport/data_channel_transport_interface.h" #include "call/call.h" @@ -72,6 +73,7 @@ #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_certificate.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/unique_id_generator.h" @@ -111,7 +113,23 @@ class FakeVoiceMediaSendChannelForStats : public FakeVoiceMediaSendChannel { return false; } + absl::AnyInvocable()> GetStatsCallback() + override { + return [this, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VoiceMediaSendInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; + } + private: + ScopedTaskSafety task_safety_; std::optional send_stats_; }; @@ -138,7 +156,23 @@ class FakeVoiceMediaReceiveChannelForStats return false; } + absl::AnyInvocable()> GetStatsCallback( + bool get_and_clear_legacy_stats) override { + return [this, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VoiceMediaReceiveInfo info; + if (GetStats(&info, /*get_and_clear_legacy_stats=*/false)) { + return info; + } + return std::nullopt; + }; + } + private: + ScopedTaskSafety task_safety_; std::optional receive_stats_; }; @@ -164,7 +198,23 @@ class FakeVideoMediaSendChannelForStats : public FakeVideoMediaSendChannel { return false; } + absl::AnyInvocable()> GetStatsCallback() + override { + return [this, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VideoMediaSendInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; + } + private: + ScopedTaskSafety task_safety_; std::optional send_stats_; }; @@ -189,7 +239,23 @@ class FakeVideoMediaReceiveChannelForStats return false; } + absl::AnyInvocable()> GetStatsCallback() + override { + return [this, safety = task_safety_.flag()]() mutable + -> std::optional { + if (!safety->alive()) { + return std::nullopt; + } + VideoMediaReceiveInfo info; + if (GetStats(&info)) { + return info; + } + return std::nullopt; + }; + } + private: + ScopedTaskSafety task_safety_; std::optional receive_stats_; }; @@ -270,20 +336,22 @@ class VideoChannelForTesting : public VideoChannel { class FakePeerConnectionForStats : public FakePeerConnectionBase, public JsepTransportController::Observer { public: - // TODO(steveanton): Add support for specifying separate threads to test - // multi-threading correctness. - FakePeerConnectionForStats() - : network_thread_(Thread::Current()), - worker_thread_(Thread::Current()), + explicit FakePeerConnectionForStats( + const Environment& env, + Thread* worker_thread = Thread::Current(), + Thread* network_thread = Thread::Current()) + : FakePeerConnectionBase(env), + network_thread_(network_thread), + worker_thread_(worker_thread), signaling_thread_(Thread::Current()), // TODO(hta): remove separate thread variables and use context. - env_(CreateEnvironment()), - dependencies_(MakeDependencies()), - context_(ConnectionContext::Create(env_, &dependencies_)), + dependencies_( + MakeDependencies(signaling_thread_, worker_thread, network_thread)), + context_(ConnectionContext::Create(env, &dependencies_)), local_streams_(StreamCollection::Create()), remote_streams_(StreamCollection::Create()), data_channel_controller_(network_thread_), - codec_lookup_helper_(context_.get(), env_.field_trials()), + codec_lookup_helper_(context_.get(), env.field_trials()), ice_transport_factory_(std::make_unique()) { JsepTransportController::Config config; config.ice_transport_factory = ice_transport_factory_.get(); @@ -293,7 +361,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, config.un_demuxable_packet_handler = [](const RtpPacketReceived& parsed_packet) {}; transport_controller_ = std::make_unique( - env_, signaling_thread_, network_thread_, /*port_allocator=*/nullptr, + env, signaling_thread_, network_thread_, /*port_allocator=*/nullptr, /*async_dns_resolver_factory=*/nullptr, /*lna_permission_factory=*/nullptr, std::move(config)); } @@ -302,13 +370,17 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, for (auto transceiver : transceivers_) { transceiver->internal()->ClearChannel(); } + network_thread_->BlockingCall([&]() { transport_controller_.reset(); }); } - static PeerConnectionFactoryDependencies MakeDependencies() { + static PeerConnectionFactoryDependencies MakeDependencies( + Thread* signaling_thread, + Thread* worker_thread, + Thread* network_thread) { PeerConnectionFactoryDependencies dependencies; - dependencies.network_thread = Thread::Current(); - dependencies.worker_thread = Thread::Current(); - dependencies.signaling_thread = Thread::Current(); + dependencies.network_thread = network_thread; + dependencies.worker_thread = worker_thread; + dependencies.signaling_thread = signaling_thread; EnableFakeMedia(dependencies); return dependencies; } @@ -321,7 +393,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, return remote_streams_; } - scoped_refptr AddSender( + PLAN_B_ONLY scoped_refptr AddSender( scoped_refptr sender) { // TODO(steveanton): Switch tests to use RtpTransceivers directly. auto sender_proxy = RtpSenderProxyWithInternal::Create( @@ -332,13 +404,13 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, return sender_proxy; } - void RemoveSender(scoped_refptr sender) { + PLAN_B_ONLY void RemoveSender(scoped_refptr sender) { GetOrCreateFirstTransceiverOfType(sender->media_type()) ->internal() ->RemoveSenderPlanB(sender.get()); } - scoped_refptr AddReceiver( + PLAN_B_ONLY scoped_refptr AddReceiver( scoped_refptr receiver) { // TODO(steveanton): Switch tests to use RtpTransceivers directly. auto receiver_proxy = @@ -350,14 +422,15 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, return receiver_proxy; } - void RemoveReceiver(scoped_refptr receiver) { + PLAN_B_ONLY void RemoveReceiver( + scoped_refptr receiver) { GetOrCreateFirstTransceiverOfType(receiver->media_type()) ->internal() ->RemoveReceiverPlanB(receiver.get()); } - std::pair + PLAN_B_ONLY std::pair AddVoiceChannel(const std::string& mid, const std::string& transport_name, VoiceMediaInfo initial_stats = VoiceMediaInfo()) { @@ -375,24 +448,28 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, auto transceiver = GetOrCreateFirstTransceiverOfType(webrtc::MediaType::AUDIO, mid) ->internal(); - if (transceiver->channel()) { + if (transceiver->HasChannel()) { // This transceiver already has a channel, create a new one. transceiver = CreateTransceiverOfType(webrtc::MediaType::AUDIO, mid)->internal(); } - RTC_DCHECK(!transceiver->channel()); + RTC_DCHECK(!transceiver->HasChannel()); RTC_DCHECK(transceiver->mid()); - transceiver->SetChannel(std::move(voice_channel), - [](const std::string&) { return nullptr; }); + UpdateJsepTransportController(mid, transport_name); + transceiver->SetChannel( + std::move(voice_channel), [this](const std::string& mid) { + return transport_controller_->GetRtpTransport(mid); + }); + auto dtls_transport = transport_controller_->LookupDtlsTransportByMid(mid); + transceiver->SetTransport(dtls_transport, transport_name); voice_media_send_channel_ptr->SetStats(initial_stats); voice_media_receive_channel_ptr->SetStats(initial_stats); - UpdateJsepTransportController(mid, transport_name); return std::make_pair(voice_media_send_channel_ptr, voice_media_receive_channel_ptr); } - std::pair + PLAN_B_ONLY std::pair AddVideoChannel(const std::string& mid, const std::string& transport_name, VideoMediaInfo initial_stats = VideoMediaInfo()) { @@ -410,18 +487,22 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, auto transceiver = GetOrCreateFirstTransceiverOfType(webrtc::MediaType::VIDEO, mid) ->internal(); - if (transceiver->channel()) { + if (transceiver->HasChannel()) { // This transceiver already has a channel, create a new one. transceiver = CreateTransceiverOfType(webrtc::MediaType::VIDEO, mid)->internal(); } - RTC_DCHECK(!transceiver->channel()); + RTC_DCHECK(!transceiver->HasChannel()); RTC_DCHECK(transceiver->mid()); - transceiver->SetChannel(std::move(video_channel), - [](const std::string&) { return nullptr; }); + UpdateJsepTransportController(mid, transport_name); + transceiver->SetChannel( + std::move(video_channel), [this](const std::string& mid) { + return transport_controller_->GetRtpTransport(mid); + }); + auto dtls_transport = transport_controller_->LookupDtlsTransportByMid(mid); + transceiver->SetTransport(dtls_transport, transport_name); video_media_send_channel_ptr->SetStats(initial_stats); video_media_receive_channel_ptr->SetStats(initial_stats); - UpdateJsepTransportController(mid, transport_name); return std::make_pair(video_media_send_channel_ptr, video_media_receive_channel_ptr); } @@ -476,20 +557,24 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, // PeerConnectionInterface overrides. - scoped_refptr local_streams() override { + PLAN_B_ONLY scoped_refptr local_streams() + override { return local_streams_; } - scoped_refptr remote_streams() override { + PLAN_B_ONLY scoped_refptr remote_streams() + override { return remote_streams_; } std::vector> GetSenders() const override { std::vector> senders; for (auto transceiver : transceivers_) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (auto sender : transceiver->internal()->senders()) { senders.push_back(sender); } + RTC_ALLOW_PLAN_B_DEPRECATION_END() } return senders; } @@ -498,9 +583,11 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, const override { std::vector> receivers; for (auto transceiver : transceivers_) { + RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() for (auto receiver : transceiver->internal()->receivers()) { receivers.push_back(receiver); } + RTC_ALLOW_PLAN_B_DEPRECATION_END() } return receivers; } @@ -665,7 +752,6 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase, Thread* const worker_thread_; Thread* const signaling_thread_; - Environment env_; PeerConnectionFactoryDependencies dependencies_; scoped_refptr context_; diff --git a/pc/test/fake_rtc_certificate_generator.h b/pc/test/fake_rtc_certificate_generator.h index 2253366e496..c17037900ab 100644 --- a/pc/test/fake_rtc_certificate_generator.h +++ b/pc/test/fake_rtc_certificate_generator.h @@ -11,6 +11,7 @@ #ifndef PC_TEST_FAKE_RTC_CERTIFICATE_GENERATOR_H_ #define PC_TEST_FAKE_RTC_CERTIFICATE_GENERATOR_H_ +#include #include #include #include @@ -25,9 +26,11 @@ #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/ssl_identity.h" +namespace webrtc { + // RSA with mod size 1024, pub exp 0x10001. -static const webrtc::RTCCertificatePEM kRsaPems[] = { - webrtc::RTCCertificatePEM( +static const std::array kRsaPems{ + RTCCertificatePEM( "-----BEGIN RSA PRI" // Linebreak to avoid detection of private "VATE KEY-----\n" // keys by linters. "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n" @@ -56,7 +59,7 @@ static const webrtc::RTCCertificatePEM kRsaPems[] = { "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n" "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n" "-----END CERTIFICATE-----\n"), - webrtc::RTCCertificatePEM( + RTCCertificatePEM( "-----BEGIN RSA PRI" // Linebreak to avoid detection of private "VATE KEY-----\n" // keys by linters. "MIICXQIBAAKBgQDeYqlyJ1wuiMsi905e3X81/WA/G3ym50PIDZBVtSwZi7JVQPgj\n" @@ -94,8 +97,8 @@ static const webrtc::RTCCertificatePEM kRsaPems[] = { // `SSLIdentity::Create` and invoking `identity->PrivateKeyToPEMString()`, // `identity->PublicKeyToPEMString()` and // `identity->certificate().ToPEMString()`. -static const webrtc::RTCCertificatePEM kEcdsaPems[] = { - webrtc::RTCCertificatePEM( +static const std::array kEcdsaPems{ + RTCCertificatePEM( "-----BEGIN PRI" // Linebreak to avoid detection of private "VATE KEY-----\n" // keys by linters. "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+qaRsR5uHtqG689M\n" @@ -110,7 +113,7 @@ static const webrtc::RTCCertificatePEM kEcdsaPems[] = { "vK0wCgYIKoZIzj0EAwIDSQAwRgIhAIIc3+CqfkZ9lLwTj1PvUtt3KhnqF2kD0War\n" "cCoTBbCxAiEAyp9Cn4vo2ZBhRIVDKyoxmwak8Z0PAVhJAQaWCgoY2D4=\n" "-----END CERTIFICATE-----\n"), - webrtc::RTCCertificatePEM( + RTCCertificatePEM( "-----BEGIN PRI" // Linebreak to avoid detection of private "VATE KEY-----\n" // keys by linters. "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQghL/G4JRYnuDNbQuh\n" @@ -126,8 +129,7 @@ static const webrtc::RTCCertificatePEM kEcdsaPems[] = { "1fE/g68CIQD7uoFfbiq6dTp8ZwzbwQ8jJf08KjriamqA9OW/4268Dw==\n" "-----END CERTIFICATE-----\n")}; -class FakeRTCCertificateGenerator - : public webrtc::RTCCertificateGeneratorInterface { +class FakeRTCCertificateGenerator : public RTCCertificateGeneratorInterface { public: FakeRTCCertificateGenerator() : should_fail_(false), should_wait_(false) {} @@ -143,7 +145,7 @@ class FakeRTCCertificateGenerator int generated_certificates() { return generated_certificates_; } int generated_failures() { return generated_failures_; } - void GenerateCertificateAsync(const webrtc::KeyParams& key_params, + void GenerateCertificateAsync(const KeyParams& key_params, const std::optional& expires_ms, Callback callback) override { // The certificates are created from constant PEM strings and use its coded @@ -151,27 +153,27 @@ class FakeRTCCertificateGenerator RTC_DCHECK(!expires_ms); // Only supports RSA-1024-0x10001 and ECDSA-P256. - if (key_params.type() == webrtc::KT_RSA) { + if (key_params.type() == KT_RSA) { RTC_DCHECK_EQ(key_params.rsa_params().mod_size, 1024); RTC_DCHECK_EQ(key_params.rsa_params().pub_exp, 0x10001); } else { - RTC_DCHECK_EQ(key_params.type(), webrtc::KT_ECDSA); - RTC_DCHECK_EQ(key_params.ec_curve(), webrtc::EC_NIST_P256); + RTC_DCHECK_EQ(key_params.type(), KT_ECDSA); + RTC_DCHECK_EQ(key_params.ec_curve(), EC_NIST_P256); } - webrtc::KeyType key_type = key_params.type(); - webrtc::TaskQueueBase::Current()->PostTask(webrtc::SafeTask( - pending_delete_.flag(), - [this, key_type, callback = std::move(callback)]() mutable { - GenerateCertificate(key_type, std::move(callback)); - })); + KeyType key_type = key_params.type(); + TaskQueueBase::Current()->PostTask( + SafeTask(pending_delete_.flag(), + [this, key_type, callback = std::move(callback)]() mutable { + GenerateCertificate(key_type, std::move(callback)); + })); } - static webrtc::scoped_refptr GenerateCertificate() { - switch (webrtc::KT_DEFAULT) { - case webrtc::KT_RSA: - return webrtc::RTCCertificate::FromPEM(kRsaPems[0]); - case webrtc::KT_ECDSA: - return webrtc::RTCCertificate::FromPEM(kEcdsaPems[0]); + static scoped_refptr GenerateCertificate() { + switch (KT_DEFAULT) { + case KT_RSA: + return RTCCertificate::FromPEM(kRsaPems[0]); + case KT_ECDSA: + return RTCCertificate::FromPEM(kEcdsaPems[0]); default: RTC_DCHECK_NOTREACHED(); return nullptr; @@ -179,52 +181,50 @@ class FakeRTCCertificateGenerator } private: - const webrtc::RTCCertificatePEM& get_pem( - const webrtc::KeyType& key_type) const { + const RTCCertificatePEM& get_pem(const KeyType& key_type) const { switch (key_type) { - case webrtc::KT_RSA: + case KT_RSA: return kRsaPems[key_index_]; - case webrtc::KT_ECDSA: + case KT_ECDSA: return kEcdsaPems[key_index_]; default: RTC_DCHECK_NOTREACHED(); return kEcdsaPems[key_index_]; } } - const std::string& get_key(const webrtc::KeyType& key_type) const { + const std::string& get_key(const KeyType& key_type) const { return get_pem(key_type).private_key(); } - const std::string& get_cert(const webrtc::KeyType& key_type) const { + const std::string& get_cert(const KeyType& key_type) const { return get_pem(key_type).certificate(); } - void GenerateCertificate(webrtc::KeyType key_type, Callback callback) { + void GenerateCertificate(KeyType key_type, Callback callback) { // If the certificate generation should be stalled, re-post this same // message to the queue with a small delay so as to wait in a loop until // set_should_wait(false) is called. if (should_wait_) { - webrtc::TaskQueueBase::Current()->PostDelayedTask( - webrtc::SafeTask( - pending_delete_.flag(), - [this, key_type, callback = std::move(callback)]() mutable { - GenerateCertificate(key_type, std::move(callback)); - }), - webrtc::TimeDelta::Millis(1)); + TaskQueueBase::Current()->PostDelayedTask( + SafeTask(pending_delete_.flag(), + [this, key_type, callback = std::move(callback)]() mutable { + GenerateCertificate(key_type, std::move(callback)); + }), + TimeDelta::Millis(1)); return; } if (should_fail_) { ++generated_failures_; std::move(callback)(nullptr); } else { - webrtc::scoped_refptr certificate = - webrtc::RTCCertificate::FromPEM(get_pem(key_type)); + scoped_refptr certificate = + RTCCertificate::FromPEM(get_pem(key_type)); RTC_DCHECK(certificate); ++generated_certificates_; std::move(callback)(std::move(certificate)); } } - webrtc::ScopedTaskSafetyDetached pending_delete_; + ScopedTaskSafetyDetached pending_delete_; bool should_fail_; bool should_wait_; @@ -233,4 +233,5 @@ class FakeRTCCertificateGenerator int generated_failures_ = 0; }; +} // namespace webrtc #endif // PC_TEST_FAKE_RTC_CERTIFICATE_GENERATOR_H_ diff --git a/pc/test/fake_video_track.h b/pc/test/fake_video_track.h new file mode 100644 index 00000000000..ce11982710e --- /dev/null +++ b/pc/test/fake_video_track.h @@ -0,0 +1,60 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef PC_TEST_FAKE_VIDEO_TRACK_H_ +#define PC_TEST_FAKE_VIDEO_TRACK_H_ + +#include +#include + +#include "api/make_ref_counted.h" +#include "api/media_stream_interface.h" +#include "api/media_stream_track.h" +#include "api/scoped_refptr.h" +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" +#include "api/video/video_source_interface.h" + +namespace webrtc { + +class FakeVideoTrack : public MediaStreamTrack { + public: + static scoped_refptr Create( + const std::string& id, + MediaStreamTrackInterface::TrackState state, + scoped_refptr source) { + auto video_track = make_ref_counted(id, std::move(source)); + video_track->set_state(state); + return video_track; + } + + FakeVideoTrack(const std::string& id, + scoped_refptr source) + : MediaStreamTrack(id), source_(source) {} + + std::string kind() const override { + return MediaStreamTrackInterface::kVideoKind; + } + + void AddOrUpdateSink(VideoSinkInterface* sink, + const VideoSinkWants& wants) override {} + void RemoveSink(VideoSinkInterface* sink) override {} + + VideoTrackSourceInterface* GetSource() const override { + return source_.get(); + } + + private: + scoped_refptr source_; +}; + +} // namespace webrtc + +#endif // PC_TEST_FAKE_VIDEO_TRACK_H_ diff --git a/pc/test/fake_video_track_source.h b/pc/test/fake_video_track_source.h index df56f625ca7..241f73a5a54 100644 --- a/pc/test/fake_video_track_source.h +++ b/pc/test/fake_video_track_source.h @@ -12,6 +12,7 @@ #define PC_TEST_FAKE_VIDEO_TRACK_SOURCE_H_ #include "api/make_ref_counted.h" +#include "api/media_stream_interface.h" #include "api/scoped_refptr.h" #include "api/video/video_frame.h" #include "api/video/video_source_interface.h" @@ -31,6 +32,18 @@ class FakeVideoTrackSource : public VideoTrackSource { static scoped_refptr Create() { return Create(false); } bool is_screencast() const override { return is_screencast_; } + bool GetStats(VideoTrackSourceInterface::Stats* stats) override { + if (!stats) + return false; + stats->input_width = width_; + stats->input_height = height_; + return true; + } + + void SetSize(int width, int height) { + width_ = width; + height_ = height; + } void InjectFrame(const VideoFrame& frame) { video_broadcaster_.OnFrame(frame); @@ -47,6 +60,8 @@ class FakeVideoTrackSource : public VideoTrackSource { private: const bool is_screencast_; + int width_ = 0; + int height_ = 0; VideoBroadcaster video_broadcaster_; }; diff --git a/pc/test/integration_test_helpers.h b/pc/test/integration_test_helpers.h index 2d109502100..be76075d71c 100644 --- a/pc/test/integration_test_helpers.h +++ b/pc/test/integration_test_helpers.h @@ -78,6 +78,7 @@ #include "pc/test/fake_rtc_certificate_generator.h" #include "pc/test/fake_video_track_renderer.h" #include "pc/test/mock_peer_connection_observers.h" +#include "pc/test/rtc_stats_obtainer.h" #include "pc/video_track_source.h" #include "rtc_base/checks.h" #include "rtc_base/crypto_random.h" @@ -91,6 +92,7 @@ #include "rtc_base/socket_factory.h" #include "rtc_base/socket_server.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/task_queue_for_test.h" #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" @@ -508,9 +510,12 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, scoped_refptr OldGetStatsForTrack( MediaStreamTrackInterface* track) { auto observer = make_ref_counted(); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" EXPECT_TRUE(peer_connection_->GetStats( observer.get(), nullptr, PeerConnectionInterface::kStatsOutputLevelStandard)); +#pragma clang diagnostic pop EXPECT_THAT( WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()), IsRtcOk()); @@ -524,6 +529,7 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, // Synchronously gets stats and returns them. If it times out, fails the test // and returns null. + // TODO(tommi) - Remove this method in favor of the one that supports RunLoop. scoped_refptr NewGetStats() { auto callback = make_ref_counted(); peer_connection_->GetStats(callback.get()); @@ -533,6 +539,35 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, return callback->report(); } + scoped_refptr NewGetStats(test::RunLoop& run_loop) { + scoped_refptr report; + auto callback = RTCStatsObtainer::Create(&report, run_loop.QuitClosure()); + peer_connection_->GetStats(callback.get()); + run_loop.Run(); + EXPECT_TRUE(report); + return report; + } + + std::string DtlsCipher() { + auto report = NewGetStats(); + if (!report) + return ""; + auto stats = report->GetStatsOfType(); + if (stats.empty() || !stats[0]->dtls_cipher.has_value()) + return ""; + return *stats[0]->dtls_cipher; + } + + std::string SrtpCipher() { + auto report = NewGetStats(); + if (!report) + return ""; + auto stats = report->GetStatsOfType(); + if (stats.empty() || !stats[0]->srtp_cipher.has_value()) + return ""; + return *stats[0]->srtp_cipher; + } + int rendered_width() { EXPECT_FALSE(fake_video_renderers_.empty()); return fake_video_renderers_.empty() @@ -577,14 +612,14 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, local_rendered_height(); } - size_t number_of_remote_streams() { + PLAN_B_ONLY size_t number_of_remote_streams() { if (!pc()) { return 0; } return pc()->remote_streams()->count(); } - StreamCollectionInterface* remote_streams() const { + PLAN_B_ONLY StreamCollectionInterface* remote_streams() const { if (!pc()) { ADD_FAILURE(); return nullptr; @@ -592,7 +627,7 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, return pc()->remote_streams().get(); } - StreamCollectionInterface* local_streams() { + PLAN_B_ONLY StreamCollectionInterface* local_streams() { if (!pc()) { ADD_FAILURE(); return nullptr; @@ -681,7 +716,15 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, std::unique_ptr CreateOfferAndWait() { auto observer = make_ref_counted(); pc()->CreateOffer(observer.get(), offer_answer_options_); - return WaitForDescriptionFromObserver(observer.get()); + EXPECT_TRUE(WaitUntil([&] { return observer->called(); })); + if (!observer->result()) { + return nullptr; + } + auto description = observer->MoveDescription(); + if (generated_sdp_munger_) { + generated_sdp_munger_(description); + } + return description; } bool Rollback() { return SetRemoteDescription(CreateRollbackSessionDescription()); @@ -805,8 +848,11 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, private: // Constructor used by friend class PeerConnectionIntegrationBaseTest. explicit PeerConnectionIntegrationWrapper(const std::string& debug_name, - Environment env) - : debug_name_(debug_name), env_(env) {} + Environment env, + test::RunLoop& run_loop) + : run_loop_(run_loop), debug_name_(debug_name), env_(env) {} + + test::RunLoop& run_loop() const { return run_loop_; } bool Init(const PeerConnectionFactory::Options* options, const PeerConnectionInterface::RTCConfiguration* config, @@ -909,11 +955,6 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, std::unique_ptr CreateAnswer() { auto observer = make_ref_counted(); pc()->CreateAnswer(observer.get(), offer_answer_options_); - return WaitForDescriptionFromObserver(observer.get()); - } - - std::unique_ptr WaitForDescriptionFromObserver( - MockCreateSessionDescriptionObserver* observer) { EXPECT_THAT( WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()), IsRtcOk()); @@ -1154,6 +1195,7 @@ class PeerConnectionIntegrationWrapper : public PeerConnectionObserver, return false; } + test::RunLoop& run_loop_; std::string debug_name_; const Environment env_; @@ -1461,7 +1503,8 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { env.Set(CreateTestFieldTrialsPtr(field_trials)); std::unique_ptr client( - new PeerConnectionIntegrationWrapper(debug_name, env.Create())); + new PeerConnectionIntegrationWrapper(debug_name, env.Create(), + run_loop())); if (!client->Init(options, &modified_config, std::move(dependencies), fss_.get(), network_thread_.get(), worker_thread_.get(), @@ -1869,8 +1912,20 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { ASSERT_THAT(WaitUntil([&] { return DtlsConnected(); }, ::testing::IsTrue()), IsRtcOk()); EXPECT_THAT( - WaitUntil([&] { return caller()->OldGetStats()->SrtpCipher(); }, - ::testing::Eq(SrtpCryptoSuiteToName(expected_cipher_suite))), + WaitUntil( + [&] { + auto report = caller()->NewGetStats(run_loop()); + if (!report) { + return std::string(); + } + auto transport_stats = + report->GetStatsOfType(); + if (transport_stats.empty()) { + return std::string(); + } + return *transport_stats[0]->srtp_cipher; + }, + ::testing::Eq(SrtpCryptoSuiteToName(expected_cipher_suite))), IsRtcOk()); } diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h index c4f96e5fb44..2848905e892 100644 --- a/pc/test/mock_channel_interface.h +++ b/pc/test/mock_channel_interface.h @@ -18,6 +18,7 @@ #include "absl/strings/string_view.h" #include "api/jsep.h" #include "api/media_types.h" +#include "api/rtc_error.h" #include "media/base/media_channel.h" #include "media/base/stream_params.h" #include "pc/channel_interface.h" @@ -26,6 +27,7 @@ #include "test/gmock.h" namespace webrtc { +class RtpPacketReceived; // Mock class for BaseChannel. // Use this class in unit tests to avoid dependency on a specific @@ -60,26 +62,25 @@ class MockChannelInterface : public ChannelInterface { MOCK_METHOD(const std::string&, mid, (), (const, override)); MOCK_METHOD(void, Enable, (bool), (override)); MOCK_METHOD(void, - SetFirstPacketReceivedCallback, - (absl::AnyInvocable), + SetFirstPacketReceivedCallback_n, + (absl::AnyInvocable), (override)); MOCK_METHOD(void, - SetFirstPacketSentCallback, + SetFirstPacketSentCallback_n, (absl::AnyInvocable), (override)); MOCK_METHOD(void, SetPacketReceivedCallback_n, - (absl::AnyInvocable), + (absl::AnyInvocable), (override)); - MOCK_METHOD(bool, + MOCK_METHOD(RTCError, SetLocalContent, - (const webrtc::MediaContentDescription*, SdpType, std::string&), + (const webrtc::MediaContentDescription*, SdpType), (override)); - MOCK_METHOD(bool, + MOCK_METHOD(RTCError, SetRemoteContent, - (const webrtc::MediaContentDescription*, SdpType, std::string&), + (const webrtc::MediaContentDescription*, SdpType), (override)); - MOCK_METHOD(bool, SetPayloadTypeDemuxingEnabled, (bool), (override)); MOCK_METHOD(const std::vector&, local_streams, (), diff --git a/pc/test/mock_peer_connection_internal.h b/pc/test/mock_peer_connection_internal.h index 6f20129aadc..dddc2542748 100644 --- a/pc/test/mock_peer_connection_internal.h +++ b/pc/test/mock_peer_connection_internal.h @@ -132,10 +132,13 @@ class MockPeerConnectionInternal : public PeerConnectionInternal { GetTransceivers, (), (const, override)); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" MOCK_METHOD(bool, GetStats, (StatsObserver*, MediaStreamTrackInterface*, StatsOutputLevel), (override)); +#pragma clang diagnostic pop MOCK_METHOD(void, GetStats, (RTCStatsCollectorCallback*), (override)); MOCK_METHOD(void, GetStats, diff --git a/pc/test/mock_peer_connection_observers.h b/pc/test/mock_peer_connection_observers.h index 172c2572de4..ebe2f217ff3 100644 --- a/pc/test/mock_peer_connection_observers.h +++ b/pc/test/mock_peer_connection_observers.h @@ -44,6 +44,7 @@ #include "rtc_base/checks.h" #include "rtc_base/string_encode.h" #include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/plan_b_only.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" @@ -93,17 +94,19 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { state_ = new_state; } - MediaStreamInterface* RemoteStream(const std::string& label) { + PLAN_B_ONLY MediaStreamInterface* RemoteStream(const std::string& label) { return remote_streams_->find(label); } - StreamCollectionInterface* remote_streams() const { + PLAN_B_ONLY StreamCollectionInterface* remote_streams() const { return remote_streams_.get(); } - void OnAddStream(scoped_refptr stream) override { + PLAN_B_ONLY void OnAddStream( + scoped_refptr stream) override { last_added_stream_ = stream; remote_streams_->AddStream(stream); } - void OnRemoveStream(scoped_refptr stream) override { + PLAN_B_ONLY void OnRemoveStream( + scoped_refptr stream) override { last_removed_stream_ = stream; remote_streams_->RemoveStream(stream.get()); } @@ -136,6 +139,16 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { ice_gathering_complete_ = new_state == PeerConnectionInterface::kIceGatheringComplete; callback_triggered_ = true; + if (ice_gathering_complete_ && ice_gathering_complete_callback_) { + // In case the callback modifies `ice_gathering_complete_callback_`. + auto cb = std::move(ice_gathering_complete_callback_); + ice_gathering_complete_callback_ = nullptr; + std::move(cb)(); + } + } + + void SetIceGatheringCompleteCallback(absl::AnyInvocable callback) { + ice_gathering_complete_callback_ = std::move(callback); } void OnIceCandidate(const IceCandidate* candidate) override { RTC_DCHECK(pc_); @@ -143,11 +156,31 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); callback_triggered_ = true; + if (on_ice_candidate_callback_) { + absl::AnyInvocable cb = std::move(on_ice_candidate_callback_); + on_ice_candidate_callback_ = nullptr; + std::move(cb)(); + } + } + + void SetOnIceCandidateCallback(absl::AnyInvocable callback) { + on_ice_candidate_callback_ = std::move(callback); } void OnIceCandidateRemoved(const IceCandidate* candidate) override { ++num_candidates_removed_; callback_triggered_ = true; + if (on_ice_candidate_removed_callback_) { + absl::AnyInvocable cb = + std::move(on_ice_candidate_removed_callback_); + on_ice_candidate_removed_callback_ = nullptr; + std::move(cb)(); + } + } + + void SetOnIceCandidateRemovedCallback( + absl::AnyInvocable callback) { + on_ice_candidate_removed_callback_ = std::move(callback); } void OnIceConnectionReceivingChange(bool receiving) override { @@ -198,12 +231,12 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { // Returns the id of the last added stream. // Empty string if no stream have been added. - std::string GetLastAddedStreamId() { + PLAN_B_ONLY std::string GetLastAddedStreamId() { if (last_added_stream_) return last_added_stream_->id(); return ""; } - std::string GetLastRemovedStreamId() { + PLAN_B_ONLY std::string GetLastRemovedStreamId() { if (last_removed_stream_) return last_removed_stream_->id(); return ""; @@ -238,6 +271,8 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { bool legacy_renegotiation_needed() const { return renegotiation_needed_; } void clear_legacy_renegotiation_needed() { renegotiation_needed_ = false; } + bool ice_gathering_complete() const { return ice_gathering_complete_; } + bool has_negotiation_needed_event() { return latest_negotiation_needed_event_.has_value(); } @@ -256,6 +291,9 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { bool renegotiation_needed_ = false; std::optional latest_negotiation_needed_event_; bool ice_gathering_complete_ = false; + absl::AnyInvocable ice_gathering_complete_callback_; + absl::AnyInvocable on_ice_candidate_callback_; + absl::AnyInvocable on_ice_candidate_removed_callback_; bool ice_connected_ = false; bool callback_triggered_ = false; int num_added_tracks_ = 0; @@ -275,41 +313,46 @@ class MockCreateSessionDescriptionObserver public: MockCreateSessionDescriptionObserver() : called_(false), - error_("MockCreateSessionDescriptionObserver not called") {} + error_("MockCreateSessionDescriptionObserver not called"), + desc_(nullptr) {} + explicit MockCreateSessionDescriptionObserver( + absl::AnyInvocable quit_closure) + : quit_closure_(std::move(quit_closure)), + called_(false), + error_("MockCreateSessionDescriptionObserver not called"), + desc_(nullptr) {} ~MockCreateSessionDescriptionObserver() override {} void OnSuccess(SessionDescriptionInterface* desc) override { - MutexLock lock(&mutex_); called_ = true; error_ = ""; desc_.reset(desc); + if (quit_closure_) + std::move(quit_closure_)(); } void OnFailure(RTCError error) override { - MutexLock lock(&mutex_); called_ = true; error_ = error.message(); + if (quit_closure_) + std::move(quit_closure_)(); } bool called() const { - MutexLock lock(&mutex_); return called_; } bool result() const { - MutexLock lock(&mutex_); return error_.empty(); } const std::string& error() const { - MutexLock lock(&mutex_); return error_; } std::unique_ptr MoveDescription() { - MutexLock lock(&mutex_); return std::move(desc_); } private: - mutable Mutex mutex_; - bool called_ RTC_GUARDED_BY(mutex_); - std::string error_ RTC_GUARDED_BY(mutex_); - std::unique_ptr desc_ RTC_GUARDED_BY(mutex_); + absl::AnyInvocable quit_closure_; + bool called_; + std::string error_; + std::unique_ptr desc_; }; class MockSetSessionDescriptionObserver : public SetSessionDescriptionObserver { diff --git a/pc/test/mock_rtp_receiver_internal.h b/pc/test/mock_rtp_receiver_internal.h index baebd44361e..29fbd4f54da 100644 --- a/pc/test/mock_rtp_receiver_internal.h +++ b/pc/test/mock_rtp_receiver_internal.h @@ -16,6 +16,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/dtls_transport_interface.h" #include "api/media_stream_interface.h" @@ -71,13 +72,19 @@ class MockRtpReceiverInternal : public RtpReceiverInternal { SetMediaChannel, (webrtc::MediaReceiveChannelInterface*), (override)); - MOCK_METHOD(void, SetupMediaChannel, (uint32_t), (override)); - MOCK_METHOD(void, SetupUnsignaledMediaChannel, (), (override)); + MOCK_METHOD(absl::AnyInvocable, + GetSetupForMediaChannel, + (uint32_t), + (override)); + MOCK_METHOD(absl::AnyInvocable, + GetSetupForUnsignaledMediaChannel, + (), + (override)); MOCK_METHOD(std::optional, ssrc, (), (const, override)); - MOCK_METHOD(void, NotifyFirstPacketReceived, (), (override)); + MOCK_METHOD(void, NotifyFirstPacketReceived, (uint32_t), (override)); MOCK_METHOD(void, NotifyFirstPacketReceivedAfterReceptiveChange, - (), + (uint32_t), (override)); MOCK_METHOD(void, set_stream_ids, (std::vector), (override)); MOCK_METHOD(void, diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h index 7fe338791cc..eeba6fac6fc 100644 --- a/pc/test/mock_rtp_sender_internal.h +++ b/pc/test/mock_rtp_sender_internal.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -60,8 +61,15 @@ class MockRtpSenderInternal : public RtpSenderInternal { set_transport, (webrtc::scoped_refptr), (override)); + MOCK_METHOD(void, + SetCachedParameters, + (std::optional), + (override)); MOCK_METHOD(RtpParameters, GetParameters, (), (const, override)); - MOCK_METHOD(RtpParameters, GetParametersInternal, (), (const, override)); + MOCK_METHOD(RtpParameters, + GetParametersInternal, + (bool, bool), + (const, override)); MOCK_METHOD(RtpParameters, GetParametersInternalWithAllLayers, (), @@ -71,7 +79,7 @@ class MockRtpSenderInternal : public RtpSenderInternal { SetParametersAsync, (const RtpParameters&, SetParametersCallback), (override)); - MOCK_METHOD(void, + MOCK_METHOD(RTCError, SetParametersInternal, (const RtpParameters&, SetParametersCallback, bool blocking), (override)); diff --git a/pc/test/mock_voice_media_receive_channel_interface.h b/pc/test/mock_voice_media_receive_channel_interface.h index cb7334f2b9e..feac0eeab03 100644 --- a/pc/test/mock_voice_media_receive_channel_interface.h +++ b/pc/test/mock_voice_media_receive_channel_interface.h @@ -13,10 +13,10 @@ #include #include #include -#include #include #include +#include "absl/functional/any_invocable.h" #include "api/audio_options.h" #include "api/call/audio_sink.h" #include "api/crypto/frame_decryptor_interface.h" @@ -75,6 +75,10 @@ class MockVoiceMediaReceiveChannelInterface GetStats, (webrtc::VoiceMediaReceiveInfo * stats, bool reset_legacy), (override)); + MOCK_METHOD(absl::AnyInvocable()>, + GetStatsCallback, + (bool reset_legacy), + (override)); MOCK_METHOD(::webrtc::RtcpMode, RtcpMode, (), (const, override)); MOCK_METHOD(void, SetRtcpMode, (::webrtc::RtcpMode mode), (override)); MOCK_METHOD(void, SetReceiveNackEnabled, (bool enabled), (override)); @@ -101,18 +105,11 @@ class MockVoiceMediaReceiveChannelInterface SetInterface, (webrtc::MediaChannelNetworkInterface * iface), (override)); - MOCK_METHOD(void, - OnPacketReceived, - (const RtpPacketReceived& packet), - (override)); + MOCK_METHOD(void, OnPacketReceived, (RtpPacketReceived packet), (override)); MOCK_METHOD(std::optional, GetUnsignaledSsrc, (), (const, override)); - MOCK_METHOD(void, - ChooseReceiverReportSsrc, - (const std::set& choices), - (override)); MOCK_METHOD(void, OnDemuxerCriteriaUpdatePending, (), (override)); MOCK_METHOD(void, OnDemuxerCriteriaUpdateComplete, (), (override)); MOCK_METHOD(void, diff --git a/pc/track_media_info_map.cc b/pc/track_media_info_map.cc index 9cf2a9c641e..afe6a36c148 100644 --- a/pc/track_media_info_map.cc +++ b/pc/track_media_info_map.cc @@ -13,10 +13,10 @@ #include #include #include +#include #include #include -#include "api/array_view.h" #include "api/media_types.h" #include "api/rtp_parameters.h" #include "media/base/media_channel.h" @@ -40,21 +40,32 @@ const V* FindAddressOrNull(const flat_map& map, const K& key) { } flat_map GetSenderAttachmentIds( - ArrayView senders, + std::span senders, + std::span sender_parameters, MediaType media_type) { + RTC_DCHECK_EQ(senders.size(), sender_parameters.size()); flat_map result; - for (const auto& sender : senders) { - if (sender.media_type != media_type || sender.ssrc == 0) { + for (size_t i = 0; i < senders.size(); ++i) { + const TrackMediaInfoMap::RtpSenderSignalInfo& sender = senders[i]; + if (sender.media_type != media_type) { continue; } - result[sender.ssrc] = sender.attachment_id; + const RtpParameters& parameters = sender_parameters[i]; + if (sender.ssrc != 0) { + result[sender.ssrc] = sender.attachment_id; + } + for (const RtpEncodingParameters& encoding : parameters.encodings) { + if (encoding.ssrc) { + result[*encoding.ssrc] = sender.attachment_id; + } + } } return result; } flat_map GetReceiverAttachmentIds( - ArrayView receivers, - ArrayView receiver_parameters, + std::span receivers, + std::span receiver_parameters, MediaType media_type) { RTC_DCHECK_EQ(receivers.size(), receiver_parameters.size()); flat_map result; @@ -73,8 +84,8 @@ flat_map GetReceiverAttachmentIds( } flat_map GetReceiverTrackIds( - ArrayView receivers, - ArrayView receiver_parameters, + std::span receivers, + std::span receiver_parameters, MediaType media_type) { RTC_DCHECK_EQ(receivers.size(), receiver_parameters.size()); flat_map result; @@ -131,15 +142,16 @@ flat_map GetVideoSenderInfos( TrackMediaInfoMap::TrackMediaInfoMap( std::optional voice_media_info, std::optional video_media_info, - ArrayView senders, - ArrayView receivers, - ArrayView receiver_parameters) + std::span senders, + std::span sender_parameters, + std::span receivers, + std::span receiver_parameters) : voice_media_info_(std::move(voice_media_info)), video_media_info_(std::move(video_media_info)), audio_sender_attachment_id_by_ssrc_( - GetSenderAttachmentIds(senders, MediaType::AUDIO)), + GetSenderAttachmentIds(senders, sender_parameters, MediaType::AUDIO)), video_sender_attachment_id_by_ssrc_( - GetSenderAttachmentIds(senders, MediaType::VIDEO)), + GetSenderAttachmentIds(senders, sender_parameters, MediaType::VIDEO)), audio_receiver_attachment_id_by_ssrc_( GetReceiverAttachmentIds(receivers, receiver_parameters, diff --git a/pc/track_media_info_map.h b/pc/track_media_info_map.h index 64013015ace..f68ec7504c1 100644 --- a/pc/track_media_info_map.h +++ b/pc/track_media_info_map.h @@ -13,9 +13,9 @@ #include #include +#include #include -#include "api/array_view.h" #include "api/media_types.h" #include "api/rtp_parameters.h" #include "media/base/media_channel.h" @@ -45,9 +45,10 @@ class TrackMediaInfoMap { TrackMediaInfoMap(std::optional voice_media_info, std::optional video_media_info, - ArrayView senders, - ArrayView receivers, - ArrayView receiver_parameters); + std::span senders, + std::span sender_parameters, + std::span receivers, + std::span receiver_parameters); const std::optional& voice_media_info() const { return voice_media_info_; diff --git a/pc/track_media_info_map_unittest.cc b/pc/track_media_info_map_unittest.cc index 01b8cedd2c1..2b50e4078fc 100644 --- a/pc/track_media_info_map_unittest.cc +++ b/pc/track_media_info_map_unittest.cc @@ -33,6 +33,7 @@ #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" using ::testing::ElementsAre; @@ -175,12 +176,14 @@ class TrackMediaInfoMapTest : public ::testing::Test { // into the map. TrackMediaInfoMap InitializeMap() { std::vector sender_infos; + std::vector sender_params; for (const auto& sender : rtp_senders_) { sender_infos.push_back({ .ssrc = sender->ssrc(), .attachment_id = sender->AttachmentId(), .media_type = sender->media_type(), }); + sender_params.push_back(sender->GetParameters()); } std::vector receiver_infos; std::vector receiver_params; @@ -204,11 +207,11 @@ class TrackMediaInfoMapTest : public ::testing::Test { } return TrackMediaInfoMap(std::move(voice_media_info), std::move(video_media_info), sender_infos, - receiver_infos, receiver_params); + sender_params, receiver_infos, receiver_params); } private: - AutoThread main_thread_; + test::RunLoop main_thread_; VoiceMediaInfo voice_media_info_; VideoMediaInfo video_media_info_; diff --git a/pc/used_ids.h b/pc/used_ids.h index ac8f1188149..da51b522f48 100644 --- a/pc/used_ids.h +++ b/pc/used_ids.h @@ -13,7 +13,6 @@ #include #include -#include "api/rtp_parameters.h" #include "media/base/codec.h" #include "rtc_base/checks.h" @@ -122,67 +121,6 @@ class UsedPayloadTypes : public UsedIds { static const int kLastDynamicPayloadTypeUpperRange = 127; }; -// Helper class used for finding duplicate RTP Header extension ids among -// audio and video extensions. -class UsedRtpHeaderExtensionIds : public UsedIds { - public: - enum class IdDomain { - // Only allocate IDs that fit in one-byte header extensions. - kOneByteOnly, - // Prefer to allocate one-byte header extension IDs, but overflow to - // two-byte if none are left. - kTwoByteAllowed, - }; - - explicit UsedRtpHeaderExtensionIds(IdDomain id_domain) - : UsedIds(RtpExtension::kMinId, - id_domain == IdDomain::kTwoByteAllowed - ? RtpExtension::kMaxId - : RtpExtension::kOneByteHeaderExtensionMaxId), - id_domain_(id_domain), - next_extension_id_(RtpExtension::kOneByteHeaderExtensionMaxId) {} - - private: - // Returns the first unused id in reverse order from the max id of one byte - // header extensions. This hopefully reduces the risk of more collisions. We - // want to change the default ids as little as possible. If no unused id is - // found and two byte header extensions are enabled (i.e., - // `extmap_allow_mixed_` is true), search for unused ids from 16 to 255. - int FindUnusedId() override { - if (next_extension_id_ <= RtpExtension::kOneByteHeaderExtensionMaxId) { - // First search in reverse order from the max id of one byte header - // extensions (14). - while (IsIdUsed(next_extension_id_) && - next_extension_id_ >= min_allowed_id_) { - --next_extension_id_; - } - } - - if (id_domain_ == IdDomain::kTwoByteAllowed) { - if (next_extension_id_ < min_allowed_id_) { - // We have searched among all one-byte IDs without finding an unused ID, - // continue at the first two-byte ID (16; avoid 15 since it is somewhat - // special per https://www.rfc-editor.org/rfc/rfc8285#section-4.2 - next_extension_id_ = RtpExtension::kOneByteHeaderExtensionMaxId + 2; - } - - if (next_extension_id_ > RtpExtension::kOneByteHeaderExtensionMaxId) { - while (IsIdUsed(next_extension_id_) && - next_extension_id_ <= max_allowed_id_) { - ++next_extension_id_; - } - } - } - RTC_DCHECK(next_extension_id_ >= min_allowed_id_); - RTC_DCHECK(next_extension_id_ <= max_allowed_id_); - return next_extension_id_; - } - - const IdDomain id_domain_; - int next_extension_id_; -}; - } // namespace webrtc - #endif // PC_USED_IDS_H_ diff --git a/pc/used_ids_unittest.cc b/pc/used_ids_unittest.cc deleted file mode 100644 index 23e0160a07f..00000000000 --- a/pc/used_ids_unittest.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2019 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "pc/used_ids.h" - -#include "absl/strings/string_view.h" -#include "api/rtp_parameters.h" -#include "rtc_base/checks.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -struct Foo { - int id; -}; - -TEST(UsedIdsTest, UniqueIdsAreUnchanged) { - UsedIds used_ids(1, 5); - for (int i = 1; i <= 5; ++i) { - Foo id = {i}; - used_ids.FindAndSetIdUsed(&id); - EXPECT_EQ(id.id, i); - } -} - -TEST(UsedIdsTest, IdsOutsideRangeAreUnchanged) { - UsedIds used_ids(1, 5); - - Foo id_11 = {11}; - Foo id_12 = {12}; - Foo id_12_collision = {12}; - Foo id_13 = {13}; - Foo id_13_collision = {13}; - - used_ids.FindAndSetIdUsed(&id_11); - EXPECT_EQ(id_11.id, 11); - used_ids.FindAndSetIdUsed(&id_12); - EXPECT_EQ(id_12.id, 12); - used_ids.FindAndSetIdUsed(&id_12_collision); - EXPECT_EQ(id_12_collision.id, 12); - used_ids.FindAndSetIdUsed(&id_13); - EXPECT_EQ(id_13.id, 13); - used_ids.FindAndSetIdUsed(&id_13_collision); - EXPECT_EQ(id_13_collision.id, 13); -} - -TEST(UsedIdsTest, CollisionsAreReassignedIdsInReverseOrder) { - UsedIds used_ids(1, 10); - Foo id_1 = {1}; - Foo id_2 = {2}; - Foo id_2_collision = {2}; - Foo id_3 = {3}; - Foo id_3_collision = {3}; - - used_ids.FindAndSetIdUsed(&id_1); - used_ids.FindAndSetIdUsed(&id_2); - used_ids.FindAndSetIdUsed(&id_2_collision); - EXPECT_EQ(id_2_collision.id, 10); - used_ids.FindAndSetIdUsed(&id_3); - used_ids.FindAndSetIdUsed(&id_3_collision); - EXPECT_EQ(id_3_collision.id, 9); -} - -struct TestParams { - UsedRtpHeaderExtensionIds::IdDomain id_domain; - int max_id; -}; - -class UsedRtpHeaderExtensionIdsTest - : public ::testing::TestWithParam {}; - -constexpr TestParams kOneByteTestParams = { - .id_domain = UsedRtpHeaderExtensionIds::IdDomain::kOneByteOnly, - .max_id = 14}; -constexpr TestParams kTwoByteTestParams = { - .id_domain = UsedRtpHeaderExtensionIds::IdDomain::kTwoByteAllowed, - .max_id = 255}; - -INSTANTIATE_TEST_SUITE_P(All, - UsedRtpHeaderExtensionIdsTest, - ::testing::Values(kOneByteTestParams, - kTwoByteTestParams)); - -TEST_P(UsedRtpHeaderExtensionIdsTest, UniqueIdsAreUnchanged) { - UsedRtpHeaderExtensionIds used_ids(GetParam().id_domain); - - // Fill all IDs. - for (int j = 1; j <= GetParam().max_id; ++j) { - RtpExtension extension("", j); - used_ids.FindAndSetIdUsed(&extension); - EXPECT_EQ(extension.id, j); - } -} - -TEST_P(UsedRtpHeaderExtensionIdsTest, PrioritizeReassignmentToOneByteIds) { - UsedRtpHeaderExtensionIds used_ids(GetParam().id_domain); - RtpExtension id_1("", 1); - RtpExtension id_2("", 2); - RtpExtension id_2_collision("", 2); - RtpExtension id_3("", 3); - RtpExtension id_3_collision("", 3); - - // Expect that colliding IDs are reassigned to one-byte IDs. - used_ids.FindAndSetIdUsed(&id_1); - used_ids.FindAndSetIdUsed(&id_2); - used_ids.FindAndSetIdUsed(&id_2_collision); - EXPECT_EQ(id_2_collision.id, 14); - used_ids.FindAndSetIdUsed(&id_3); - used_ids.FindAndSetIdUsed(&id_3_collision); - EXPECT_EQ(id_3_collision.id, 13); -} - -TEST_F(UsedRtpHeaderExtensionIdsTest, TwoByteIdsAllowed) { - UsedRtpHeaderExtensionIds used_ids( - UsedRtpHeaderExtensionIds::IdDomain::kTwoByteAllowed); - - // Fill all one byte IDs. - for (int i = 1; i <= RtpExtension::kOneByteHeaderExtensionMaxId; ++i) { - RtpExtension id("", i); - used_ids.FindAndSetIdUsed(&id); - } - - // Add new extensions with colliding IDs. - RtpExtension id1_collision("", 1); - RtpExtension id2_collision("", 2); - RtpExtension id3_collision("", 3); - - // Expect to reassign to two-byte header extension IDs. - used_ids.FindAndSetIdUsed(&id1_collision); - EXPECT_EQ(id1_collision.id, 16); - used_ids.FindAndSetIdUsed(&id2_collision); - EXPECT_EQ(id2_collision.id, 17); - used_ids.FindAndSetIdUsed(&id3_collision); - EXPECT_EQ(id3_collision.id, 18); -} - -// Death tests. -// Disabled on Android because death tests misbehave on Android, see -// base/test/gtest_util.h. -#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) -TEST(UsedIdsDeathTest, DieWhenAllIdsAreOccupied) { - UsedIds used_ids(1, 5); - for (int i = 1; i <= 5; ++i) { - Foo id = {i}; - used_ids.FindAndSetIdUsed(&id); - } - Foo id_collision = {3}; - EXPECT_DEATH(used_ids.FindAndSetIdUsed(&id_collision), ""); -} - -using UsedRtpHeaderExtensionIdsDeathTest = UsedRtpHeaderExtensionIdsTest; -INSTANTIATE_TEST_SUITE_P(All, - UsedRtpHeaderExtensionIdsDeathTest, - ::testing::Values(kOneByteTestParams, - kTwoByteTestParams)); - -TEST_P(UsedRtpHeaderExtensionIdsDeathTest, DieWhenAllIdsAreOccupied) { - UsedRtpHeaderExtensionIds used_ids(GetParam().id_domain); - - // Fill all IDs. - for (int j = 1; j <= GetParam().max_id; ++j) { - RtpExtension id("", j); - used_ids.FindAndSetIdUsed(&id); - } - - RtpExtension id1_collision("", 1); - RtpExtension id2_collision("", 2); - RtpExtension id3_collision("", GetParam().max_id); - - EXPECT_DEATH(used_ids.FindAndSetIdUsed(&id1_collision), ""); - EXPECT_DEATH(used_ids.FindAndSetIdUsed(&id2_collision), ""); - EXPECT_DEATH(used_ids.FindAndSetIdUsed(&id3_collision), ""); -} -#endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) - -} // namespace -} // namespace webrtc diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc index 31c59cc9135..cbe3d86978f 100644 --- a/pc/video_rtp_receiver.cc +++ b/pc/video_rtp_receiver.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/dtls_transport_interface.h" @@ -33,6 +34,7 @@ #include "api/video/video_sink_interface.h" #include "media/base/media_channel.h" #include "pc/media_stream_track_proxy.h" +#include "pc/rtp_receiver.h" #include "pc/video_rtp_track_source.h" #include "pc/video_track.h" #include "rtc_base/checks.h" @@ -56,7 +58,7 @@ VideoRtpReceiver::VideoRtpReceiver( absl::string_view receiver_id, const std::vector>& streams, VideoMediaReceiveChannelInterface* media_channel) - : worker_thread_(worker_thread), + : RtpReceiverBase(worker_thread), id_(receiver_id), media_channel_(media_channel), source_(make_ref_counted(&source_callback_)), @@ -148,7 +150,7 @@ void VideoRtpReceiver::OnChanged() { worker_thread_->PostTask( [this, receive = cached_track_should_receive_]() { RTC_DCHECK_RUN_ON(worker_thread_); - if(receive) { + if (receive) { StartMediaChannel(); } else { StopMediaChannel(); @@ -157,7 +159,7 @@ void VideoRtpReceiver::OnChanged() { } } -void VideoRtpReceiver::StartMediaChannel() { +void VideoRtpReceiver::StartMediaChannel() { RTC_DCHECK_RUN_ON(worker_thread_); if (!media_channel_) { return; @@ -174,18 +176,19 @@ void VideoRtpReceiver::StopMediaChannel() { media_channel_->StopReceive(signaled_ssrc_.value_or(0)); } -void VideoRtpReceiver::RestartMediaChannel(std::optional ssrc) { +absl::AnyInvocable +VideoRtpReceiver::GetRestartFunctionForMediaChannel( + std::optional ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); MediaSourceInterface::SourceState state = source_->state(); - // TODO(tommi): Can we restart the media channel without blocking? - worker_thread_->BlockingCall([&] { - RTC_DCHECK_RUN_ON(worker_thread_); - RestartMediaChannel_w(std::move(ssrc), state); - }); source_->SetState(MediaSourceInterface::kLive); + return [this, ssrc = std::move(ssrc), state]() mutable { + RTC_DCHECK_RUN_ON(worker_thread_); + GetRestartFunctionForMediaChannel_w(std::move(ssrc), state); + }; } -void VideoRtpReceiver::RestartMediaChannel_w( +void VideoRtpReceiver::GetRestartFunctionForMediaChannel_w( std::optional ssrc, MediaSourceInterface::SourceState state) { RTC_DCHECK_RUN_ON(worker_thread_); @@ -237,14 +240,16 @@ void VideoRtpReceiver::SetSink(VideoSinkInterface* sink) { } } -void VideoRtpReceiver::SetupMediaChannel(uint32_t ssrc) { +absl::AnyInvocable VideoRtpReceiver::GetSetupForMediaChannel( + uint32_t ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - RestartMediaChannel(ssrc); + return GetRestartFunctionForMediaChannel(ssrc); } -void VideoRtpReceiver::SetupUnsignaledMediaChannel() { +absl::AnyInvocable +VideoRtpReceiver::GetSetupForUnsignaledMediaChannel() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - RestartMediaChannel(std::nullopt); + return GetRestartFunctionForMediaChannel(std::nullopt); } std::optional VideoRtpReceiver::ssrc() const { @@ -369,7 +374,7 @@ void VideoRtpReceiver::SetMediaChannel_w( source_->ClearCallback(); } -void VideoRtpReceiver::NotifyFirstPacketReceived() { +void VideoRtpReceiver::NotifyFirstPacketReceived(uint32_t ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (observer_) { observer_->OnFirstPacketReceived(media_type()); @@ -377,7 +382,8 @@ void VideoRtpReceiver::NotifyFirstPacketReceived() { received_first_packet_ = true; } -void VideoRtpReceiver::NotifyFirstPacketReceivedAfterReceptiveChange() { +void VideoRtpReceiver::NotifyFirstPacketReceivedAfterReceptiveChange( + uint32_t ssrc) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); if (observer_) { observer_->OnFirstPacketReceivedAfterReceptiveChange(media_type()); @@ -393,18 +399,18 @@ std::vector VideoRtpReceiver::GetSources() const { return media_channel_->GetSources(current_ssrc.value()); } -void VideoRtpReceiver::SetupMediaChannel( +absl::AnyInvocable VideoRtpReceiver::GetSetupForMediaChannel( std::optional ssrc, - MediaReceiveChannelInterface* media_channel) { + VideoMediaReceiveChannelInterface* media_channel) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); RTC_DCHECK(media_channel); MediaSourceInterface::SourceState state = source_->state(); - worker_thread_->BlockingCall([&] { + source_->SetState(MediaSourceInterface::kLive); + return [this, ssrc = std::move(ssrc), media_channel, state]() mutable { RTC_DCHECK_RUN_ON(worker_thread_); SetMediaChannel_w(media_channel); - RestartMediaChannel_w(std::move(ssrc), state); - }); - source_->SetState(MediaSourceInterface::kLive); + GetRestartFunctionForMediaChannel_w(std::move(ssrc), state); + }; } void VideoRtpReceiver::OnGenerateKeyFrame() { diff --git a/pc/video_rtp_receiver.h b/pc/video_rtp_receiver.h index bf4d5e06730..96003d40868 100644 --- a/pc/video_rtp_receiver.h +++ b/pc/video_rtp_receiver.h @@ -17,6 +17,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/dtls_transport_interface.h" @@ -37,14 +38,12 @@ #include "pc/rtp_receiver.h" #include "pc/video_rtp_track_source.h" #include "pc/video_track.h" -#include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" namespace webrtc { -class VideoRtpReceiver : public RtpReceiverInternal, - public ObserverInterface { +class VideoRtpReceiver : public RtpReceiverBase, public ObserverInterface { public: // An SSRC of 0 will create a receiver that will match the first SSRC it // sees. Must be called on signaling thread. @@ -54,6 +53,8 @@ class VideoRtpReceiver : public RtpReceiverInternal, VideoMediaReceiveChannelInterface* media_channel = nullptr); // TODO(hbos): Remove this when streams() is removed. // https://crbug.com/webrtc/9480 + // This should be PLAN_B_ONLY; but this marking is deferred due to templating + // issues VideoRtpReceiver( Thread* worker_thread, absl::string_view receiver_id, @@ -90,11 +91,11 @@ class VideoRtpReceiver : public RtpReceiverInternal, // RtpReceiverInternal implementation. void Stop() override; - void SetupMediaChannel(uint32_t ssrc) override; - void SetupUnsignaledMediaChannel() override; + absl::AnyInvocable GetSetupForMediaChannel(uint32_t ssrc) override; + absl::AnyInvocable GetSetupForUnsignaledMediaChannel() override; std::optional ssrc() const override; - void NotifyFirstPacketReceived() override; - void NotifyFirstPacketReceivedAfterReceptiveChange() override; + void NotifyFirstPacketReceived(uint32_t ssrc) override; + void NotifyFirstPacketReceivedAfterReceptiveChange(uint32_t ssrc) override; void set_stream_ids(std::vector stream_ids) override; void set_transport( scoped_refptr dtls_transport) override; @@ -112,19 +113,20 @@ class VideoRtpReceiver : public RtpReceiverInternal, std::vector GetSources() const override; - // Combines SetMediaChannel, SetupMediaChannel and - // SetupUnsignaledMediaChannel. - void SetupMediaChannel(std::optional ssrc, - MediaReceiveChannelInterface* media_channel); + // Combines SetMediaChannel, GetSetupForMediaChannel and + // GetSetupForUnsignaledMediaChannel. + absl::AnyInvocable GetSetupForMediaChannel( + std::optional ssrc, + VideoMediaReceiveChannelInterface* media_channel); private: void StartMediaChannel(); void StopMediaChannel(); - void RestartMediaChannel(std::optional ssrc) - RTC_RUN_ON(&signaling_thread_checker_); - void RestartMediaChannel_w(std::optional ssrc, - MediaSourceInterface::SourceState state) - RTC_RUN_ON(worker_thread_); + absl::AnyInvocable GetRestartFunctionForMediaChannel( + std::optional ssrc) RTC_RUN_ON(&signaling_thread_checker_); + void GetRestartFunctionForMediaChannel_w( + std::optional ssrc, + MediaSourceInterface::SourceState state) RTC_RUN_ON(worker_thread_); void SetSink(VideoSinkInterface* sink) RTC_RUN_ON(worker_thread_); void SetMediaChannel_w(MediaReceiveChannelInterface* media_channel) RTC_RUN_ON(worker_thread_); @@ -149,9 +151,6 @@ class VideoRtpReceiver : public RtpReceiverInternal, VideoRtpReceiver* const receiver_; } source_callback_{this}; - RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; - Thread* const worker_thread_; - const std::string id_; VideoMediaReceiveChannelInterface* media_channel_ RTC_GUARDED_BY(worker_thread_) = nullptr; diff --git a/pc/video_rtp_receiver_unittest.cc b/pc/video_rtp_receiver_unittest.cc index 15b23a6d08c..ed73fac8008 100644 --- a/pc/video_rtp_receiver_unittest.cc +++ b/pc/video_rtp_receiver_unittest.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "api/make_ref_counted.h" @@ -25,10 +26,10 @@ #include "api/video/video_sink_interface.h" #include "media/base/fake_media_engine.h" #include "media/base/media_channel.h" -#include "rtc_base/task_queue_for_test.h" #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" using ::testing::_; using ::testing::AnyNumber; @@ -96,15 +97,15 @@ class VideoRtpReceiverTest : public testing::Test { } void SetMediaChannel(MediaReceiveChannelInterface* media_channel) { - SendTask(worker_thread_.get(), - [&]() { receiver_->SetMediaChannel(media_channel); }); + worker_thread_->BlockingCall( + [&]() { receiver_->SetMediaChannel(media_channel); }); } VideoTrackSourceInterface* Source() { return receiver_->streams()[0]->FindVideoTrack("receiver")->GetSource(); } - AutoThread main_thread_; + test::RunLoop loop_; std::unique_ptr worker_thread_; NiceMock channel_; scoped_refptr receiver_; @@ -197,7 +198,7 @@ TEST_F(VideoRtpReceiverTest, BroadcastsEncodedFramesWhenEnabled) { EXPECT_CALL(sink, OnFrame).Times(2); MockRecordableEncodedFrame frame; broadcast(frame); - SendTask(worker_thread_.get(), [&] { broadcast(frame); }); + worker_thread_->BlockingCall([&] { broadcast(frame); }); } TEST_F(VideoRtpReceiverTest, EnablesEncodedOutputOnChannelRestart) { @@ -205,10 +206,12 @@ TEST_F(VideoRtpReceiverTest, EnablesEncodedOutputOnChannelRestart) { MockVideoSink sink; Source()->AddEncodedSink(&sink); EXPECT_CALL(channel_, SetRecordableEncodedFrameCallback(4711, _)); - receiver_->SetupMediaChannel(4711); + auto setup_media_channel = receiver_->GetSetupForMediaChannel(4711); + worker_thread_->BlockingCall([&]() { std::move(setup_media_channel)(); }); EXPECT_CALL(channel_, ClearRecordableEncodedFrameCallback(4711)); EXPECT_CALL(channel_, SetRecordableEncodedFrameCallback(0, _)); - receiver_->SetupUnsignaledMediaChannel(); + auto setup_task = receiver_->GetSetupForUnsignaledMediaChannel(); + worker_thread_->BlockingCall([&]() { std::move(setup_task)(); }); } } // namespace diff --git a/pc/video_track_unittest.cc b/pc/video_track_unittest.cc index a0a078c84bb..259d4152261 100644 --- a/pc/video_track_unittest.cc +++ b/pc/video_track_unittest.cc @@ -22,6 +22,7 @@ #include "pc/test/fake_video_track_source.h" #include "rtc_base/thread.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { namespace { @@ -38,7 +39,7 @@ class VideoTrackTest : public ::testing::Test { } protected: - AutoThread main_thread_; + test::RunLoop main_thread_; scoped_refptr video_track_source_; scoped_refptr video_track_; FakeFrameSource frame_source_; diff --git a/resources/audio_processing/output_data_float.pb.sha1 b/resources/audio_processing/output_data_float.pb.sha1 index 7dd76325f3b..7035368d685 100644 --- a/resources/audio_processing/output_data_float.pb.sha1 +++ b/resources/audio_processing/output_data_float.pb.sha1 @@ -1 +1 @@ -d80019464496fec3a149fd36809769e84a9ea5ce \ No newline at end of file +c5f9b0b9c75f89e34e8f58cfb6e37e8a46271072 \ No newline at end of file diff --git a/resources/audio_processing/output_data_float_avx2.pb.sha1 b/resources/audio_processing/output_data_float_avx2.pb.sha1 index ddca9839e78..6859932988c 100644 --- a/resources/audio_processing/output_data_float_avx2.pb.sha1 +++ b/resources/audio_processing/output_data_float_avx2.pb.sha1 @@ -1 +1 @@ -8751057d01ad4d58c98781cd7236a65ece699706 \ No newline at end of file +1623a92acd619a7ad2e599d7859463227179c1d4 \ No newline at end of file diff --git a/resources/video/timing/simulator/README.md b/resources/video/timing/simulator/README.md index f507db68bbf..b6fcbc14238 100644 --- a/resources/video/timing/simulator/README.md +++ b/resources/video/timing/simulator/README.md @@ -3,6 +3,9 @@ A set of RtcEventLog's recorded using `chrome://webrtc-internals`: * `video_recv_vp8_pt96`: Receiving a single stream of VP8 on payload type 96. +* `video_recv_vp8_pt96_lossy`: Similar to the previous one, but after half the session there is packet loss. * `video_recv_vp9_pt98`: Receiving a single stream of VP9 on payload type 98. * `video_recv_av1_pt45`: Receiving a single stream of AV1 on payload type 45. -* `video_recv_sequential_join_vp8_vp9_av1`: Sequential join of three remote streams, with codecs and payload types as above. \ No newline at end of file +* `video_recv_sequential_join_vp8_vp9_av1`: Sequential join of three remote streams, with codecs and payload types as above. + +The `video_recv_vp8_pt96_lossy` log was recorded after the RTX OSN logging change in https://webrtc-review.googlesource.com/c/src/+/442320. The other logs were recorded before. diff --git a/resources/video/timing/simulator/video_recv_vp8_pt96_lossy.rtceventlog.sha1 b/resources/video/timing/simulator/video_recv_vp8_pt96_lossy.rtceventlog.sha1 new file mode 100644 index 00000000000..32ace85632c --- /dev/null +++ b/resources/video/timing/simulator/video_recv_vp8_pt96_lossy.rtceventlog.sha1 @@ -0,0 +1 @@ +cba0936ed7a398934983a4905d61f48911010a91 \ No newline at end of file diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 9b871393a62..380090a9f72 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -29,7 +29,6 @@ rtc_library("bitstream_reader") { deps = [ ":checks", ":safe_conversions", - "../api:array_view", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/numeric:bits", "//third_party/abseil-cpp/absl/strings:string_view", @@ -80,7 +79,7 @@ rtc_source_set("buffer") { ":checks", ":type_traits", ":zero_memory", - "../api:array_view", + "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -89,7 +88,11 @@ rtc_source_set("buffer") { rtc_source_set("byte_order") { visibility = [ "*" ] sources = [ "byte_order.h" ] - deps = [ "system:arch" ] + deps = [ + ":checks", + "system:arch", + "//third_party/abseil-cpp/absl/base:core_headers", + ] } rtc_source_set("mod_ops") { @@ -137,7 +140,6 @@ rtc_library("bit_buffer") { ] deps = [ ":checks", - "../api:array_view", "../api/units:data_size", "//third_party/abseil-cpp/absl/numeric:bits", "//third_party/abseil-cpp/absl/strings:string_view", @@ -153,7 +155,6 @@ rtc_library("byte_buffer") { deps = [ ":buffer", ":byte_order", - "../api:array_view", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -184,8 +185,10 @@ rtc_library("copy_on_write_buffer") { ":checks", ":refcount", ":type_traits", + "../api:make_ref_counted", "../api:scoped_refptr", "system:rtc_export", + "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -222,13 +225,31 @@ rtc_library("event_tracer") { ":platform_thread_types", ":rtc_event", ":timeutils", - "..:tracing", "../api:sequence_checker", + "../api/environment", "../api/units:time_delta", + "../api/units:timestamp", + "../system_wrappers", "synchronization:mutex", "system:rtc_export", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/strings:string_view", ] + all_dependent_configs = [ "//third_party/perfetto/gn:public_config" ] + if (rtc_use_perfetto) { + if (build_with_chromium) { + deps += [ "//third_party/perfetto:libperfetto" ] + } else { + deps += [ + "//third_party/perfetto/include/perfetto/tracing", + "//third_party/perfetto/src/tracing:client_api_without_backends", + "//third_party/perfetto/src/tracing:platform_impl", + ] + } + } else { + deps += [ "//third_party/perfetto/include/perfetto/tracing" ] + } } rtc_library("histogram_percentile_counter") { @@ -358,10 +379,7 @@ rtc_library("zero_memory") { "zero_memory.cc", "zero_memory.h", ] - deps = [ - ":checks", - "../api:array_view", - ] + deps = [ ":checks" ] } rtc_library("platform_thread_types") { @@ -616,13 +634,27 @@ rtc_library("stringutils") { deps = [ ":checks", ":safe_minmax", - "../api:array_view", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", ] } +rtc_library("text2pcap") { + visibility = [ "*" ] + sources = [ + "text2pcap.cc", + "text2pcap.h", + ] + deps = [ + ":logging", + ":stringutils", + ":timeutils", + "system:rtc_export", + "//third_party/abseil-cpp/absl/strings:string_view", + ] +} + rtc_source_set("type_traits") { sources = [ "type_traits.h" ] } @@ -719,7 +751,6 @@ if (rtc_include_tests) { sources = [ "task_queue_stdlib_unittest.cc" ] deps = [ - ":gunit_helpers", ":logging", ":rtc_event", ":rtc_task_queue_stdlib", @@ -728,6 +759,7 @@ if (rtc_include_tests) { "../api/task_queue", "../api/task_queue:task_queue_test", "../api/units:time_delta", + "../test:run_loop", "../test:test_support", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -1097,6 +1129,7 @@ if (rtc_include_tests) { deps = [ ":socket", ":socket_address", + "../test:run_loop", "../test:test_support", ] } @@ -1228,7 +1261,6 @@ rtc_library("network") { ":socket_factory", ":stringutils", ":threading", - "../api:array_view", "../api:field_trials_view", "../api:scoped_refptr", "../api:sequence_checker", @@ -1287,7 +1319,6 @@ rtc_library("socket_adapters") { ":socket_address", ":stringutils", ":zero_memory", - "../api:array_view", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", @@ -1320,7 +1351,6 @@ rtc_library("async_tcp_socket") { ":socket", ":socket_address", ":timeutils", - "../api:array_view", "../api/environment", "../api/units:timestamp", "network:received_packet", @@ -1386,9 +1416,9 @@ if (rtc_include_tests) { sources = [ "async_packet_socket_unittest.cc" ] deps = [ ":async_packet_socket", - ":gunit_helpers", ":socket", ":socket_address", + "../test:run_loop", "../test:test_support", "network:received_packet", ] @@ -1400,7 +1430,6 @@ if (rtc_include_tests) { deps = [ ":async_packet_socket", ":async_udp_socket", - ":gunit_helpers", ":mock_socket", ":rtc_base_tests_utils", ":socket", @@ -1410,6 +1439,7 @@ if (rtc_include_tests) { "../api/units:timestamp", "../system_wrappers", "../test:create_test_environment", + "../test:run_loop", "../test:test_support", "network:received_packet", "//third_party/abseil-cpp/absl/memory", @@ -1421,11 +1451,11 @@ if (rtc_include_tests) { deps = [ ":async_packet_socket", ":async_tcp_socket", - ":gunit_helpers", ":net_helpers", ":rtc_base_tests_utils", ":socket", "../test:create_test_environment", + "../test:run_loop", "../test:test_support", ] } @@ -1477,7 +1507,6 @@ rtc_library("unique_id_generator") { ":crypto_random", ":macromagic", ":stringutils", - "../api:array_view", "../api:sequence_checker", "synchronization:mutex", "system:no_unique_address", @@ -1506,7 +1535,6 @@ rtc_library("stream") { ":logging", ":macromagic", ":threading", - "../api:array_view", "../api:sequence_checker", "system:no_unique_address", "system:rtc_export", @@ -1612,7 +1640,6 @@ rtc_library("ssl") { ":ssl_header", ":stringutils", ":timeutils", - "../api:array_view", "../api:refcountedbase", "../api:scoped_refptr", "system:rtc_export", @@ -1688,7 +1715,6 @@ rtc_library("ssl_adapter") { ":stringutils", ":threading", ":timeutils", - "../api:array_view", "../api:field_trials_view", "../api:sequence_checker", "../api/task_queue", @@ -1714,19 +1740,6 @@ rtc_source_set("gtest_prod") { sources = [ "gtest_prod_util.h" ] } -rtc_library("gunit_helpers") { - testonly = true - sources = [ "gunit.h" ] - deps = [ - ":logging", - ":rtc_base_tests_utils", - ":stringutils", - ":threading", - "../test:test_support", - "//third_party/abseil-cpp/absl/strings:string_view", - ] -} - rtc_library("testclient") { testonly = true sources = [ @@ -1737,7 +1750,6 @@ rtc_library("testclient") { ":async_packet_socket", ":async_udp_socket", ":buffer", - ":gunit_helpers", ":rtc_base_tests_utils", ":socket", ":socket_address", @@ -1758,7 +1770,6 @@ rtc_library("callback_list_unittests") { sources = [ "callback_list_unittest.cc" ] deps = [ ":callback_list", - ":gunit_helpers", ":untyped_function", "../api:function_view", "../test:test_support", @@ -1824,7 +1835,6 @@ rtc_library("rtc_base_tests_utils") { ":stringutils", ":threading", ":timeutils", - "../api:array_view", "../api:make_ref_counted", "../api:refcountedbase", "../api:scoped_refptr", @@ -1897,7 +1907,6 @@ if (!rtc_rusty_base64) { "base64.h", ] deps = [ - "../api:array_view", "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", @@ -1923,7 +1932,6 @@ if (!rtc_rusty_base64) { ] deps = [ ":base64_rust_bridge", - "../api:array_view", "//build/rust:cxx_cppdeps", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -1949,7 +1957,7 @@ rtc_library("cpu_info") { if (rtc_include_tests) { if (rtc_enable_google_benchmarks) { - rtc_library("base64_benchmark_temp") { + rtc_library("base64_benchmark") { testonly = true sources = [ "base64_benchmark.cc" ] deps = [ @@ -1963,6 +1971,7 @@ if (rtc_include_tests) { sources = [ "untyped_function_unittest.cc" ] deps = [ ":untyped_function", + "../test:run_loop", "../test:test_support", ] } @@ -1973,13 +1982,13 @@ if (rtc_include_tests) { sources = [ "operations_chain_unittest.cc" ] deps = [ ":checks", - ":gunit_helpers", ":rtc_event", ":rtc_operations_chain", ":threading", "../api:rtc_error_matchers", "../api:scoped_refptr", "../api/units:time_delta", + "../test:run_loop", "../test:test_support", "../test:wait_until", ] @@ -2004,7 +2013,6 @@ if (rtc_include_tests) { ":buffer", ":checks", ":file_rotating_stream", - ":gunit_helpers", ":ip_address", ":logging", ":net_helpers", @@ -2028,6 +2036,7 @@ if (rtc_include_tests) { "../test:create_test_environment", "../test:fileutils", "../test:near_matcher", + "../test:run_loop", "../test:test_support", "../test:wait_until", "system:file_wrapper", @@ -2104,7 +2113,6 @@ if (rtc_include_tests) { ":divide_round", ":event_tracer", ":frequency_tracker", - ":gunit_helpers", ":histogram_percentile_counter", ":ip_address", ":logging", @@ -2139,7 +2147,6 @@ if (rtc_include_tests) { ":timestamp_aligner", ":timeutils", ":zero_memory", - "../api:array_view", "../api:make_ref_counted", "../api:ref_count", "../api:scoped_refptr", @@ -2154,13 +2161,17 @@ if (rtc_include_tests) { "../system_wrappers", "../test:create_test_environment", "../test:fileutils", + "../test:run_loop", "../test:test_support", + "../test/time_controller", "containers:flat_map", "containers:unittests", "memory:unittests", "network:received_packet", "synchronization:mutex", "task_utils:repeating_task", + "//testing/gtest", + "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/numeric:bits", @@ -2177,13 +2188,13 @@ if (rtc_include_tests) { sources = [ "task_queue_unittest.cc" ] deps = [ - ":gunit_helpers", ":rtc_base_tests_utils", ":rtc_event", ":task_queue_for_test", ":timeutils", "../api/task_queue", "../api/units:time_delta", + "../test:run_loop", "../test:test_support", "//third_party/abseil-cpp/absl/memory", ] @@ -2194,11 +2205,11 @@ if (rtc_include_tests) { sources = [ "weak_ptr_unittest.cc" ] deps = [ - ":gunit_helpers", ":rtc_base_tests_utils", ":rtc_event", ":task_queue_for_test", ":weak_ptr", + "../test:run_loop", "../test:test_support", ] } @@ -2223,6 +2234,7 @@ if (rtc_include_tests) { ":timeutils", ":windowed_min_filter", "../api/units:time_delta", + "../test:run_loop", "../test:test_support", "//testing/gtest", "//third_party/abseil-cpp/absl/algorithm:container", @@ -2234,9 +2246,9 @@ if (rtc_include_tests) { sources = [ "strings/json_unittest.cc" ] deps = [ - ":gunit_helpers", ":rtc_base_tests_utils", ":rtc_json", + "../test:run_loop", "../test:test_support", ] } @@ -2278,7 +2290,6 @@ if (rtc_include_tests) { ":data_rate_limiter", ":denormal_disabler", ":digest", - ":gunit_helpers", ":ifaddrs_converter", ":ip_address", ":logging", @@ -2311,7 +2322,6 @@ if (rtc_include_tests) { ":threading", ":timeutils", ":unique_id_generator", - "../api:array_view", "../api:field_trials", "../api:field_trials_view", "../api:location", @@ -2332,8 +2342,10 @@ if (rtc_include_tests) { "../test:create_test_field_trials", "../test:fileutils", "../test:rtc_expect_death", + "../test:run_loop", "../test:test_support", "../test:wait_until", + "../test/time_controller", "memory:fifo_buffer", "network:received_packet", "synchronization:mutex", diff --git a/rtc_base/DEPS b/rtc_base/DEPS index 3861de1bb69..01756f3c99a 100644 --- a/rtc_base/DEPS +++ b/rtc_base/DEPS @@ -6,46 +6,46 @@ include_rules = [ ] specific_include_rules = { - "checks\.h": [ + "checks\\.h": [ "+absl/strings/has_absl_stringify.h", ], - "string_builder\.h": [ + "string_builder\\.h": [ "+absl/strings/has_absl_stringify.h", ], - "protobuf_utils\.h": [ + "protobuf_utils\\.h": [ "+third_party/protobuf", ], - "gunit\.h": [ + "gunit\\.h": [ "+testing/base/public/gunit.h" ], - "trace_categories\.h": [ + "trace_categories\\.h": [ "+third_party/perfetto", ], - "event_tracer\.cc": [ + "event_tracer\\.cc": [ "+third_party/perfetto", ], - "logging.h": [ + "logging\\.h": [ "+absl/strings/has_absl_stringify.h", ], - "trace_event\.h": [ + "trace_event\\.h": [ "+third_party/perfetto", ], - "openssl_adapter.cc": [ + "openssl_adapter\\.cc": [ "+openssl", ], - "openssl_stream_adapter.cc": [ + "openssl_stream_adapter\\.cc": [ "+openssl", ], - "openssl_stream_adapter.h": [ + "openssl_stream_adapter\\.h": [ "+openssl", ], - "openssl_session_cache\.h": [ + "openssl_session_cache\\.h": [ "+openssl", ], - "base64_benchmark\.cc": [ + "base64_benchmark\\.cc": [ "+benchmark", ], - "base64_rust\.cc": [ + "base64_rust\\.cc": [ "+third_party/rust/chromium_crates_io/vendor/cxx-v1/include/cxx.h", ], } diff --git a/rtc_base/OWNERS b/rtc_base/OWNERS index 3a420a088ee..251a43426b0 100644 --- a/rtc_base/OWNERS +++ b/rtc_base/OWNERS @@ -1,7 +1,7 @@ hta@webrtc.org -mflodman@webrtc.org tommi@webrtc.org mbonadei@webrtc.org +jonaso@webrtc.org per-file rate_statistics*=sprang@webrtc.org per-file rate_statistics*=stefan@webrtc.org diff --git a/rtc_base/async_tcp_socket.cc b/rtc_base/async_tcp_socket.cc index 219b325003f..02aa98fffed 100644 --- a/rtc_base/async_tcp_socket.cc +++ b/rtc_base/async_tcp_socket.cc @@ -15,11 +15,11 @@ #include #include #include +#include #include #include "absl/base/nullability.h" #include "absl/memory/memory.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/byte_order.h" @@ -127,7 +127,7 @@ int AsyncTCPSocketBase::SendTo(const void* pv, int AsyncTCPSocketBase::FlushOutBuffer() { RTC_DCHECK_GT(outbuf_.size(), 0); - ArrayView view = outbuf_; + std::span view = outbuf_; int res; while (!view.empty()) { res = socket_->Send(view.data(), view.size()); @@ -139,7 +139,7 @@ int AsyncTCPSocketBase::FlushOutBuffer() { res = -1; break; } - view = view.subview(res); + view = view.subspan(res); } if (res > 0) { // The output buffer may have been written out over multiple partial Send(), @@ -156,8 +156,7 @@ int AsyncTCPSocketBase::FlushOutBuffer() { res = outbuf_.size() - view.size(); } if (view.size() < outbuf_.size()) { - memmove(outbuf_.data(), view.data(), view.size()); - outbuf_.SetSize(view.size()); + outbuf_.SetData(view); } } return res; @@ -212,9 +211,11 @@ void AsyncTCPSocketBase::OnReadEvent(Socket* socket) { inbuf_.Clear(); } else { if (bytes_remaining > 0) { - memmove(inbuf_.data(), inbuf_.data() + processed, bytes_remaining); + // Move remaining bytes to beginning of buffer. + inbuf_.SetData(std::span(inbuf_).subspan(processed)); + } else { + inbuf_.Clear(); } - inbuf_.SetSize(bytes_remaining); } } @@ -271,7 +272,7 @@ int AsyncTCPSocket::Send(const void* pv, return static_cast(cb); } -size_t AsyncTCPSocket::ProcessInput(ArrayView data) { +size_t AsyncTCPSocket::ProcessInput(std::span data) { SocketAddress remote_addr(GetRemoteAddress()); size_t processed_bytes = 0; @@ -280,12 +281,13 @@ size_t AsyncTCPSocket::ProcessInput(ArrayView data) { if (bytes_left < kPacketLenSize) return processed_bytes; - PacketLength pkt_len = GetBE16(data.data() + processed_bytes); + PacketLength pkt_len = + GetBE16(data.subspan(processed_bytes, kPacketLenSize)); if (bytes_left < kPacketLenSize + pkt_len) return processed_bytes; ReceivedIpPacket received_packet( - data.subview(processed_bytes + kPacketLenSize, pkt_len), remote_addr, + data.subspan(processed_bytes + kPacketLenSize, pkt_len), remote_addr, env_.clock().CurrentTime()); NotifyPacketReceived(received_packet); processed_bytes += kPacketLenSize + pkt_len; diff --git a/rtc_base/async_tcp_socket.h b/rtc_base/async_tcp_socket.h index fdd5e3a9af8..c937fdd08f0 100644 --- a/rtc_base/async_tcp_socket.h +++ b/rtc_base/async_tcp_socket.h @@ -14,9 +14,9 @@ #include #include #include +#include #include "absl/base/nullability.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/buffer.h" @@ -42,7 +42,7 @@ class AsyncTCPSocketBase : public AsyncPacketSocket { size_t cb, const AsyncSocketPacketOptions& options) override = 0; // Must return the number of bytes processed. - virtual size_t ProcessInput(ArrayView data) = 0; + virtual size_t ProcessInput(std::span data) = 0; SocketAddress GetLocalAddress() const override; SocketAddress GetRemoteAddress() const override; @@ -92,7 +92,7 @@ class AsyncTCPSocket : public AsyncTCPSocketBase { int Send(const void* pv, size_t cb, const AsyncSocketPacketOptions& options) override; - size_t ProcessInput(ArrayView) override; + size_t ProcessInput(std::span) override; private: const Environment env_; diff --git a/rtc_base/async_udp_socket.cc b/rtc_base/async_udp_socket.cc index 84731499161..8aec7fe7621 100644 --- a/rtc_base/async_udp_socket.cc +++ b/rtc_base/async_udp_socket.cc @@ -57,6 +57,11 @@ AsyncUDPSocket::AsyncUDPSocket(const Environment& env, [this](Socket* socket) { OnReadEvent(socket); }); socket_->SubscribeWriteEvent( this, [this](Socket* socket) { OnWriteEvent(socket); }); + // need to forward that also for UDP case (DTLS) once the SSL handshake is + // finished + + socket_->SubscribeConnectEvent( + this, [this](Socket* socket) { OnConnectEvent(socket); }); } SocketAddress AsyncUDPSocket::GetLocalAddress() const { @@ -125,6 +130,10 @@ void AsyncUDPSocket::SetError(int error) { return socket_->SetError(error); } +void AsyncUDPSocket::OnConnectEvent(Socket* socket) { + NotifyConnect(this); +} + void AsyncUDPSocket::OnReadEvent(Socket* socket) { RTC_DCHECK(socket_.get() == socket); RTC_DCHECK_RUN_ON(&sequence_checker_); diff --git a/rtc_base/async_udp_socket.h b/rtc_base/async_udp_socket.h index d0586b3ff44..4ae1fe511fe 100644 --- a/rtc_base/async_udp_socket.h +++ b/rtc_base/async_udp_socket.h @@ -63,6 +63,8 @@ class AsyncUDPSocket : public AsyncPacketSocket { void SetError(int error) override; private: + // called when the underlying socket is connected - DTLS handshake case + void OnConnectEvent(Socket* socket); // Called when the underlying socket is ready to be read from. void OnReadEvent(Socket* socket); // Called when the underlying socket is ready to send. diff --git a/rtc_base/base64.h b/rtc_base/base64.h index 9a83d8b07d2..540fc9a3e2e 100644 --- a/rtc_base/base64.h +++ b/rtc_base/base64.h @@ -13,16 +13,16 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" namespace webrtc { std::string Base64Encode(absl::string_view data); -inline std::string Base64Encode(ArrayView data) { +inline std::string Base64Encode(std::span data) { return Base64Encode(absl::string_view( reinterpret_cast(data.data()), data.size())); } diff --git a/rtc_base/bit_buffer.cc b/rtc_base/bit_buffer.cc index 0d8c75a84c3..ab2be821230 100644 --- a/rtc_base/bit_buffer.cc +++ b/rtc_base/bit_buffer.cc @@ -1,5 +1,5 @@ /* - * Copyright 2015 The WebRTC Project Authors. All rights reserved. + * Copyright 2015 The WebRTC Project Authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source @@ -15,12 +15,14 @@ #include #include #include +#include #include "absl/numeric/bits.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" +namespace webrtc { + namespace { // Returns the highest byte of `val` in a uint8_t. @@ -52,9 +54,7 @@ uint8_t WritePartialByte(uint8_t source, } // namespace -namespace webrtc { - -BitBufferWriter::BitBufferWriter(ArrayView bytes) +BitBufferWriter::BitBufferWriter(std::span bytes) : BitBufferWriter(bytes.data(), bytes.size()) {} BitBufferWriter::BitBufferWriter(uint8_t* bytes, size_t byte_count) diff --git a/rtc_base/bit_buffer.h b/rtc_base/bit_buffer.h index 1c78662e18b..575a7ef77e2 100644 --- a/rtc_base/bit_buffer.h +++ b/rtc_base/bit_buffer.h @@ -14,8 +14,9 @@ #include // For size_t. #include // For integer types. +#include + #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/units/data_size.h" namespace webrtc { @@ -29,7 +30,7 @@ class BitBufferWriter { static constexpr DataSize kMaxLeb128Length = DataSize::Bytes(10); // Constructs a bit buffer for the writable buffer of `bytes`. - explicit BitBufferWriter(ArrayView bytes); + explicit BitBufferWriter(std::span bytes); BitBufferWriter(uint8_t* bytes, size_t byte_count); BitBufferWriter(const BitBufferWriter&) = delete; diff --git a/rtc_base/bit_buffer_unittest.cc b/rtc_base/bit_buffer_unittest.cc index bca818c2b21..b607c4003c3 100644 --- a/rtc_base/bit_buffer_unittest.cc +++ b/rtc_base/bit_buffer_unittest.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/bitstream_reader.h" #include "test/gmock.h" #include "test/gtest.h" @@ -166,7 +166,7 @@ TEST(BitBufferWriterTest, SymmetricReadWrite) { // That should be all that fits in the buffer. EXPECT_FALSE(buffer.WriteBits(1, 1)); - BitstreamReader reader(MakeArrayView(bytes, 4)); + BitstreamReader reader(std::span(bytes, 4)); EXPECT_EQ(reader.ReadBits(3), 0x2u); EXPECT_EQ(reader.ReadBits(2), 0x1u); EXPECT_EQ(reader.ReadBits(7), 0x53u); diff --git a/rtc_base/bitstream_reader.h b/rtc_base/bitstream_reader.h index 622534ef704..5b9ee982923 100644 --- a/rtc_base/bitstream_reader.h +++ b/rtc_base/bitstream_reader.h @@ -13,12 +13,12 @@ #include +#include #include #include #include "absl/base/attributes.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" @@ -33,7 +33,7 @@ namespace webrtc { class BitstreamReader { public: explicit BitstreamReader( - ArrayView bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); + std::span bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); explicit BitstreamReader( absl::string_view bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); BitstreamReader(const BitstreamReader&) = default; @@ -126,7 +126,7 @@ class BitstreamReader { mutable bool last_read_is_verified_ = true; }; -inline BitstreamReader::BitstreamReader(ArrayView bytes) +inline BitstreamReader::BitstreamReader(std::span bytes) : bytes_(bytes.data()), remaining_bits_(checked_cast(bytes.size() * 8)) {} diff --git a/rtc_base/bitstream_reader_unittest.cc b/rtc_base/bitstream_reader_unittest.cc index 4d76546f69a..f3f8f9a3c29 100644 --- a/rtc_base/bitstream_reader_unittest.cc +++ b/rtc_base/bitstream_reader_unittest.cc @@ -15,10 +15,10 @@ #include #include #include +#include #include #include "absl/numeric/bits.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "test/gtest.h" @@ -197,14 +197,14 @@ TEST(BitstreamReaderTest, ReadBits) { } TEST(BitstreamReaderTest, ReadZeroBits) { - BitstreamReader reader(ArrayView(nullptr, 0)); + BitstreamReader reader(std::span{}); EXPECT_EQ(reader.ReadBits(0), 0u); EXPECT_TRUE(reader.Ok()); } TEST(BitstreamReaderTest, ReadBitFromEmptyArray) { - BitstreamReader reader(ArrayView(nullptr, 0)); + BitstreamReader reader(std::span{}); // Trying to read from the empty array shouldn't dereference the pointer, // i.e. shouldn't crash. @@ -213,7 +213,7 @@ TEST(BitstreamReaderTest, ReadBitFromEmptyArray) { } TEST(BitstreamReaderTest, ReadBitsFromEmptyArray) { - BitstreamReader reader(ArrayView(nullptr, 0)); + BitstreamReader reader(std::span{}); // Trying to read from the empty array shouldn't dereference the pointer, // i.e. shouldn't crash. @@ -324,12 +324,12 @@ TEST(BitstreamReaderTest, NoGolombOverread) { const uint8_t bytes[] = {0x00, 0xFF, 0xFF}; // Make sure the bit buffer correctly enforces byte length on golomb reads. // If it didn't, the above buffer would be valid at 3 bytes. - BitstreamReader reader1(MakeArrayView(bytes, 1)); + BitstreamReader reader1(std::span(bytes, 1)); // When parse fails, `ReadExponentialGolomb` may return any number. reader1.ReadExponentialGolomb(); EXPECT_FALSE(reader1.Ok()); - BitstreamReader reader2(MakeArrayView(bytes, 2)); + BitstreamReader reader2(std::span(bytes, 2)); reader2.ReadExponentialGolomb(); EXPECT_FALSE(reader2.Ok()); diff --git a/rtc_base/buffer.h b/rtc_base/buffer.h index 5d9d8caabd0..88b2ff76dd4 100644 --- a/rtc_base/buffer.h +++ b/rtc_base/buffer.h @@ -16,12 +16,12 @@ #include #include #include +#include #include #include -#include "absl/base/attributes.h" +#include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/type_traits.h" #include "rtc_base/zero_memory.h" @@ -66,7 +66,16 @@ class BufferT { public: using value_type = T; - using const_iterator = const T*; + using iterator = std::span::iterator; + using const_iterator = std::span::iterator; + + // Static methods to construct a buffer with size and/or capacity. + static BufferT CreateWithCapacity(size_t capacity) { + return BufferT(InternalTag{}, 0, capacity); + } + static BufferT CreateUninitializedWithSize(size_t size) { + return BufferT(InternalTag{}, size, size); + } // An empty BufferT. BufferT() : size_(0), capacity_(0), data_(nullptr) { @@ -87,15 +96,15 @@ class BufferT { } // Construct a buffer with the specified number of uninitialized elements. - ABSL_DEPRECATED("Use CreateUninitializedWithSize()") - explicit BufferT(size_t size) : BufferT(size, size) {} + [[deprecated("Use CreateUninitializedWithSize()")]] + explicit BufferT(size_t size) + : BufferT(size, size) {} + // Construct a buffer with the specified number of uninitialized elements, + // and a possibly non-zero size() + [[deprecated("Use CreateWithCapacity() or CreateUninitializedWithSize()")]] BufferT(size_t size, size_t capacity) - : size_(size), - capacity_(std::max(size, capacity)), - data_(capacity_ > 0 ? new T[capacity_] : nullptr) { - RTC_DCHECK(IsConsistent()); - } + : BufferT(InternalTag{}, size, capacity) {} // Construct a buffer and copy the specified number of elements into it. template ::value>::type* = nullptr> - BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) { + BufferT(U* data, size_t size, size_t capacity) + : BufferT(InternalTag{}, size, capacity) { static_assert(sizeof(T) == sizeof(U), ""); if (size > 0) { RTC_DCHECK(data); + RTC_DCHECK_LE(size, capacity); std::memcpy(data_.get(), data, size * sizeof(U)); } } @@ -121,6 +132,12 @@ class BufferT { internal::BufferCompat::value>::type* = nullptr> BufferT(U (&array)[N]) : BufferT(array, N) {} + // Construct a buffer from any type with a data() and size() member. + template ::value>::type* = nullptr> + explicit BufferT(const W& w) : BufferT(w.data(), w.size()) {} + ~BufferT() { MaybeZeroCompleteBuffer(); } // Implicit conversion to absl::string_view if T is compatible with char. @@ -130,14 +147,6 @@ class BufferT { return absl::string_view(data(), size()); } - // Static methods to construct a buffer with size and/or capacity. - static BufferT CreateWithCapacity(size_t capacity) { - return BufferT(0, capacity); - } - static BufferT CreateUninitializedWithSize(size_t size) { - return BufferT(size, size); - } - // Get a pointer to the data. Just .data() will give you a (const) T*, but if // T is a byte-sized integer, you may also use .data() for any other // byte-sized integer U. @@ -205,20 +214,20 @@ class BufferT { T& operator[](size_t index) { RTC_DCHECK_LT(index, size_); - return data()[index]; + return std::span(*this)[index]; } T operator[](size_t index) const { RTC_DCHECK_LT(index, size_); - return data()[index]; + return std::span(*this)[index]; } - T* begin() { return data(); } - T* end() { return data() + size(); } - const T* begin() const { return data(); } - const T* end() const { return data() + size(); } - const T* cbegin() const { return data(); } - const T* cend() const { return data() + size(); } + iterator begin() { return std::span(*this).begin(); } + iterator end() { return std::span(*this).end(); } + const_iterator begin() const { return std::span(*this).begin(); } + const_iterator end() const { return std::span(*this).end(); } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } // The SetData functions replace the contents of the buffer. They accept the // same input types as the constructors. @@ -253,12 +262,12 @@ class BufferT { // Replaces the data in the buffer with at most `max_elements` of data, using // the function `setter`, which should have the following signature: // - // size_t setter(ArrayView view) + // size_t setter(std::span view) // - // `setter` is given an appropriately typed ArrayView of length exactly + // `setter` is given an appropriately typed std::span of length exactly // `max_elements` that describes the area where it should write the data; it // should return the number of elements actually written. (If it doesn't fill - // the whole ArrayView, it should leave the unused space at the end.) + // the whole std::span, it should leave the unused space at the end.) template source(data, size); + std::span destination = + std::span(data_.get(), capacity_).subspan(size_, size); + absl::c_copy(source, destination.begin()); size_ = new_size; RTC_DCHECK(IsConsistent()); } @@ -318,12 +330,12 @@ class BufferT { // Appends at most `max_elements` to the end of the buffer, using the function // `setter`, which should have the following signature: // - // size_t setter(ArrayView view) + // size_t setter(std::span view) // - // `setter` is given an appropriately typed ArrayView of length exactly + // `setter` is given an appropriately typed std::span of length exactly // `max_elements` that describes the area where it should write the data; it // should return the number of elements actually written. (If it doesn't fill - // the whole ArrayView, it should leave the unused space at the end.) + // the whole std::span, it should leave the unused space at the end.) template () + old_size; - size_t written_elements = setter(ArrayView(base_ptr, max_elements)); + SetSizeInternal(old_size + max_elements); + size_t written_elements = + setter(std::span(data(), size()).subspan(old_size)); RTC_CHECK_LE(written_elements, max_elements); size_ = old_size + written_elements; @@ -345,9 +357,15 @@ class BufferT { // buffer contents will be kept but truncated; if the new size is greater, // the existing contents will be kept and the new space will be // uninitialized. - void SetSize(size_t size) { - const size_t old_size = size_; - EnsureCapacityWithHeadroom(size, true); + // TODO: issues.webrtc.org/42223681 - deprecate and remove the ability to + // create uninitialized buffer space. + // When we know that the new size is smaller than the old, use Truncate(). + void SetSize(size_t size) { SetSizeInternal(size); } + + // Truncate the buffer. The buffer contents will be kept but truncated. + void Truncate(size_t size) { + RTC_DCHECK_LE(size, size_); + size_t old_size = size_; size_ = size; if (ZeroOnFree && size_ < old_size) { ZeroTrailingData(old_size - size_); @@ -380,6 +398,25 @@ class BufferT { } private: + // Internal constructor that allows uninitialized memory to be created. + // Used by CreateUninitialized* functions and by the deprecated constructors + // that are replaced by CreateUninitialized functions. + struct InternalTag {}; + BufferT(InternalTag tag, size_t size, size_t capacity) + : size_(size), + capacity_(std::max(size, capacity)), + data_(capacity_ > 0 ? new T[capacity_] : nullptr) { + RTC_DCHECK(IsConsistent()); + } + + void SetSizeInternal(size_t size) { + const size_t old_size = size_; + EnsureCapacityWithHeadroom(size, true); + size_ = size; + if (ZeroOnFree && size_ < old_size) { + ZeroTrailingData(old_size - size_); + } + } void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) { RTC_DCHECK(IsConsistent()); if (capacity <= capacity_) @@ -410,7 +447,7 @@ class BufferT { // It would be sufficient to only zero "size_" elements, as all other // methods already ensure that the unused capacity contains no sensitive // data---but better safe than sorry. - ExplicitZeroMemory(data_.get(), capacity_ * sizeof(T)); + ExplicitZeroMemory(std::span(data_.get(), capacity_)); } } @@ -418,7 +455,7 @@ class BufferT { void ZeroTrailingData(size_t count) { RTC_DCHECK(IsConsistent()); RTC_DCHECK_LE(count, capacity_ - size_); - ExplicitZeroMemory(data_.get() + size_, count * sizeof(T)); + ExplicitZeroMemory(std::span(data(), capacity_).subspan(size_)); } // Precondition for all methods except Clear, operator= and the destructor. diff --git a/rtc_base/buffer_queue.cc b/rtc_base/buffer_queue.cc index e791e91322c..5327dbd7a2b 100644 --- a/rtc_base/buffer_queue.cc +++ b/rtc_base/buffer_queue.cc @@ -73,7 +73,7 @@ bool BufferQueue::WriteBack(const void* buffer, packet = free_list_.back(); free_list_.pop_back(); } else { - packet = new Buffer(bytes, default_size_); + packet = new Buffer(Buffer::CreateWithCapacity(default_size_)); } packet->SetData(static_cast(buffer), bytes); diff --git a/rtc_base/buffer_unittest.cc b/rtc_base/buffer_unittest.cc index 88ebd0dd6a1..4c226675db1 100644 --- a/rtc_base/buffer_unittest.cc +++ b/rtc_base/buffer_unittest.cc @@ -10,13 +10,14 @@ #include "rtc_base/buffer.h" +#include #include #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "test/gmock.h" #include "test/gtest.h" @@ -25,11 +26,18 @@ namespace webrtc { namespace { +using ::testing::Each; using ::testing::ElementsAre; using ::testing::ElementsAreArray; +using ::testing::Eq; -constexpr uint8_t kTestData[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; +auto kTestData = + std::to_array({0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}); + +uint8_t* TestDataAtIndex(int index) { + return std::span(kTestData).subspan(index).data(); +} void TestBuf(const Buffer& b1, size_t size, size_t capacity) { EXPECT_EQ(b1.size(), size); @@ -43,28 +51,26 @@ TEST(BufferTest, TestConstructEmpty) { TestBuf(Buffer(Buffer()), 0, 0); TestBuf(Buffer::CreateUninitializedWithSize(0), 0, 0); - // We can't use a literal 0 for the first argument, because C++ will allow - // that to be considered a null pointer, which makes the call ambiguous. - TestBuf(Buffer(0 + 0, 10), 0, 10); + TestBuf(Buffer::CreateWithCapacity(10), 0, 10); - TestBuf(Buffer(kTestData, 0), 0, 0); - TestBuf(Buffer(kTestData, 0, 20), 0, 20); + TestBuf(Buffer(kTestData.data(), 0), 0, 0); + TestBuf(Buffer(kTestData.data(), 0, 20), 0, 20); } TEST(BufferTest, TestConstructData) { - Buffer buf(kTestData, 7); + Buffer buf(kTestData.data(), 7); EXPECT_EQ(buf.size(), 7u); EXPECT_EQ(buf.capacity(), 7u); EXPECT_FALSE(buf.empty()); - EXPECT_EQ(0, memcmp(buf.data(), kTestData, 7)); + EXPECT_EQ(0, memcmp(buf.data(), kTestData.data(), 7)); } TEST(BufferTest, TestConstructDataWithCapacity) { - Buffer buf(kTestData, 7, 14); + Buffer buf(kTestData.data(), 7, 14); EXPECT_EQ(buf.size(), 7u); EXPECT_EQ(buf.capacity(), 14u); EXPECT_FALSE(buf.empty()); - EXPECT_EQ(0, memcmp(buf.data(), kTestData, 7)); + EXPECT_EQ(0, memcmp(buf.data(), kTestData.data(), 7)); } TEST(BufferTest, TestConstructArray) { @@ -72,38 +78,38 @@ TEST(BufferTest, TestConstructArray) { EXPECT_EQ(buf.size(), 16u); EXPECT_EQ(buf.capacity(), 16u); EXPECT_FALSE(buf.empty()); - EXPECT_EQ(0, memcmp(buf.data(), kTestData, 16)); + EXPECT_EQ(0, memcmp(buf.data(), kTestData.data(), 16)); } TEST(BufferTest, TestStringViewConversion) { Buffer buf(kTestData); absl::string_view view = buf; - EXPECT_EQ(view, - absl::string_view(reinterpret_cast(kTestData), 16u)); + EXPECT_EQ(view, absl::string_view( + reinterpret_cast(kTestData.data()), 16u)); } TEST(BufferTest, TestSetData) { - Buffer buf(kTestData + 4, 7); - buf.SetData(kTestData, 9); + Buffer buf(TestDataAtIndex(4), 7); + buf.SetData(kTestData.data(), 9); EXPECT_EQ(buf.size(), 9u); EXPECT_EQ(buf.capacity(), 7u * 3 / 2); EXPECT_FALSE(buf.empty()); - EXPECT_EQ(0, memcmp(buf.data(), kTestData, 9)); + EXPECT_EQ(0, memcmp(buf.data(), kTestData.data(), 9)); Buffer buf2; buf2.SetData(buf); EXPECT_EQ(buf.size(), 9u); EXPECT_EQ(buf.capacity(), 7u * 3 / 2); - EXPECT_EQ(0, memcmp(buf.data(), kTestData, 9)); + EXPECT_EQ(0, memcmp(buf.data(), kTestData.data(), 9)); } TEST(BufferTest, TestAppendData) { - Buffer buf(kTestData + 4, 3); - buf.AppendData(kTestData + 10, 2); + Buffer buf(TestDataAtIndex(4), 3); + buf.AppendData(TestDataAtIndex(10), 2); const int8_t exp[] = {0x4, 0x5, 0x6, 0xa, 0xb}; EXPECT_EQ(buf, Buffer(exp)); Buffer buf2; buf2.AppendData(buf); - buf2.AppendData(ArrayView(buf)); + buf2.AppendData(std::span(buf)); const int8_t exp2[] = {0x4, 0x5, 0x6, 0xa, 0xb, 0x4, 0x5, 0x6, 0xa, 0xb}; EXPECT_EQ(buf2, Buffer(exp2)); } @@ -111,33 +117,34 @@ TEST(BufferTest, TestAppendData) { TEST(BufferTest, TestSetAndAppendWithUnknownArg) { struct TestDataContainer { size_t size() const { return 3; } - const uint8_t* data() const { return kTestData; } + const uint8_t* data() const { return kTestData.data(); } }; Buffer buf; buf.SetData(TestDataContainer()); EXPECT_EQ(3u, buf.size()); - EXPECT_EQ(Buffer(kTestData, 3), buf); + EXPECT_EQ(Buffer(kTestData.data(), 3), buf); EXPECT_THAT(buf, ElementsAre(0, 1, 2)); buf.AppendData(TestDataContainer()); EXPECT_EQ(6u, buf.size()); - EXPECT_EQ(0, memcmp(buf.data(), kTestData, 3)); - EXPECT_EQ(0, memcmp(buf.data() + 3, kTestData, 3)); + EXPECT_EQ(0, memcmp(buf.data(), kTestData.data(), 3)); + EXPECT_EQ(0, memcmp(std::span(buf).subspan(3).data(), + kTestData.data(), 3)); EXPECT_THAT(buf, ElementsAre(0, 1, 2, 0, 1, 2)); } TEST(BufferTest, TestSetSizeSmaller) { Buffer buf; - buf.SetData(kTestData, 15); + buf.SetData(kTestData.data(), 15); buf.SetSize(10); EXPECT_EQ(buf.size(), 10u); EXPECT_EQ(buf.capacity(), 15u); // Hasn't shrunk. EXPECT_FALSE(buf.empty()); - EXPECT_EQ(buf, Buffer(kTestData, 10)); + EXPECT_EQ(buf, Buffer(kTestData.data(), 10)); } TEST(BufferTest, TestSetSizeLarger) { Buffer buf; - buf.SetData(kTestData, 15); + buf.SetData(kTestData.data(), 15); EXPECT_EQ(buf.size(), 15u); EXPECT_EQ(buf.capacity(), 15u); EXPECT_FALSE(buf.empty()); @@ -145,7 +152,7 @@ TEST(BufferTest, TestSetSizeLarger) { EXPECT_EQ(buf.size(), 20u); EXPECT_EQ(buf.capacity(), 15u * 3 / 2); // Has grown. EXPECT_FALSE(buf.empty()); - EXPECT_EQ(0, memcmp(buf.data(), kTestData, 15)); + EXPECT_EQ(0, memcmp(buf.data(), kTestData.data(), 15)); } TEST(BufferTest, TestEnsureCapacitySmaller) { @@ -159,18 +166,18 @@ TEST(BufferTest, TestEnsureCapacitySmaller) { } TEST(BufferTest, TestEnsureCapacityLarger) { - Buffer buf(kTestData, 5); + Buffer buf(kTestData.data(), 5); buf.EnsureCapacity(10); const int8_t* data = buf.data(); EXPECT_EQ(buf.capacity(), 10u); - buf.AppendData(kTestData + 5, 5); + buf.AppendData(TestDataAtIndex(5), 5); EXPECT_EQ(buf.data(), data); // No reallocation. EXPECT_FALSE(buf.empty()); - EXPECT_EQ(buf, Buffer(kTestData, 10)); + EXPECT_EQ(buf, Buffer(kTestData.data(), 10)); } TEST(BufferTest, TestMoveConstruct) { - Buffer buf1(kTestData, 3, 40); + Buffer buf1(kTestData.data(), 3, 40); const uint8_t* data = buf1.data(); Buffer buf2(std::move(buf1)); EXPECT_EQ(buf2.size(), 3u); @@ -185,7 +192,7 @@ TEST(BufferTest, TestMoveConstruct) { } TEST(BufferTest, TestMoveAssign) { - Buffer buf1(kTestData, 3, 40); + Buffer buf1(kTestData.data(), 3, 40); const uint8_t* data = buf1.data(); Buffer buf2(kTestData); buf2 = std::move(buf1); @@ -206,14 +213,14 @@ TEST(BufferTest, TestMoveAssignSelf) { // state could be caught by the DCHECKs and/or by the leak checker.) We need // to be sneaky when testing this; if we're doing a too-obvious // move-assign-to-self, clang's -Wself-move triggers at compile time. - Buffer buf(kTestData, 3, 40); + Buffer buf(kTestData.data(), 3, 40); Buffer* buf_ptr = &buf; buf = std::move(*buf_ptr); } TEST(BufferTest, TestSwap) { - Buffer buf1(kTestData, 3); - Buffer buf2(kTestData, 6, 40); + Buffer buf1(kTestData.data(), 3); + Buffer buf2(kTestData.data(), 6, 40); uint8_t* data1 = buf1.data(); uint8_t* data2 = buf2.data(); using std::swap; @@ -230,7 +237,7 @@ TEST(BufferTest, TestSwap) { TEST(BufferTest, TestClear) { Buffer buf; - buf.SetData(kTestData, 15); + buf.SetData(kTestData.data(), 15); EXPECT_EQ(buf.size(), 15u); EXPECT_EQ(buf.capacity(), 15u); EXPECT_FALSE(buf.empty()); @@ -243,15 +250,15 @@ TEST(BufferTest, TestClear) { } TEST(BufferTest, TestLambdaSetAppend) { - auto setter = [](ArrayView av) { + auto setter = [](std::span av) { for (int i = 0; i != 15; ++i) av[i] = kTestData[i]; return 15; }; Buffer buf1; - buf1.SetData(kTestData, 15); - buf1.AppendData(kTestData, 15); + buf1.SetData(kTestData.data(), 15); + buf1.AppendData(kTestData.data(), 15); Buffer buf2; EXPECT_EQ(buf2.SetData(15, setter), 15u); @@ -263,15 +270,15 @@ TEST(BufferTest, TestLambdaSetAppend) { } TEST(BufferTest, TestLambdaSetAppendSigned) { - auto setter = [](ArrayView av) { + auto setter = [](std::span av) { for (int i = 0; i != 15; ++i) av[i] = kTestData[i]; return 15; }; Buffer buf1; - buf1.SetData(kTestData, 15); - buf1.AppendData(kTestData, 15); + buf1.SetData(kTestData.data(), 15); + buf1.AppendData(kTestData.data(), 15); Buffer buf2; EXPECT_EQ(buf2.SetData(15, setter), 15u); @@ -283,14 +290,14 @@ TEST(BufferTest, TestLambdaSetAppendSigned) { } TEST(BufferTest, TestLambdaAppendEmpty) { - auto setter = [](ArrayView av) { + auto setter = [](std::span av) { for (int i = 0; i != 15; ++i) av[i] = kTestData[i]; return 15; }; Buffer buf1; - buf1.SetData(kTestData, 15); + buf1.SetData(kTestData.data(), 15); Buffer buf2; EXPECT_EQ(buf2.AppendData(15, setter), 15u); @@ -301,7 +308,7 @@ TEST(BufferTest, TestLambdaAppendEmpty) { } TEST(BufferTest, TestLambdaAppendPartial) { - auto setter = [](ArrayView av) { + auto setter = [](std::span av) { for (int i = 0; i != 7; ++i) av[i] = kTestData[i]; return 7; @@ -317,7 +324,7 @@ TEST(BufferTest, TestLambdaAppendPartial) { TEST(BufferTest, TestMutableLambdaSetAppend) { uint8_t magic_number = 17; - auto setter = [magic_number](ArrayView av) mutable { + auto setter = [magic_number](std::span av) mutable { for (int i = 0; i != 15; ++i) { av[i] = magic_number; ++magic_number; @@ -336,12 +343,12 @@ TEST(BufferTest, TestMutableLambdaSetAppend) { EXPECT_FALSE(buf.empty()); for (uint8_t i = 0; i != buf.size(); ++i) { - EXPECT_EQ(buf.data()[i], magic_number + i); + EXPECT_EQ(buf[i], magic_number + i); } } TEST(BufferTest, TestBracketRead) { - Buffer buf(kTestData, 7); + Buffer buf(kTestData.data(), 7); EXPECT_EQ(buf.size(), 7u); EXPECT_EQ(buf.capacity(), 7u); EXPECT_NE(buf.data(), nullptr); @@ -353,7 +360,7 @@ TEST(BufferTest, TestBracketRead) { } TEST(BufferTest, TestBracketReadConst) { - Buffer buf(kTestData, 7); + Buffer buf(kTestData.data(), 7); EXPECT_EQ(buf.size(), 7u); EXPECT_EQ(buf.capacity(), 7u); EXPECT_NE(buf.data(), nullptr); @@ -377,20 +384,21 @@ TEST(BufferTest, TestBracketWrite) { buf[i] = kTestData[i]; } - EXPECT_THAT(buf, ElementsAreArray(kTestData, 7)); + EXPECT_THAT(buf, ElementsAreArray(kTestData.data(), 7)); } TEST(BufferTest, TestBeginEnd) { const Buffer cbuf(kTestData); Buffer buf(kTestData); - auto* b1 = cbuf.begin(); + auto b1 = cbuf.begin(); for (auto& x : buf) { EXPECT_EQ(*b1, x); + ASSERT_NE(b1, cbuf.end()); ++b1; ++x; } EXPECT_EQ(cbuf.end(), b1); - auto* b2 = buf.begin(); + auto b2 = buf.begin(); for (auto& y : cbuf) { EXPECT_EQ(*b2, y + 1); ++b2; @@ -471,12 +479,13 @@ TEST(BufferDeathTest, DieOnUseAfterMove) { } TEST(ZeroOnFreeBufferTest, TestZeroOnSetData) { - ZeroOnFreeBuffer buf(kTestData, 7); + ZeroOnFreeBuffer buf(kTestData.data(), 7); const uint8_t* old_data = buf.data(); const size_t old_capacity = buf.capacity(); - const size_t old_size = buf.size(); constexpr size_t offset = 1; - buf.SetData(kTestData + offset, 2); + // Pointer to the last five bytes of the underlying buffer. + auto to_be_zeroed = std::span(buf).subspan(2); + buf.SetData(TestDataAtIndex(offset), 2); // Sanity checks to make sure the underlying heap memory was not reallocated. EXPECT_EQ(old_data, buf.data()); EXPECT_EQ(old_capacity, buf.capacity()); @@ -484,23 +493,21 @@ TEST(ZeroOnFreeBufferTest, TestZeroOnSetData) { // been zeroed. EXPECT_EQ(kTestData[offset], buf[0]); EXPECT_EQ(kTestData[offset + 1], buf[1]); - for (size_t i = 2; i < old_size; i++) { - EXPECT_EQ(0, old_data[i]); - } + EXPECT_THAT(to_be_zeroed, Each(Eq(0))); } TEST(ZeroOnFreeBufferTest, TestZeroOnSetDataFromSetter) { static constexpr size_t offset = 1; - const auto setter = [](ArrayView av) { + const auto setter = [](std::span av) { for (int i = 0; i != 2; ++i) av[i] = kTestData[offset + i]; return 2; }; - ZeroOnFreeBuffer buf(kTestData, 7); + ZeroOnFreeBuffer buf(kTestData.data(), 7); const uint8_t* old_data = buf.data(); + auto to_be_zeroed = std::span(buf).subspan(2); const size_t old_capacity = buf.capacity(); - const size_t old_size = buf.size(); buf.SetData(2, setter); // Sanity checks to make sure the underlying heap memory was not reallocated. EXPECT_EQ(old_data, buf.data()); @@ -509,16 +516,14 @@ TEST(ZeroOnFreeBufferTest, TestZeroOnSetDataFromSetter) { // been zeroed. EXPECT_EQ(kTestData[offset], buf[0]); EXPECT_EQ(kTestData[offset + 1], buf[1]); - for (size_t i = 2; i < old_size; i++) { - EXPECT_EQ(0, old_data[i]); - } + EXPECT_THAT(to_be_zeroed, Each(Eq(0))); } TEST(ZeroOnFreeBufferTest, TestZeroOnSetSize) { - ZeroOnFreeBuffer buf(kTestData, 7); + ZeroOnFreeBuffer buf(kTestData.data(), 7); + auto to_be_zeroed = std::span(buf).subspan(2); const uint8_t* old_data = buf.data(); const size_t old_capacity = buf.capacity(); - const size_t old_size = buf.size(); buf.SetSize(2); // Sanity checks to make sure the underlying heap memory was not reallocated. EXPECT_EQ(old_data, buf.data()); @@ -527,24 +532,20 @@ TEST(ZeroOnFreeBufferTest, TestZeroOnSetSize) { // been zeroed. EXPECT_EQ(kTestData[0], buf[0]); EXPECT_EQ(kTestData[1], buf[1]); - for (size_t i = 2; i < old_size; i++) { - EXPECT_EQ(0, old_data[i]); - } + EXPECT_THAT(to_be_zeroed, Each(Eq(0))); } TEST(ZeroOnFreeBufferTest, TestZeroOnClear) { - ZeroOnFreeBuffer buf(kTestData, 7); + ZeroOnFreeBuffer buf(kTestData.data(), 7); + auto to_be_zeroed = std::span(buf); const uint8_t* old_data = buf.data(); const size_t old_capacity = buf.capacity(); - const size_t old_size = buf.size(); buf.Clear(); // Sanity checks to make sure the underlying heap memory was not reallocated. EXPECT_EQ(old_data, buf.data()); EXPECT_EQ(old_capacity, buf.capacity()); // The underlying memory was not released but cleared. - for (size_t i = 0; i < old_size; i++) { - EXPECT_EQ(0, old_data[i]); - } + EXPECT_THAT(to_be_zeroed, Each(Eq(0))); } } // namespace webrtc diff --git a/rtc_base/byte_buffer.cc b/rtc_base/byte_buffer.cc index 719720d777a..b3ebb1ec8a6 100644 --- a/rtc_base/byte_buffer.cc +++ b/rtc_base/byte_buffer.cc @@ -12,10 +12,10 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/byte_order.h" namespace webrtc { @@ -25,7 +25,7 @@ ByteBufferWriter::ByteBufferWriter() : ByteBufferWriterT() {} ByteBufferWriter::ByteBufferWriter(const uint8_t* bytes, size_t len) : ByteBufferWriterT(bytes, len) {} -ByteBufferReader::ByteBufferReader(ArrayView bytes) { +ByteBufferReader::ByteBufferReader(std::span bytes) { Construct(bytes.data(), bytes.size()); } @@ -146,7 +146,7 @@ bool ByteBufferReader::ReadStringView(absl::string_view* val, size_t len) { return true; } -bool ByteBufferReader::ReadBytes(ArrayView val) { +bool ByteBufferReader::ReadBytes(std::span val) { if (val.empty()) { return true; } diff --git a/rtc_base/byte_buffer.h b/rtc_base/byte_buffer.h index cc7d6487a80..64648d0a01a 100644 --- a/rtc_base/byte_buffer.h +++ b/rtc_base/byte_buffer.h @@ -14,11 +14,11 @@ #include #include +#include #include #include "absl/base/attributes.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" @@ -41,8 +41,8 @@ class ByteBufferWriterT { const value_type* Data() const { return buffer_.data(); } size_t Length() const { return buffer_.size(); } size_t Capacity() const { return buffer_.capacity(); } - ArrayView DataView() const { - return MakeArrayView(Data(), Length()); + std::span DataView() const { + return std::span(Data(), Length()); } // Accessor that returns a string_view, independent of underlying type. // Intended to provide access for existing users that expect char* @@ -97,12 +97,12 @@ class ByteBufferWriterT { val.size()); } // Write an array of bytes (uint8_t) - [[deprecated("issues.webrtc.org/4225170 - use Write(ArrayView)")]] + [[deprecated("issues.webrtc.org/4225170 - use Write(std::span)")]] void WriteBytes(const uint8_t* val, size_t len) { WriteBytesInternal(reinterpret_cast(val), len); } - void Write(ArrayView data) { + void Write(std::span data) { WriteBytesInternal(data.data(), data.size()); } @@ -157,7 +157,7 @@ class ByteBufferWriter : public ByteBufferWriterT> { class ByteBufferReader { public: explicit ByteBufferReader( - ArrayView bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); + std::span bytes ABSL_ATTRIBUTE_LIFETIME_BOUND); explicit ByteBufferReader(const ByteBufferWriter& buf); @@ -168,8 +168,8 @@ class ByteBufferReader { // Returns number of unprocessed bytes. size_t Length() const { return end_ - start_; } // Returns a view of the unprocessed data. Does not move current position. - ArrayView DataView() const { - return ArrayView(bytes_ + start_, end_ - start_); + std::span DataView() const { + return std::span(bytes_ + start_, end_ - start_); } // Read a next value from the buffer. Return false if there isn't @@ -181,7 +181,7 @@ class ByteBufferReader { bool ReadUInt64(uint64_t* val); bool ReadUVarint(uint64_t* val); // Copies the val.size() next bytes into val.data(). - bool ReadBytes(ArrayView val); + bool ReadBytes(std::span val); // Appends next `len` bytes from the buffer to `val`. Returns false // if there is less than `len` bytes left. bool ReadString(std::string* val, size_t len); diff --git a/rtc_base/byte_buffer_unittest.cc b/rtc_base/byte_buffer_unittest.cc index 07752b10926..cdc34bed4a2 100644 --- a/rtc_base/byte_buffer_unittest.cc +++ b/rtc_base/byte_buffer_unittest.cc @@ -12,11 +12,11 @@ #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" #include "test/gmock.h" @@ -56,9 +56,12 @@ TEST(ByteBufferTest, TestByteOrder) { EXPECT_EQ(n64, HostToNetwork64(n64)); // GetBE converts big endian to little endian here. - EXPECT_EQ(n16 >> 8, GetBE16(&n16)); - EXPECT_EQ(n32 >> 24, GetBE32(&n32)); - EXPECT_EQ(n64 >> 56, GetBE64(&n64)); + EXPECT_EQ(n16 >> 8, GetBE16(std::span( + reinterpret_cast(&n16), 2))); + EXPECT_EQ(n32 >> 24, GetBE32(std::span( + reinterpret_cast(&n32), 4))); + EXPECT_EQ(n64 >> 56, GetBE64(std::span( + reinterpret_cast(&n64), 8))); } else { // The host is little endian. EXPECT_NE(n16, HostToNetwork16(n16)); @@ -66,14 +69,23 @@ TEST(ByteBufferTest, TestByteOrder) { EXPECT_NE(n64, HostToNetwork64(n64)); // GetBE converts little endian to big endian here. - EXPECT_EQ(GetBE16(&n16), HostToNetwork16(n16)); - EXPECT_EQ(GetBE32(&n32), HostToNetwork32(n32)); - EXPECT_EQ(GetBE64(&n64), HostToNetwork64(n64)); + EXPECT_EQ(GetBE16(std::span( + reinterpret_cast(&n16), 2)), + HostToNetwork16(n16)); + EXPECT_EQ(GetBE32(std::span( + reinterpret_cast(&n32), 4)), + HostToNetwork32(n32)); + EXPECT_EQ(GetBE64(std::span( + reinterpret_cast(&n64), 8)), + HostToNetwork64(n64)); // GetBE converts little endian to big endian here. - EXPECT_EQ(n16 << 8, GetBE16(&n16)); - EXPECT_EQ(n32 << 24, GetBE32(&n32)); - EXPECT_EQ(n64 << 56, GetBE64(&n64)); + EXPECT_EQ(n16 << 8, GetBE16(std::span( + reinterpret_cast(&n16), 2))); + EXPECT_EQ(n32 << 24, GetBE32(std::span( + reinterpret_cast(&n32), 4))); + EXPECT_EQ(n64 << 56, GetBE64(std::span( + reinterpret_cast(&n64), 8))); } } @@ -105,7 +117,7 @@ TEST(ByteBufferTest, TestBufferLength) { TEST(ByteBufferTest, TestReadWriteBuffer) { ByteBufferWriter buffer; - ByteBufferReader read_buf(ArrayView(nullptr, 0)); + ByteBufferReader read_buf(std::span{}); uint8_t ru8; EXPECT_FALSE(read_buf.ReadUInt8(&ru8)); @@ -171,7 +183,7 @@ TEST(ByteBufferTest, TestReadWriteBuffer) { // Write and read bytes uint8_t write_bytes[] = {3, 2, 1}; - buffer.Write(ArrayView(write_bytes, 3)); + buffer.Write(std::span(write_bytes, 3)); ByteBufferReader read_buf7(buffer); uint8_t read_bytes[3]; EXPECT_TRUE(read_buf7.ReadBytes(read_bytes)); @@ -248,10 +260,10 @@ TEST(ByteBufferTest, TestWriteBuffer) { EXPECT_EQ(read_buf11.Length(), 0U); } -TEST(ByteBufferTest, TestWriteArrayView) { +TEST(ByteBufferTest, TestWriteSpan) { const uint8_t write_data[3] = {3, 2, 1}; - // Write and read arrayview - ArrayView write_view(write_data); + // Write and read std::span + std::span write_view(write_data); ByteBufferWriter buffer; buffer.Write(write_view); ByteBufferReader read_buf12(buffer); @@ -276,7 +288,7 @@ TEST(ByteBufferTest, TestReadStringView) { for (const auto& test : tests) buffer += test; - ArrayView bytes(reinterpret_cast(&buffer[0]), + std::span bytes(reinterpret_cast(&buffer[0]), buffer.size()); ByteBufferReader read_buf(bytes); @@ -347,9 +359,9 @@ TEST(ByteBufferTest, TestReadWriteUVarint) { EXPECT_EQ(size, read_buffer.Length()); } -TEST(ByteBufferTest, ReadFromArrayView) { +TEST(ByteBufferTest, ReadFromSpan) { const uint8_t buf[] = {'a', 'b', 'c'}; - ArrayView view(buf, 3); + std::span view(buf, 3); ByteBufferReader read_buffer(view); uint8_t val; @@ -362,18 +374,18 @@ TEST(ByteBufferTest, ReadFromArrayView) { EXPECT_FALSE(read_buffer.ReadUInt8(&val)); } -TEST(ByteBufferTest, ReadToArrayView) { +TEST(ByteBufferTest, ReadToSpan) { const uint8_t buf[] = {'a', 'b', 'c'}; - ArrayView stored_view(buf, 3); + std::span stored_view(buf, 3); ByteBufferReader read_buffer(stored_view); uint8_t result[] = {'1', '2', '3'}; - EXPECT_TRUE(read_buffer.ReadBytes(MakeArrayView(result, 2))); + EXPECT_TRUE(read_buffer.ReadBytes(std::span(result, 2))); EXPECT_EQ(result[0], 'a'); EXPECT_EQ(result[1], 'b'); EXPECT_EQ(result[2], '3'); - EXPECT_TRUE(read_buffer.ReadBytes(MakeArrayView(&result[2], 1))); + EXPECT_TRUE(read_buffer.ReadBytes(std::span(&result[2], 1))); EXPECT_EQ(result[2], 'c'); - EXPECT_FALSE(read_buffer.ReadBytes(MakeArrayView(result, 1))); + EXPECT_FALSE(read_buffer.ReadBytes(std::span(result, 1))); } } // namespace webrtc diff --git a/rtc_base/byte_order.h b/rtc_base/byte_order.h index 3a9bfe46e0c..bf44c450e64 100644 --- a/rtc_base/byte_order.h +++ b/rtc_base/byte_order.h @@ -14,7 +14,9 @@ #include #include #include +#include +#include "rtc_base/checks.h" #include "rtc_base/system/arch.h" // IWYU pragma: keep #if defined(WEBRTC_POSIX) @@ -92,77 +94,89 @@ namespace webrtc { // Reading and writing of little and big-endian numbers from memory -inline void Set8(void* memory, size_t offset, uint8_t v) { - static_cast(memory)[offset] = v; +inline void Set8(std::span data, size_t offset, uint8_t v) { + data[offset] = v; } -inline uint8_t Get8(const void* memory, size_t offset) { - return static_cast(memory)[offset]; +inline uint8_t Get8(std::span data, size_t offset) { + return data[offset]; } -inline void SetBE16(void* memory, uint16_t v) { +inline void SetBE16(std::span data, uint16_t v) { + RTC_DCHECK_GE(data.size(), sizeof(v)); uint16_t val = htobe16(v); - memcpy(memory, &val, sizeof(val)); + memcpy(data.data(), &val, sizeof(val)); } -inline void SetBE32(void* memory, uint32_t v) { +inline void SetBE32(std::span data, uint32_t v) { + RTC_DCHECK_GE(data.size(), sizeof(v)); uint32_t val = htobe32(v); - memcpy(memory, &val, sizeof(val)); + memcpy(data.data(), &val, sizeof(val)); } -inline void SetBE64(void* memory, uint64_t v) { +inline void SetBE64(std::span data, uint64_t v) { + RTC_DCHECK_GE(data.size(), sizeof(v)); uint64_t val = htobe64(v); - memcpy(memory, &val, sizeof(val)); + memcpy(data.data(), &val, sizeof(val)); } -inline uint16_t GetBE16(const void* memory) { +inline uint16_t GetBE16(std::span data) { uint16_t val; - memcpy(&val, memory, sizeof(val)); + RTC_DCHECK_GE(data.size(), sizeof(val)); + memcpy(&val, data.data(), sizeof(val)); return be16toh(val); } -inline uint32_t GetBE32(const void* memory) { +inline uint32_t GetBE32(std::span data) { uint32_t val; - memcpy(&val, memory, sizeof(val)); + RTC_DCHECK_GE(data.size(), sizeof(val)); + memcpy(&val, data.data(), sizeof(val)); return be32toh(val); } -inline uint64_t GetBE64(const void* memory) { +inline uint64_t GetBE64(std::span data) { uint64_t val; - memcpy(&val, memory, sizeof(val)); + RTC_DCHECK_GE(data.size(), sizeof(val)); + memcpy(&val, data.data(), sizeof(val)); return be64toh(val); } -inline void SetLE16(void* memory, uint16_t v) { +inline void SetLE16(std::span data, uint16_t v) { + RTC_DCHECK_GE(data.size(), sizeof(v)); uint16_t val = htole16(v); - memcpy(memory, &val, sizeof(val)); + memcpy(data.data(), &val, sizeof(val)); } -inline void SetLE32(void* memory, uint32_t v) { +inline void SetLE32(std::span data, uint32_t v) { + RTC_DCHECK_GE(data.size(), sizeof(v)); uint32_t val = htole32(v); - memcpy(memory, &val, sizeof(val)); + memcpy(data.data(), &val, sizeof(val)); } -inline void SetLE64(void* memory, uint64_t v) { +inline void SetLE64(std::span data, uint64_t v) { + RTC_DCHECK_GE(data.size(), sizeof(v)); uint64_t val = htole64(v); - memcpy(memory, &val, sizeof(val)); + memcpy(data.data(), &val, sizeof(val)); } -inline uint16_t GetLE16(const void* memory) { +inline uint16_t GetLE16(std::span data) { uint16_t val; - memcpy(&val, memory, sizeof(val)); + RTC_DCHECK_GE(data.size(), sizeof(val)); + memcpy(&val, data.data(), sizeof(val)); return le16toh(val); } -inline uint32_t GetLE32(const void* memory) { +inline uint32_t GetLE32(std::span data) { uint32_t val; - memcpy(&val, memory, sizeof(val)); + RTC_DCHECK_GE(data.size(), sizeof(val)); + memcpy(&val, data.data(), sizeof(val)); return le32toh(val); } -inline uint64_t GetLE64(const void* memory) { +inline uint64_t GetLE64(std::span data) { uint64_t val; - memcpy(&val, memory, sizeof(val)); + RTC_DCHECK_GE(data.size(), sizeof(val)); + memcpy(&val, data.data(), sizeof(val)); return le64toh(val); } diff --git a/rtc_base/byte_order_unittest.cc b/rtc_base/byte_order_unittest.cc index 8c1e10edf22..893c0e734b3 100644 --- a/rtc_base/byte_order_unittest.cc +++ b/rtc_base/byte_order_unittest.cc @@ -13,6 +13,7 @@ #include #include +#include #include "test/gtest.h" @@ -21,27 +22,28 @@ namespace webrtc { // Test memory set functions put values into memory in expected order. TEST(ByteOrderTest, TestSet) { uint8_t buf[8] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; - Set8(buf, 0, 0xfb); - Set8(buf, 1, 0x12); + std::span av(buf); + Set8(av, 0, 0xfb); + Set8(av, 1, 0x12); EXPECT_EQ(0xfb, buf[0]); EXPECT_EQ(0x12, buf[1]); - SetBE16(buf, 0x1234); + SetBE16(av, 0x1234); EXPECT_EQ(0x12, buf[0]); EXPECT_EQ(0x34, buf[1]); - SetLE16(buf, 0x1234); + SetLE16(av, 0x1234); EXPECT_EQ(0x34, buf[0]); EXPECT_EQ(0x12, buf[1]); - SetBE32(buf, 0x12345678); + SetBE32(av, 0x12345678); EXPECT_EQ(0x12, buf[0]); EXPECT_EQ(0x34, buf[1]); EXPECT_EQ(0x56, buf[2]); EXPECT_EQ(0x78, buf[3]); - SetLE32(buf, 0x12345678); + SetLE32(av, 0x12345678); EXPECT_EQ(0x78, buf[0]); EXPECT_EQ(0x56, buf[1]); EXPECT_EQ(0x34, buf[2]); EXPECT_EQ(0x12, buf[3]); - SetBE64(buf, UINT64_C(0x0123456789abcdef)); + SetBE64(av, UINT64_C(0x0123456789abcdef)); EXPECT_EQ(0x01, buf[0]); EXPECT_EQ(0x23, buf[1]); EXPECT_EQ(0x45, buf[2]); @@ -50,7 +52,7 @@ TEST(ByteOrderTest, TestSet) { EXPECT_EQ(0xab, buf[5]); EXPECT_EQ(0xcd, buf[6]); EXPECT_EQ(0xef, buf[7]); - SetLE64(buf, UINT64_C(0x0123456789abcdef)); + SetLE64(av, UINT64_C(0x0123456789abcdef)); EXPECT_EQ(0xef, buf[0]); EXPECT_EQ(0xcd, buf[1]); EXPECT_EQ(0xab, buf[2]); @@ -64,6 +66,7 @@ TEST(ByteOrderTest, TestSet) { // Test memory get functions get values from memory in expected order. TEST(ByteOrderTest, TestGet) { uint8_t buf[8]; + std::span av(buf); buf[0] = 0x01u; buf[1] = 0x23u; buf[2] = 0x45u; @@ -72,14 +75,14 @@ TEST(ByteOrderTest, TestGet) { buf[5] = 0xabu; buf[6] = 0xcdu; buf[7] = 0xefu; - EXPECT_EQ(0x01u, Get8(buf, 0)); - EXPECT_EQ(0x23u, Get8(buf, 1)); - EXPECT_EQ(0x0123u, GetBE16(buf)); - EXPECT_EQ(0x2301u, GetLE16(buf)); - EXPECT_EQ(0x01234567u, GetBE32(buf)); - EXPECT_EQ(0x67452301u, GetLE32(buf)); - EXPECT_EQ(UINT64_C(0x0123456789abcdef), GetBE64(buf)); - EXPECT_EQ(UINT64_C(0xefcdab8967452301), GetLE64(buf)); + EXPECT_EQ(0x01u, Get8(av, 0)); + EXPECT_EQ(0x23u, Get8(av, 1)); + EXPECT_EQ(0x0123u, GetBE16(av)); + EXPECT_EQ(0x2301u, GetLE16(av)); + EXPECT_EQ(0x01234567u, GetBE32(av)); + EXPECT_EQ(0x67452301u, GetLE32(av)); + EXPECT_EQ(UINT64_C(0x0123456789abcdef), GetBE64(av)); + EXPECT_EQ(UINT64_C(0xefcdab8967452301), GetLE64(av)); } } // namespace webrtc diff --git a/rtc_base/callback_list.cc b/rtc_base/callback_list.cc index 8c108a52086..68adf9f2296 100644 --- a/rtc_base/callback_list.cc +++ b/rtc_base/callback_list.cc @@ -62,6 +62,12 @@ void CallbackListReceivers::RemoveReceivers(const void* removal_tag) { ++first_todo; RTC_DCHECK_EQ(receivers_[first_remove - 1].removal_tag, removal_tag); --first_remove; + } else { + // When send_in_progress_ we really only iterate the list and + // mark elements with pending_removal_tag(). + RTC_DCHECK(send_in_progress_); + RTC_DCHECK_EQ(receivers_[first_todo].removal_tag, removal_tag); + receivers_[first_todo++].removal_tag = pending_removal_tag(); } } diff --git a/rtc_base/callback_list_unittest.cc b/rtc_base/callback_list_unittest.cc index 77e4c73b79c..7c576ea39c5 100644 --- a/rtc_base/callback_list_unittest.cc +++ b/rtc_base/callback_list_unittest.cc @@ -267,6 +267,54 @@ TEST(CallbackList, RemoveFromSend) { c.Send(); EXPECT_EQ(removal_tag, 1); } + +TEST(CallbackList, RemoveFromSendFirst) { + int removal_tag0 = 0; + int removal_tag1 = 0; + CallbackList<> c; + c.AddReceiver(&removal_tag0, [&] { + c.RemoveReceivers(&removal_tag0); + // Do after RemoveReceivers to make sure the lambda is still valid. + ++removal_tag0; + }); + c.AddReceiver(&removal_tag1, [&] {}); + c.Send(); + c.Send(); + EXPECT_EQ(removal_tag0, 1); +} + +TEST(CallbackList, RemoveLastFromSend) { + int removal_tag0 = 0; + int removal_tag1 = 0; + CallbackList<> c; + c.AddReceiver(&removal_tag0, [] {}); + c.AddReceiver(&removal_tag1, [&] { + c.RemoveReceivers(&removal_tag1); + // Do after RemoveReceivers to make sure the lambda is still valid. + ++removal_tag1; + }); + c.Send(); + c.Send(); + EXPECT_EQ(removal_tag1, 1); +} + +TEST(CallbackList, RemoveMiddleFromSend) { + int removal_tag0 = 0; + int removal_tag1 = 0; + int removal_tag2 = 0; + CallbackList<> c; + c.AddReceiver(&removal_tag0, [&] {}); + c.AddReceiver(&removal_tag1, [&] { + c.RemoveReceivers(&removal_tag1); + // Do after RemoveReceivers to make sure the lambda is still valid. + ++removal_tag1; + }); + c.AddReceiver(&removal_tag2, [&] {}); + c.Send(); + c.Send(); + EXPECT_EQ(removal_tag1, 1); +} + #pragma clang diagnostic pop } // namespace diff --git a/rtc_base/checks.cc b/rtc_base/checks.cc index a2e4079f231..d1e00947d9c 100644 --- a/rtc_base/checks.cc +++ b/rtc_base/checks.cc @@ -37,6 +37,8 @@ #include "rtc_base/checks.h" +namespace webrtc { + namespace { #if defined(__GNUC__) @@ -59,7 +61,6 @@ void AppendFormat(std::string* s, const char* fmt, ...) { } } // namespace -namespace webrtc { namespace webrtc_checks_impl { #if !defined(WEBRTC_CHROMIUM_BUILD) diff --git a/rtc_base/checks.h b/rtc_base/checks.h index 992c98d4da9..26f5da27542 100644 --- a/rtc_base/checks.h +++ b/rtc_base/checks.h @@ -526,4 +526,16 @@ inline T CheckedDivExact(T a, T b) { #endif // __cplusplus +// Hardening assert. This assert can be turned on and off independently +// of debug mode / non debug mode. +// If enabled, it turns into RTC_CHECK. +// If disabled, it turns into RTC_DCHECK. +// This resembles the behavior of ABSL_HARDENING_ASSERT, +// but is not affected by NDEBUG. +#ifdef RTC_HARDENING +#define RTC_HARDENING_ASSERT(x) RTC_CHECK(x) +#else +#define RTC_HARDENING_ASSERT(x) RTC_DCHECK(x) +#endif + #endif // RTC_BASE_CHECKS_H_ diff --git a/rtc_base/copy_on_write_buffer.cc b/rtc_base/copy_on_write_buffer.cc index 160ce94d056..68e98ce7293 100644 --- a/rtc_base/copy_on_write_buffer.cc +++ b/rtc_base/copy_on_write_buffer.cc @@ -12,14 +12,32 @@ #include #include +#include #include +#include +#include #include +#include "absl/algorithm/container.h" #include "absl/strings/string_view.h" +#include "api/make_ref_counted.h" +#include "api/scoped_refptr.h" #include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" namespace webrtc { +CopyOnWriteBuffer::RawBuffer::RawBuffer(size_t size) + : size_(size), data_(std::make_unique_for_overwrite(size)) {} + +scoped_refptr +CopyOnWriteBuffer::CreateBuffer(size_t capacity) { + if (capacity == 0) { + return nullptr; + } + return make_ref_counted(capacity); +} + CopyOnWriteBuffer::CopyOnWriteBuffer() : offset_(0), size_(0) { RTC_DCHECK(IsConsistent()); } @@ -38,18 +56,13 @@ CopyOnWriteBuffer::CopyOnWriteBuffer(absl::string_view s) : CopyOnWriteBuffer(s.data(), s.length()) {} CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size) - : buffer_(size > 0 ? new RefCountedBuffer(size, size) : nullptr), - // note - the RefCountedBuffer will be created uninitialized. - offset_(0), - size_(size) { + : buffer_(CreateBuffer(/*capacity=*/size)), offset_(0), size_(size) { + // note - the RefCountedBuffer will be created uninitialized. RTC_DCHECK(IsConsistent()); } CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity) - : buffer_(size > 0 || capacity > 0 ? new RefCountedBuffer(size, capacity) - : nullptr), - offset_(0), - size_(size) { + : buffer_(CreateBuffer(std::max(size, capacity))), offset_(0), size_(size) { RTC_DCHECK(IsConsistent()); } @@ -65,39 +78,19 @@ bool CopyOnWriteBuffer::operator==(const CopyOnWriteBuffer& buf) const { void CopyOnWriteBuffer::SetSize(size_t size) { RTC_DCHECK(IsConsistent()); - if (!buffer_) { - if (size > 0) { - buffer_ = new RefCountedBuffer(size, size); - // Note - the new buffer will be created uninitialized. - offset_ = 0; - size_ = size; - } - RTC_DCHECK(IsConsistent()); - return; - } - if (size <= size_) { size_ = size; return; } UnshareAndEnsureCapacity(std::max(capacity(), size)); - buffer_->SetSize(size + offset_); size_ = size; RTC_DCHECK(IsConsistent()); } void CopyOnWriteBuffer::EnsureCapacity(size_t new_capacity) { RTC_DCHECK(IsConsistent()); - if (!buffer_) { - if (new_capacity > 0) { - buffer_ = new RefCountedBuffer(0, new_capacity); - offset_ = 0; - size_ = 0; - } - RTC_DCHECK(IsConsistent()); - return; - } else if (new_capacity <= capacity()) { + if (new_capacity <= capacity()) { return; } @@ -109,24 +102,56 @@ void CopyOnWriteBuffer::Clear() { if (!buffer_) return; - if (buffer_->HasOneRef()) { - buffer_->Clear(); - } else { - buffer_ = new RefCountedBuffer(0, capacity()); + if (!buffer_->HasOneRef()) { + buffer_ = CreateBuffer(capacity()); } offset_ = 0; size_ = 0; RTC_DCHECK(IsConsistent()); } +void CopyOnWriteBuffer::Set(std::span data) { + RTC_DCHECK(IsConsistent()); + if (data.empty()) { + offset_ = 0; + size_ = 0; + return; + } + + if (buffer_ == nullptr || !buffer_->HasOneRef() || + buffer_->capacity() < data.size()) { + buffer_ = CreateBuffer(std::max(data.size(), capacity())); + } + absl::c_copy(data, buffer_->data().begin()); + offset_ = 0; + size_ = data.size(); + + RTC_DCHECK(IsConsistent()); +} + +void CopyOnWriteBuffer::Append(std::span data) { + RTC_DCHECK(IsConsistent()); + if (data.empty()) { + return; + } + + UnshareAndEnsureCapacity(std::max(capacity(), size_ + data.size())); + absl::c_copy(data, buffer_->data().subspan(offset_ + size_).begin()); + size_ += data.size(); + + RTC_DCHECK(IsConsistent()); +} + void CopyOnWriteBuffer::UnshareAndEnsureCapacity(size_t new_capacity) { - if (buffer_->HasOneRef() && new_capacity <= capacity()) { + if (buffer_ != nullptr && buffer_->HasOneRef() && + new_capacity <= capacity()) { return; } - buffer_ = - new RefCountedBuffer(buffer_->data() + offset_, size_, new_capacity); + scoped_refptr b = CreateBuffer(new_capacity); + absl::c_copy(AsConstSpan(), b->data().begin()); offset_ = 0; + buffer_ = std::move(b); RTC_DCHECK(IsConsistent()); } diff --git a/rtc_base/copy_on_write_buffer.h b/rtc_base/copy_on_write_buffer.h index be8a2611735..cda92f1e864 100644 --- a/rtc_base/copy_on_write_buffer.h +++ b/rtc_base/copy_on_write_buffer.h @@ -11,10 +11,10 @@ #ifndef RTC_BASE_COPY_ON_WRITE_BUFFER_H_ #define RTC_BASE_COPY_ON_WRITE_BUFFER_H_ -#include - -#include -#include +#include +#include +#include +#include #include #include @@ -30,12 +30,24 @@ namespace webrtc { class RTC_EXPORT CopyOnWriteBuffer { public: + using const_iterator = std::span::iterator; + // An empty buffer. CopyOnWriteBuffer(); // Share the data with an existing buffer. CopyOnWriteBuffer(const CopyOnWriteBuffer& buf); + CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) = default; + // Move contents from an existing buffer. CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) noexcept; + CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) { + RTC_DCHECK(IsConsistent()); + RTC_DCHECK(buf.IsConsistent()); + buffer_ = std::exchange(buf.buffer_, nullptr); + offset_ = std::exchange(buf.offset_, 0); + size_ = std::exchange(buf.size_, 0); + return *this; + } // Construct a buffer from a string, convenient for unittests. explicit CopyOnWriteBuffer(absl::string_view s); @@ -56,11 +68,7 @@ class RTC_EXPORT CopyOnWriteBuffer { internal::BufferCompat::value>::type* = nullptr> CopyOnWriteBuffer(const T* data, size_t size, size_t capacity) : CopyOnWriteBuffer(size, capacity) { - if (buffer_) { - std::memcpy(buffer_->data(), data, size); - offset_ = 0; - size_ = size; - } + SetData(data, size); } // Construct a buffer from the contents of an array. @@ -115,7 +123,7 @@ class RTC_EXPORT CopyOnWriteBuffer { return nullptr; } UnshareAndEnsureCapacity(capacity()); - return buffer_->data() + offset_; + return reinterpret_cast(buffer_->data().subspan(offset_).data()); } // Get const pointer to the data. This will not create a copy of the @@ -124,11 +132,7 @@ class RTC_EXPORT CopyOnWriteBuffer { typename std::enable_if< internal::BufferCompat::value>::type* = nullptr> const T* cdata() const { - RTC_DCHECK(IsConsistent()); - if (!buffer_) { - return nullptr; - } - return buffer_->data() + offset_; + return reinterpret_cast(AsConstSpan().data()); } bool empty() const { return size_ == 0; } @@ -143,40 +147,14 @@ class RTC_EXPORT CopyOnWriteBuffer { return buffer_ ? buffer_->capacity() - offset_ : 0; } - const uint8_t* begin() const { return data(); } - const uint8_t* end() const { return data() + size_; } - - CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) { - RTC_DCHECK(IsConsistent()); - RTC_DCHECK(buf.IsConsistent()); - if (&buf != this) { - buffer_ = buf.buffer_; - offset_ = buf.offset_; - size_ = buf.size_; - } - return *this; - } - - CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) { - RTC_DCHECK(IsConsistent()); - RTC_DCHECK(buf.IsConsistent()); - buffer_ = std::move(buf.buffer_); - offset_ = buf.offset_; - size_ = buf.size_; - buf.offset_ = 0; - buf.size_ = 0; - return *this; - } + const_iterator begin() const { return AsConstSpan().begin(); } + const_iterator end() const { return AsConstSpan().end(); } bool operator==(const CopyOnWriteBuffer& buf) const; - bool operator!=(const CopyOnWriteBuffer& buf) const { - return !(*this == buf); - } - uint8_t operator[](size_t index) const { RTC_DCHECK_LT(index, size()); - return cdata()[index]; + return AsConstSpan()[index]; } // Replace the contents of the buffer. Accepts the same types as the @@ -185,18 +163,7 @@ class RTC_EXPORT CopyOnWriteBuffer { typename std::enable_if< internal::BufferCompat::value>::type* = nullptr> void SetData(const T* data, size_t size) { - RTC_DCHECK(IsConsistent()); - if (!buffer_) { - buffer_ = size > 0 ? new RefCountedBuffer(data, size) : nullptr; - } else if (!buffer_->HasOneRef()) { - buffer_ = new RefCountedBuffer(data, size, capacity()); - } else { - buffer_->SetData(data, size); - } - offset_ = 0; - size_ = size; - - RTC_DCHECK(IsConsistent()); + Set(std::span(reinterpret_cast(data), size)); } template ::value>::type* = nullptr> void AppendData(const T* data, size_t size) { - RTC_DCHECK(IsConsistent()); - if (!buffer_) { - buffer_ = new RefCountedBuffer(data, size); - offset_ = 0; - size_ = size; - RTC_DCHECK(IsConsistent()); - return; - } - - UnshareAndEnsureCapacity(std::max(capacity(), size_ + size)); - - buffer_->SetSize(offset_ + - size_); // Remove data to the right of the slice. - buffer_->AppendData(data, size); - size_ += size; - - RTC_DCHECK(IsConsistent()); + Append(std::span(reinterpret_cast(data), size)); } template ; + class RTC_EXPORT RawBuffer { + public: + explicit RawBuffer(size_t size); + + std::span data() { return std::span(data_.get(), size_); } + size_t capacity() const { return size_; } + + private: + const size_t size_; + const std::unique_ptr data_; + }; + using RefCountedBuffer = FinalRefCountedObject; + + static scoped_refptr CreateBuffer(size_t capacity); + + std::span AsConstSpan() const { + RTC_DCHECK(IsConsistent()); + if (buffer_ == nullptr) { + return {}; + } + return buffer_->data().subspan(offset_, size_); + } + + void Set(std::span buffer); + void Append(std::span buffer); + // Create a copy of the underlying data if it is referenced from other Buffer // objects or there is not enough capacity. void UnshareAndEnsureCapacity(size_t new_capacity); @@ -299,8 +267,7 @@ class RTC_EXPORT CopyOnWriteBuffer { // Pre- and postcondition of all methods. bool IsConsistent() const { if (buffer_) { - return buffer_->capacity() > 0 && offset_ <= buffer_->size() && - offset_ + size_ <= buffer_->size(); + return buffer_->capacity() > 0 && offset_ + size_ <= buffer_->capacity(); } else { return size_ == 0 && offset_ == 0; } diff --git a/rtc_base/copy_on_write_buffer_unittest.cc b/rtc_base/copy_on_write_buffer_unittest.cc index 59247e5762c..e42d986dcba 100644 --- a/rtc_base/copy_on_write_buffer_unittest.cc +++ b/rtc_base/copy_on_write_buffer_unittest.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include #include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -374,8 +374,8 @@ TEST(CopyOnWriteBufferTest, SlicesAreIndependent) { TEST(CopyOnWriteBufferTest, AcceptsVectorLikeTypes) { std::vector a = {1, 2}; std::vector b = {3, 4}; - ArrayView c(a); - ArrayView d(b); + std::span c(a); + std::span d(b); CopyOnWriteBuffer a_buf(a); CopyOnWriteBuffer b_buf(b); diff --git a/rtc_base/cpu_info.cc b/rtc_base/cpu_info.cc index 32e2eb35b23..f79edd3bea1 100644 --- a/rtc_base/cpu_info.cc +++ b/rtc_base/cpu_info.cc @@ -43,9 +43,11 @@ // Parts of this file derived from Chromium's base/cpu.cc. +namespace webrtc { + namespace { -uint32_t DetectNumberOfCores() { +uint32_t DetectNumberOfCoresHelper() { int number_of_cores = 0; #if defined(WEBRTC_WIN) @@ -121,8 +123,6 @@ inline void __cpuid(int cpu_info[4], int info_type) { } // namespace -namespace webrtc { - namespace cpu_info { uint32_t DetectNumberOfCores() { @@ -130,7 +130,7 @@ uint32_t DetectNumberOfCores() { // is running in a sandbox, we may only be able to read the value once (before // the sandbox is initialized) and not thereafter. // For more information see crbug.com/176522. - static const uint32_t logical_cpus = ::DetectNumberOfCores(); + static const uint32_t logical_cpus = DetectNumberOfCoresHelper(); return logical_cpus; } diff --git a/rtc_base/cpu_time.cc b/rtc_base/cpu_time.cc index 2da8c5e5702..0efadd31d6f 100644 --- a/rtc_base/cpu_time.cc +++ b/rtc_base/cpu_time.cc @@ -36,6 +36,8 @@ #include #endif +namespace webrtc { + #if defined(WEBRTC_WIN) namespace { // FILETIME resolution is 100 nanosecs. @@ -43,8 +45,6 @@ const int64_t kNanosecsPerFiletime = 100; } // namespace #endif -namespace webrtc { - int64_t GetProcessCpuTimeNanos() { #if defined(WEBRTC_FUCHSIA) zx_info_task_runtime_t runtime_info; diff --git a/rtc_base/cpu_time_unittest.cc b/rtc_base/cpu_time_unittest.cc index 8bbc48c5b21..d42abb31224 100644 --- a/rtc_base/cpu_time_unittest.cc +++ b/rtc_base/cpu_time_unittest.cc @@ -26,6 +26,8 @@ #define MAYBE_TEST(test_name) test_name #endif +namespace webrtc { + namespace { constexpr int kAllowedErrorMillisecs = 30; constexpr int kProcessingTimeMillisecs = 500; @@ -34,17 +36,14 @@ constexpr int kWorkingThreads = 2; // Consumes approximately kProcessingTimeMillisecs of CPU time in single thread. void WorkingFunction(int64_t* counter) { *counter = 0; - int64_t stop_cpu_time = - webrtc::GetThreadCpuTimeNanos() + - kProcessingTimeMillisecs * webrtc::kNumNanosecsPerMillisec; - while (webrtc::GetThreadCpuTimeNanos() < stop_cpu_time) { + int64_t stop_cpu_time = GetThreadCpuTimeNanos() + + kProcessingTimeMillisecs * kNumNanosecsPerMillisec; + while (GetThreadCpuTimeNanos() < stop_cpu_time) { (*counter)++; } } } // namespace -namespace webrtc { - // A minimal test which can be run on instrumented builds, so that they're at // least exercising the code to check for memory leaks/etc. TEST(CpuTimeTest, BasicTest) { diff --git a/rtc_base/event_tracer.cc b/rtc_base/event_tracer.cc index be421804a41..dd8e293a069 100644 --- a/rtc_base/event_tracer.cc +++ b/rtc_base/event_tracer.cc @@ -11,23 +11,25 @@ #include "rtc_base/event_tracer.h" #include +#include +#include "api/environment/environment.h" #include "api/units/time_delta.h" #include "rtc_base/trace_event.h" #if defined(RTC_USE_PERFETTO) #include "rtc_base/trace_categories.h" -#include "third_party/perfetto/include/perfetto/tracing/tracing.h" +#include "third_party/perfetto/include/perfetto/tracing/tracing.h" // nogncheck #else #include #include -#include #include #include #include #include "absl/strings/string_view.h" #include "api/sequence_checker.h" +#include "api/units/timestamp.h" #include "rtc_base/checks.h" #include "rtc_base/event.h" #include "rtc_base/logging.h" @@ -35,7 +37,7 @@ #include "rtc_base/platform_thread_types.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" -#include "rtc_base/time_utils.h" +#include "system_wrappers/include/clock.h" #endif namespace webrtc { @@ -92,7 +94,8 @@ void EventTracer::AddTraceEvent(char phase, #if defined(RTC_USE_PERFETTO) // TODO(bugs.webrtc.org/15917): Implement for perfetto. namespace tracing { -void SetupInternalTracer(bool enable_all_categories) {} +void SetupInternalTracer(bool) {} +void SetupInternalTracer(const Environment&, bool) {} bool StartInternalCapture(absl::string_view filename) { return false; } @@ -116,6 +119,9 @@ std::atomic g_event_logging_active(0); // TODO(pbos): Log metadata for all threads, etc. class EventLogger final { public: + explicit EventLogger(std::optional env) + : env_(env), + clock_(env.has_value() ? env->clock() : *Clock::GetRealTimeClock()) {} ~EventLogger() { RTC_DCHECK(thread_checker_.IsCurrent()); } void AddTraceEvent(const char* name, @@ -125,9 +131,9 @@ class EventLogger final { const char** arg_names, const unsigned char* arg_types, const unsigned long long* arg_values, - uint64_t timestamp, int /* pid */, PlatformThreadId thread_id) { + Timestamp now = clock_.CurrentTime(); std::vector args(num_args); for (int i = 0; i < num_args; ++i) { TraceArg& arg = args[i]; @@ -149,7 +155,7 @@ class EventLogger final { .category_enabled = category_enabled, .phase = phase, .args = args, - .timestamp = timestamp, + .timestamp = now, .pid = 1, .tid = thread_id}); } @@ -206,7 +212,7 @@ class EventLogger final { "%s" "}\n", has_logged_event ? "," : " ", e.name, e.category_enabled, - e.phase, e.timestamp, e.pid, e.tid, args_str.c_str()); + e.phase, e.timestamp.us(), e.pid, e.tid, args_str.c_str()); has_logged_event = true; } if (shutting_down) @@ -285,7 +291,7 @@ class EventLogger final { const unsigned char* category_enabled; char phase; std::vector args; - uint64_t timestamp; + Timestamp timestamp; int pid; PlatformThreadId tid; }; @@ -352,6 +358,10 @@ class EventLogger final { Mutex mutex_; std::vector trace_events_ RTC_GUARDED_BY(mutex_); + // TODO(https://issues.webrtc.org/481963632): Make environment non-optional + // and remove `clock_` once an environment is required. + std::optional env_; + Clock& clock_; PlatformThread logging_thread_; Event shutdown_event_; SequenceChecker thread_checker_; @@ -391,16 +401,25 @@ void InternalAddTraceEvent(char phase, return; g_event_logger.load()->AddTraceEvent(name, category_enabled, phase, num_args, - arg_names, arg_types, arg_values, - TimeMicros(), 1, CurrentThreadId()); + arg_names, arg_types, arg_values, 1, + CurrentThreadId()); } } // namespace void SetupInternalTracer(bool enable_all_categories) { EventLogger* null_logger = nullptr; - RTC_CHECK( - g_event_logger.compare_exchange_strong(null_logger, new EventLogger())); + RTC_CHECK(g_event_logger.compare_exchange_strong( + null_logger, new EventLogger(std::nullopt))); + SetupEventTracer(enable_all_categories ? InternalEnableAllCategories + : InternalGetCategoryEnabled, + InternalAddTraceEvent); +} + +void SetupInternalTracer(const Environment& env, bool enable_all_categories) { + EventLogger* null_logger = nullptr; + RTC_CHECK(g_event_logger.compare_exchange_strong(null_logger, + new EventLogger(env))); SetupEventTracer(enable_all_categories ? InternalEnableAllCategories : InternalGetCategoryEnabled, InternalAddTraceEvent); diff --git a/rtc_base/event_tracer.h b/rtc_base/event_tracer.h index a13cdea5c5e..fd3a999a010 100644 --- a/rtc_base/event_tracer.h +++ b/rtc_base/event_tracer.h @@ -29,6 +29,7 @@ #include #include "absl/strings/string_view.h" +#include "api/environment/environment.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -75,7 +76,14 @@ class EventTracer { namespace tracing { // Set up internal event tracer. // TODO(webrtc:15917): Implement for perfetto. +RTC_EXPORT void SetupInternalTracer(const Environment& env, + bool enable_all_categories = true); + +// TODO(https://issues.webrtc.org/481963632): Remove once this is no longer +// used. +// deprecated: use SetupInternalTracer(const Environment&, bool) RTC_EXPORT void SetupInternalTracer(bool enable_all_categories = true); + RTC_EXPORT bool StartInternalCapture(absl::string_view filename); RTC_EXPORT void StartInternalCaptureToFile(FILE* file); RTC_EXPORT void StopInternalCapture(); @@ -85,5 +93,4 @@ RTC_EXPORT void ShutdownInternalTracer(); } // namespace webrtc - #endif // RTC_BASE_EVENT_TRACER_H_ diff --git a/rtc_base/event_tracer_unittest.cc b/rtc_base/event_tracer_unittest.cc index eae19a2588b..dce51d89cbe 100644 --- a/rtc_base/event_tracer_unittest.cc +++ b/rtc_base/event_tracer_unittest.cc @@ -15,22 +15,24 @@ #include "rtc_base/trace_event.h" #include "test/gtest.h" +namespace webrtc { + namespace { class TestStatistics { public: void Reset() { - webrtc::MutexLock lock(&mutex_); + MutexLock lock(&mutex_); events_logged_ = 0; } void Increment() { - webrtc::MutexLock lock(&mutex_); + MutexLock lock(&mutex_); ++events_logged_; } int Count() const { - webrtc::MutexLock lock(&mutex_); + MutexLock lock(&mutex_); return events_logged_; } @@ -41,14 +43,12 @@ class TestStatistics { } private: - mutable webrtc::Mutex mutex_; + mutable Mutex mutex_; int events_logged_ RTC_GUARDED_BY(mutex_) = 0; }; } // namespace -namespace webrtc { - TEST(EventTracerTest, EventTracerDisabled) { { TRACE_EVENT0("webrtc-test", "EventTracerDisabled"); } EXPECT_FALSE(TestStatistics::Get()->Count()); diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn index 13601c302ca..98d3025a047 100644 --- a/rtc_base/experiments/BUILD.gn +++ b/rtc_base/experiments/BUILD.gn @@ -235,7 +235,6 @@ if (rtc_include_tests && !build_with_chromium) { ":quality_scaler_settings", ":quality_scaling_experiment", ":rate_control_settings", - "..:gunit_helpers", "../../api:field_trials", "../../api:field_trials_view", "../../api/units:data_rate", diff --git a/rtc_base/experiments/DEPS b/rtc_base/experiments/DEPS index cb997ad8ab4..855824f071b 100644 --- a/rtc_base/experiments/DEPS +++ b/rtc_base/experiments/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - ".*rate_control_settings.*": [ + "rate_control_settings.*": [ "+video/config", ], } diff --git a/rtc_base/gunit.h b/rtc_base/gunit.h deleted file mode 100644 index cf61fabf1a2..00000000000 --- a/rtc_base/gunit.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef RTC_BASE_GUNIT_H_ -#define RTC_BASE_GUNIT_H_ - -// TODO(bugs.webrtc.org/42226242): remove transitive includes -#include "rtc_base/thread.h" // IWYU pragma: keep -#include "test/gtest.h" // IWYU pragma: keep - -// Wait until "ex" is true, or "timeout" expires, using fake clock where -// messages are processed every millisecond. -// TODO(pthatcher): Allow tests to control how many milliseconds to advance. -#define SIMULATED_WAIT(ex, timeout, clock) \ - for (int64_t wait_start = ::webrtc::TimeMillis(); \ - !(ex) && ::webrtc::TimeMillis() < wait_start + (timeout);) { \ - (clock).AdvanceTime(webrtc::TimeDelta::Millis(1)); \ - } - -#endif // RTC_BASE_GUNIT_H_ diff --git a/rtc_base/logging.cc b/rtc_base/logging.cc index 53faf0eee75..90e7a515f92 100644 --- a/rtc_base/logging.cc +++ b/rtc_base/logging.cc @@ -375,7 +375,9 @@ void LogMessage::OutputToDebug(const LogLineRef& log_line) { #if defined(WEBRTC_WIN) // Always log to the debugger. // Perhaps stderr should be controlled by a preference, as on Mac? +#if RTC_DLOG_IS_ON OutputDebugStringA(msg_str.c_str()); +#endif if (log_to_stderr) { // This handles dynamically allocated consoles, too. if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) { diff --git a/rtc_base/logging_unittest.cc b/rtc_base/logging_unittest.cc index 47c2deea702..217fed9bdfa 100644 --- a/rtc_base/logging_unittest.cc +++ b/rtc_base/logging_unittest.cc @@ -17,9 +17,10 @@ #include #include "absl/strings/string_view.h" +#include "api/units/timestamp.h" #include "rtc_base/checks.h" #include "rtc_base/platform_thread.h" -#include "rtc_base/time_utils.h" +#include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" @@ -309,6 +310,8 @@ TEST(LogTest, CheckTagAddedToStringInDefaultOnLogMessageAndroid) { // Test the time required to write 1000 80-character logs to a string. TEST(LogTest, Perf) { + // This test should probably be turned into a benchmark. + Clock& clock = *Clock::GetRealTimeClock(); std::string str; LogSinkImpl stream(&str); LogMessage::AddLogToStream(&stream, LS_VERBOSE); @@ -325,19 +328,17 @@ TEST(LogTest, Perf) { str.reserve(120000); static const int kRepetitions = 1000; - int64_t start = TimeMillis(), finish; + Timestamp start = clock.CurrentTime(); for (int i = 0; i < kRepetitions; ++i) { LogMessageForTesting(__FILE__, __LINE__, LS_VERBOSE).stream() << message; } - finish = TimeMillis(); + Timestamp finish = clock.CurrentTime(); LogMessage::RemoveLogToStream(&stream); EXPECT_EQ(str.size(), (message.size() + logging_overhead) * kRepetitions); - RTC_LOG(LS_INFO) << "Total log time: " << TimeDiff(finish, start) - << " ms " - " total bytes logged: " - << str.size(); + RTC_LOG(LS_INFO) << "Total log time: " << (finish - start) + << " total bytes logged: " << str.size(); } TEST(LogTest, EnumsAreSupported) { diff --git a/rtc_base/memory/BUILD.gn b/rtc_base/memory/BUILD.gn index 8162e3a665e..2a0ec551a21 100644 --- a/rtc_base/memory/BUILD.gn +++ b/rtc_base/memory/BUILD.gn @@ -36,7 +36,6 @@ rtc_library("fifo_buffer") { "..:checks", "..:macromagic", "..:stream", - "../../api:array_view", "../../api:sequence_checker", "../../api/task_queue", "../../api/task_queue:pending_task_safety_flag", @@ -60,7 +59,7 @@ rtc_library("unittests") { ":fifo_buffer", "..:stream", "..:threading", - "../../api:array_view", + "../../test:run_loop", "../../test:test_support", ] } diff --git a/rtc_base/memory/fifo_buffer.cc b/rtc_base/memory/fifo_buffer.cc index faebc928f32..34f0fef4888 100644 --- a/rtc_base/memory/fifo_buffer.cc +++ b/rtc_base/memory/fifo_buffer.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/sequence_checker.h" #include "api/task_queue/task_queue_base.h" #include "rtc_base/checks.h" @@ -56,7 +56,7 @@ StreamState FifoBuffer::GetState() const { return state_; } -StreamResult FifoBuffer::Read(ArrayView buffer, +StreamResult FifoBuffer::Read(std::span buffer, size_t& bytes_read, int& error) { RTC_DCHECK_RUN_ON(&callback_sequence_); @@ -79,7 +79,7 @@ StreamResult FifoBuffer::Read(ArrayView buffer, return result; } -StreamResult FifoBuffer::Write(ArrayView buffer, +StreamResult FifoBuffer::Write(std::span buffer, size_t& bytes_written, int& error) { RTC_DCHECK_RUN_ON(&callback_sequence_); diff --git a/rtc_base/memory/fifo_buffer.h b/rtc_base/memory/fifo_buffer.h index 70fee304b85..0f0de40939c 100644 --- a/rtc_base/memory/fifo_buffer.h +++ b/rtc_base/memory/fifo_buffer.h @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" @@ -42,10 +42,10 @@ class FifoBuffer final : public StreamInterface { // StreamInterface methods StreamState GetState() const override; - StreamResult Read(ArrayView buffer, + StreamResult Read(std::span buffer, size_t& bytes_read, int& error) override; - StreamResult Write(ArrayView buffer, + StreamResult Write(std::span buffer, size_t& bytes_written, int& error) override; void Close() override; diff --git a/rtc_base/memory/fifo_buffer_unittest.cc b/rtc_base/memory/fifo_buffer_unittest.cc index 22f78a4461d..a5ebd880ba6 100644 --- a/rtc_base/memory/fifo_buffer_unittest.cc +++ b/rtc_base/memory/fifo_buffer_unittest.cc @@ -12,16 +12,16 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/stream.h" -#include "rtc_base/thread.h" #include "test/gtest.h" +#include "test/run_loop.h" namespace webrtc { TEST(FifoBufferTest, TestAll) { - AutoThread main_thread; + test::RunLoop main_thread; const size_t kSize = 16; const uint8_t in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; uint8_t out[kSize * 2]; @@ -33,49 +33,49 @@ TEST(FifoBufferTest, TestAll) { // Test assumptions about base state EXPECT_EQ(SS_OPEN, buf.GetState()); int error; - EXPECT_EQ(SR_BLOCK, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_BLOCK, buf.Read(std::span(out, kSize), bytes, error)); EXPECT_TRUE(nullptr != buf.GetWriteBuffer(&bytes)); EXPECT_EQ(kSize, bytes); buf.ConsumeWriteBuffer(0); // Try a full write - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize), bytes, error)); EXPECT_EQ(kSize, bytes); // Try a write that should block - EXPECT_EQ(SR_BLOCK, buf.Write(MakeArrayView(in, kSize), bytes, error)); + EXPECT_EQ(SR_BLOCK, buf.Write(std::span(in, kSize), bytes, error)); // Try a full read - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize), bytes, error)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); // Try a read that should block - EXPECT_EQ(SR_BLOCK, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_BLOCK, buf.Read(std::span(out, kSize), bytes, error)); // Try a too-big write - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize * 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize * 2), bytes, error)); EXPECT_EQ(bytes, kSize); // Try a too-big read - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize * 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize * 2), bytes, error)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); // Try some small writes and reads - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); @@ -87,23 +87,22 @@ TEST(FifoBufferTest, TestAll) { // XXXXWWWWWWWWXXXX 4567012345670123 // RRRRXXXXXXXXRRRR ....01234567.... // ....RRRRRRRR.... ................ - EXPECT_EQ(SR_SUCCESS, - buf.Write(MakeArrayView(in, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize * 3 / 4), bytes, error)); EXPECT_EQ(kSize * 3 / 4, bytes); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 4), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 4), bytes, error)); EXPECT_EQ(kSize / 4, bytes); EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); @@ -112,16 +111,16 @@ TEST(FifoBufferTest, TestAll) { buf.ConsumeWriteBuffer(0); // Try using GetReadData to do a full read - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize), bytes, error)); q = buf.GetReadData(&bytes); EXPECT_TRUE(nullptr != q); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(q, in, kSize)); buf.ConsumeReadData(kSize); - EXPECT_EQ(SR_BLOCK, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_BLOCK, buf.Read(std::span(out, kSize), bytes, error)); // Try using GetReadData to do some small reads - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize), bytes, error)); q = buf.GetReadData(&bytes); EXPECT_TRUE(nullptr != q); EXPECT_EQ(kSize, bytes); @@ -132,7 +131,7 @@ TEST(FifoBufferTest, TestAll) { EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(q, in + kSize / 2, kSize / 2)); buf.ConsumeReadData(kSize / 2); - EXPECT_EQ(SR_BLOCK, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_BLOCK, buf.Read(std::span(out, kSize), bytes, error)); // Try using GetReadData in a wraparound case // WWWWWWWWWWWWWWWW 0123456789ABCDEF @@ -140,10 +139,9 @@ TEST(FifoBufferTest, TestAll) { // WWWWWWWW....XXXX 01234567....CDEF // ............RRRR 01234567........ // RRRRRRRR........ ................ - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize), bytes, error)); - EXPECT_EQ(SR_SUCCESS, - buf.Read(MakeArrayView(out, kSize * 3 / 4), bytes, error)); - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize / 2), bytes, error)); q = buf.GetReadData(&bytes); EXPECT_TRUE(nullptr != q); EXPECT_EQ(kSize / 4, bytes); @@ -165,7 +163,7 @@ TEST(FifoBufferTest, TestAll) { EXPECT_EQ(kSize, bytes); memcpy(p, in, kSize); buf.ConsumeWriteBuffer(kSize); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize), bytes, error)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); @@ -180,7 +178,7 @@ TEST(FifoBufferTest, TestAll) { EXPECT_EQ(kSize / 2, bytes); memcpy(p, in + kSize / 2, kSize / 2); buf.ConsumeWriteBuffer(kSize / 2); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize), bytes, error)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); @@ -190,9 +188,8 @@ TEST(FifoBufferTest, TestAll) { // ........XXXXWWWW ........89AB0123 // WWWW....XXXXXXXX 4567....89AB0123 // RRRR....RRRRRRRR ................ - EXPECT_EQ(SR_SUCCESS, - buf.Write(MakeArrayView(in, kSize * 3 / 4), bytes, error)); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); p = buf.GetWriteBuffer(&bytes); EXPECT_TRUE(nullptr != p); EXPECT_EQ(kSize / 4, bytes); @@ -203,27 +200,26 @@ TEST(FifoBufferTest, TestAll) { EXPECT_EQ(kSize / 2, bytes); memcpy(p, in + kSize / 4, kSize / 4); buf.ConsumeWriteBuffer(kSize / 4); - EXPECT_EQ(SR_SUCCESS, - buf.Read(MakeArrayView(out, kSize * 3 / 4), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize * 3 / 4), bytes, error)); EXPECT_EQ(kSize * 3 / 4, bytes); EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); EXPECT_EQ(0, memcmp(in, out + kSize / 4, kSize / 4)); // Check that the stream is now empty - EXPECT_EQ(SR_BLOCK, buf.Read(MakeArrayView(out, kSize), bytes, error)); + EXPECT_EQ(SR_BLOCK, buf.Read(std::span(out, kSize), bytes, error)); // Write to the stream, close it, read the remaining bytes - EXPECT_EQ(SR_SUCCESS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Write(std::span(in, kSize / 2), bytes, error)); buf.Close(); EXPECT_EQ(SS_CLOSED, buf.GetState()); - EXPECT_EQ(SR_EOS, buf.Write(MakeArrayView(in, kSize / 2), bytes, error)); - EXPECT_EQ(SR_SUCCESS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_EOS, buf.Write(std::span(in, kSize / 2), bytes, error)); + EXPECT_EQ(SR_SUCCESS, buf.Read(std::span(out, kSize / 2), bytes, error)); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); - EXPECT_EQ(SR_EOS, buf.Read(MakeArrayView(out, kSize / 2), bytes, error)); + EXPECT_EQ(SR_EOS, buf.Read(std::span(out, kSize / 2), bytes, error)); } TEST(FifoBufferTest, FullBufferCheck) { - AutoThread main_thread; + test::RunLoop main_thread; FifoBuffer buff(10); buff.ConsumeWriteBuffer(10); diff --git a/rtc_base/memory_stream.cc b/rtc_base/memory_stream.cc index 9134d610e4b..7994449e64e 100644 --- a/rtc_base/memory_stream.cc +++ b/rtc_base/memory_stream.cc @@ -14,8 +14,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/stream.h" @@ -25,7 +25,7 @@ StreamState MemoryStream::GetState() const { return SS_OPEN; } -StreamResult MemoryStream::Read(ArrayView buffer, +StreamResult MemoryStream::Read(std::span buffer, size_t& bytes_read, int& error) { if (seek_position_ >= data_length_) { @@ -45,7 +45,7 @@ StreamResult MemoryStream::Read(ArrayView buffer, return SR_SUCCESS; } -StreamResult MemoryStream::Write(ArrayView buffer, +StreamResult MemoryStream::Write(std::span buffer, size_t& bytes_written, int& error) { size_t available = buffer_length_ - seek_position_; diff --git a/rtc_base/memory_stream.h b/rtc_base/memory_stream.h index 312bde67038..13dd396d344 100644 --- a/rtc_base/memory_stream.h +++ b/rtc_base/memory_stream.h @@ -14,8 +14,8 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/stream.h" namespace webrtc { @@ -28,10 +28,10 @@ class MemoryStream final : public StreamInterface { ~MemoryStream() override; StreamState GetState() const override; - StreamResult Read(ArrayView buffer, + StreamResult Read(std::span buffer, size_t& bytes_read, int& error) override; - StreamResult Write(ArrayView buffer, + StreamResult Write(std::span buffer, size_t& bytes_written, int& error) override; void Close() override; diff --git a/rtc_base/net_helper.cc b/rtc_base/net_helper.cc index 38138df36f9..aa81de0ac06 100644 --- a/rtc_base/net_helper.cc +++ b/rtc_base/net_helper.cc @@ -18,6 +18,7 @@ namespace webrtc { const char UDP_PROTOCOL_NAME[] = "udp"; +const char DTLS_PROTOCOL_NAME[] = "dtls"; const char TCP_PROTOCOL_NAME[] = "tcp"; const char SSLTCP_PROTOCOL_NAME[] = "ssltcp"; const char TLS_PROTOCOL_NAME[] = "tls"; @@ -28,7 +29,8 @@ int GetProtocolOverhead(absl::string_view protocol) { } else if (protocol == UDP_PROTOCOL_NAME) { return kUdpHeaderSize; } else { - // TODO(srte): We should crash on unexpected input and handle TLS correctly. + // TODO(srte): We should crash on unexpected input and handle DTLS and TLS + // correctly. return 8; } } @@ -37,6 +39,8 @@ absl::string_view ProtoToString(ProtocolType proto) { switch (proto) { case PROTO_UDP: return UDP_PROTOCOL_NAME; + case PROTO_DTLS: + return DTLS_PROTOCOL_NAME; case PROTO_TCP: return TCP_PROTOCOL_NAME; case PROTO_SSLTCP: diff --git a/rtc_base/net_helper.h b/rtc_base/net_helper.h index aa49f2ebb7e..b9adf86e9d6 100644 --- a/rtc_base/net_helper.h +++ b/rtc_base/net_helper.h @@ -21,6 +21,7 @@ namespace webrtc { enum ProtocolType { PROTO_UDP, + PROTO_DTLS, PROTO_TCP, PROTO_SSLTCP, // Pseudo-TLS. PROTO_TLS, @@ -28,6 +29,7 @@ enum ProtocolType { }; RTC_EXPORT extern const char UDP_PROTOCOL_NAME[]; +extern const char DTLS_PROTOCOL_NAME[]; RTC_EXPORT extern const char TCP_PROTOCOL_NAME[]; extern const char SSLTCP_PROTOCOL_NAME[]; extern const char TLS_PROTOCOL_NAME[]; diff --git a/rtc_base/network.cc b/rtc_base/network.cc index 01e0cb84605..fd1e3e739ce 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -16,7 +16,9 @@ #include #include #include +#include #include +#include #include #include @@ -25,7 +27,6 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" #include "api/sequence_checker.h" @@ -111,10 +112,15 @@ bool SortNetworks(const Network* a, const Network* b) { uint16_t ComputeNetworkCostByType(int type, bool is_vpn, bool use_differentiated_cellular_costs, - bool add_network_cost_to_vpn) { - // TODO(jonaso) : Rollout support for cellular network cost using A/B - // experiment to make sure it does not introduce regressions. + bool add_network_cost_to_vpn, + NetworkSlice network_slice, + bool add_network_cost_for_slice) { int vpnCost = (is_vpn && add_network_cost_to_vpn) ? kNetworkCostVpn : 0; + uint16_t slice_cost = + (add_network_cost_for_slice && network_slice != NetworkSlice::NO_SLICE) + ? kNetworkCostSlice + : 0; + switch (type) { case ADAPTER_TYPE_ETHERNET: case ADAPTER_TYPE_LOOPBACK: @@ -122,7 +128,7 @@ uint16_t ComputeNetworkCostByType(int type, case ADAPTER_TYPE_WIFI: return kNetworkCostLow + vpnCost; case ADAPTER_TYPE_CELLULAR: - return kNetworkCostCellular + vpnCost; + return kNetworkCostCellular + vpnCost + slice_cost; case ADAPTER_TYPE_CELLULAR_2G: return (use_differentiated_cellular_costs ? kNetworkCostCellular2G : kNetworkCostCellular) + @@ -138,7 +144,7 @@ uint16_t ComputeNetworkCostByType(int type, case ADAPTER_TYPE_CELLULAR_5G: return (use_differentiated_cellular_costs ? kNetworkCostCellular5G : kNetworkCostCellular) + - vpnCost; + vpnCost + slice_cost; case ADAPTER_TYPE_ANY: // Candidates gathered from the any-address/wildcard ports, as backups, // are given the maximum cost so that if there are other candidates with @@ -561,7 +567,7 @@ Network* NetworkManagerBase::GetNetworkFromAddress(const IPAddress& ip) const { return nullptr; } -bool NetworkManagerBase::IsVpnMacAddress(ArrayView address) { +bool NetworkManagerBase::IsVpnMacAddress(std::span address) { if (address.data() == nullptr && address.empty()) { return false; } @@ -904,7 +910,7 @@ bool BasicNetworkManager::CreateNetworks( adapter_type = ADAPTER_TYPE_VPN; } if (adapter_type != ADAPTER_TYPE_VPN && - IsVpnMacAddress(ArrayView( + IsVpnMacAddress(std::span( reinterpret_cast( adapter_addrs->PhysicalAddress), adapter_addrs->PhysicalAddressLength))) { @@ -1244,54 +1250,68 @@ uint16_t Network::GetCost(const FieldTrialsView& field_trials) const { field_trials.IsEnabled("WebRTC-UseDifferentiatedCellularCosts"); const bool add_network_cost_to_vpn = field_trials.IsEnabled("WebRTC-AddNetworkCostToVpn"); - return ComputeNetworkCostByType(type, IsVpn(), - use_differentiated_cellular_costs, - add_network_cost_to_vpn); + const bool add_network_cost_for_slice = + field_trials.IsEnabled("WebRTC-UnifiedCommunications"); + return ComputeNetworkCostByType( + type, IsVpn(), use_differentiated_cellular_costs, add_network_cost_to_vpn, + network_slice(), add_network_cost_for_slice); } // This is the inverse of ComputeNetworkCostByType(). -std::pair Network::GuessAdapterFromNetworkCost( - int network_cost) { +std::tuple +Network::GuessAdapterFromNetworkCost(int network_cost) { switch (network_cost) { case kNetworkCostMin: - return {ADAPTER_TYPE_ETHERNET, false}; + return {ADAPTER_TYPE_ETHERNET, false, NetworkSlice::NO_SLICE}; case kNetworkCostMin + kNetworkCostVpn: - return {ADAPTER_TYPE_ETHERNET, true}; + return {ADAPTER_TYPE_ETHERNET, true, NetworkSlice::NO_SLICE}; case kNetworkCostLow: - return {ADAPTER_TYPE_WIFI, false}; + return {ADAPTER_TYPE_WIFI, false, NetworkSlice::NO_SLICE}; case kNetworkCostLow + kNetworkCostVpn: - return {ADAPTER_TYPE_WIFI, true}; + return {ADAPTER_TYPE_WIFI, true, NetworkSlice::NO_SLICE}; case kNetworkCostCellular: - return {ADAPTER_TYPE_CELLULAR, false}; + return {ADAPTER_TYPE_CELLULAR, false, NetworkSlice::NO_SLICE}; case kNetworkCostCellular + kNetworkCostVpn: - return {ADAPTER_TYPE_CELLULAR, true}; + return {ADAPTER_TYPE_CELLULAR, true, NetworkSlice::NO_SLICE}; + case kNetworkCostCellularSlice: + return {ADAPTER_TYPE_CELLULAR, false, + NetworkSlice::UNIFIED_COMMUNICATIONS}; + case kNetworkCostCellularVpnSlice: + return {ADAPTER_TYPE_CELLULAR, true, + NetworkSlice::UNIFIED_COMMUNICATIONS}; case kNetworkCostCellular2G: - return {ADAPTER_TYPE_CELLULAR_2G, false}; + return {ADAPTER_TYPE_CELLULAR_2G, false, NetworkSlice::NO_SLICE}; case kNetworkCostCellular2G + kNetworkCostVpn: - return {ADAPTER_TYPE_CELLULAR_2G, true}; + return {ADAPTER_TYPE_CELLULAR_2G, true, NetworkSlice::NO_SLICE}; case kNetworkCostCellular3G: - return {ADAPTER_TYPE_CELLULAR_3G, false}; + return {ADAPTER_TYPE_CELLULAR_3G, false, NetworkSlice::NO_SLICE}; case kNetworkCostCellular3G + kNetworkCostVpn: - return {ADAPTER_TYPE_CELLULAR_3G, true}; + return {ADAPTER_TYPE_CELLULAR_3G, true, NetworkSlice::NO_SLICE}; case kNetworkCostCellular4G: - return {ADAPTER_TYPE_CELLULAR_4G, false}; + return {ADAPTER_TYPE_CELLULAR_4G, false, NetworkSlice::NO_SLICE}; case kNetworkCostCellular4G + kNetworkCostVpn: - return {ADAPTER_TYPE_CELLULAR_4G, true}; + return {ADAPTER_TYPE_CELLULAR_4G, true, NetworkSlice::NO_SLICE}; case kNetworkCostCellular5G: - return {ADAPTER_TYPE_CELLULAR_5G, false}; + return {ADAPTER_TYPE_CELLULAR_5G, false, NetworkSlice::NO_SLICE}; case kNetworkCostCellular5G + kNetworkCostVpn: - return {ADAPTER_TYPE_CELLULAR_5G, true}; + return {ADAPTER_TYPE_CELLULAR_5G, true, NetworkSlice::NO_SLICE}; + case kNetworkCostCellular5GSlice: + return {ADAPTER_TYPE_CELLULAR_5G, false, + NetworkSlice::UNIFIED_COMMUNICATIONS}; + case kNetworkCostCellular5GVpnSlice: + return {ADAPTER_TYPE_CELLULAR_5G, true, + NetworkSlice::UNIFIED_COMMUNICATIONS}; case kNetworkCostUnknown: - return {ADAPTER_TYPE_UNKNOWN, false}; + return {ADAPTER_TYPE_UNKNOWN, false, NetworkSlice::NO_SLICE}; case kNetworkCostUnknown + kNetworkCostVpn: - return {ADAPTER_TYPE_UNKNOWN, true}; + return {ADAPTER_TYPE_UNKNOWN, true, NetworkSlice::NO_SLICE}; case kNetworkCostMax: - return {ADAPTER_TYPE_ANY, false}; + return {ADAPTER_TYPE_ANY, false, NetworkSlice::NO_SLICE}; case kNetworkCostMax + kNetworkCostVpn: - return {ADAPTER_TYPE_ANY, true}; + return {ADAPTER_TYPE_ANY, true, NetworkSlice::NO_SLICE}; } RTC_LOG(LS_VERBOSE) << "Unknown network cost: " << network_cost; - return {ADAPTER_TYPE_UNKNOWN, false}; + return {ADAPTER_TYPE_UNKNOWN, false, NetworkSlice::NO_SLICE}; } std::string Network::ToString() const { @@ -1304,6 +1324,9 @@ std::string Network::ToString() const { if (IsVpn()) { ss << "/" << AdapterTypeToString(underlying_type_for_vpn_); } + if (network_slice() != NetworkSlice::NO_SLICE) { + ss << "/" << NetworkSliceToString(network_slice()); + } ss << ":id=" << id_ << "]"; return ss.Release(); } diff --git a/rtc_base/network.h b/rtc_base/network.h index fcf4cd6d845..4a6bdd0a3bd 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -15,14 +15,15 @@ #include #include +#include #include +#include #include #include #include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/field_trials_view.h" #include "api/scoped_refptr.h" @@ -426,8 +427,8 @@ class RTC_EXPORT Network { NetworkSlice network_slice() const { return network_slice_; } void set_network_slice(NetworkSlice slice) { network_slice_ = slice; } - static std::pair GuessAdapterFromNetworkCost( - int network_cost); + static std::tuple + GuessAdapterFromNetworkCost(int network_cost); // Debugging description of this network std::string ToString() const; @@ -469,7 +470,7 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { // Check if MAC address in |bytes| is one of the pre-defined // MAC addresses for know VPNs. - static bool IsVpnMacAddress(ArrayView address); + static bool IsVpnMacAddress(std::span address); protected: // Updates `networks_` with the networks listed in `list`. If @@ -625,5 +626,4 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, } // namespace webrtc - #endif // RTC_BASE_NETWORK_H_ diff --git a/rtc_base/network/BUILD.gn b/rtc_base/network/BUILD.gn index 3801b732495..d0705cbec00 100644 --- a/rtc_base/network/BUILD.gn +++ b/rtc_base/network/BUILD.gn @@ -26,7 +26,6 @@ rtc_library("received_packet") { deps = [ "..:checks", "..:socket_address", - "../../api:array_view", "../../api/transport:ecn_marking", "../../api/units:timestamp", "../system:rtc_export", diff --git a/rtc_base/network/received_packet.cc b/rtc_base/network/received_packet.cc index 3f08e2d099d..1677b6b0cfd 100644 --- a/rtc_base/network/received_packet.cc +++ b/rtc_base/network/received_packet.cc @@ -13,9 +13,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/transport/ecn_marking.h" #include "api/units/timestamp.h" #include "rtc_base/checks.h" @@ -23,7 +23,7 @@ namespace webrtc { -ReceivedIpPacket::ReceivedIpPacket(ArrayView payload, +ReceivedIpPacket::ReceivedIpPacket(std::span payload, const SocketAddress& source_address, std::optional arrival_time, EcnMarking ecn, @@ -48,7 +48,7 @@ ReceivedIpPacket ReceivedIpPacket::CreateFromLegacy( const SocketAddress& source_address) { RTC_DCHECK(packet_time_us == -1 || packet_time_us >= 0); return ReceivedIpPacket( - MakeArrayView(data, size), source_address, + std::span(data, size), source_address, (packet_time_us >= 0) ? std::optional(Timestamp::Micros(packet_time_us)) : std::nullopt); diff --git a/rtc_base/network/received_packet.h b/rtc_base/network/received_packet.h index 1078f807a61..2868047fa41 100644 --- a/rtc_base/network/received_packet.h +++ b/rtc_base/network/received_packet.h @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "api/transport/ecn_marking.h" #include "api/units/timestamp.h" #include "rtc_base/socket_address.h" @@ -37,7 +37,7 @@ class RTC_EXPORT ReceivedIpPacket { // Caller must keep memory pointed to by payload and address valid for the // lifetime of this ReceivedPacket. - ReceivedIpPacket(ArrayView payload, + ReceivedIpPacket(std::span payload, const SocketAddress& source_address, std::optional arrival_time = std::nullopt, EcnMarking ecn = EcnMarking::kNotEct, @@ -47,7 +47,7 @@ class RTC_EXPORT ReceivedIpPacket { // Address/port of the packet sender. const SocketAddress& source_address() const { return source_address_; } - ArrayView payload() const { return payload_; } + std::span payload() const { return payload_; } // Timestamp when this packet was received. Not available on all socket // implementations. @@ -74,7 +74,7 @@ class RTC_EXPORT ReceivedIpPacket { const SocketAddress& = SocketAddress()); private: - ArrayView payload_; + std::span payload_; std::optional arrival_time_; const SocketAddress& source_address_; EcnMarking ecn_; diff --git a/rtc_base/network/sent_packet.h b/rtc_base/network/sent_packet.h index 8b89845b160..edd5ed54a49 100644 --- a/rtc_base/network/sent_packet.h +++ b/rtc_base/network/sent_packet.h @@ -32,6 +32,7 @@ enum class PacketType { enum class PacketInfoProtocolType { kUnknown, kUdp, + kDtls, kTcp, kSsltcp, kTls, diff --git a/rtc_base/network_constants.h b/rtc_base/network_constants.h index b074c2212c5..8c43576c614 100644 --- a/rtc_base/network_constants.h +++ b/rtc_base/network_constants.h @@ -32,6 +32,24 @@ constexpr uint16_t kNetworkCostMin = 0; // everything else being equal. constexpr uint16_t kNetworkCostVpn = 1; +// TODO: bugs.webrtc.org/466507512 - network slices are not currently +// differentiated so as to reduce the possibility of collisions amongst network +// costs. Differentiation may require a more extensible approach. + +// Add -2 to the network cost when using network slicing so that e.g. premium 5G +// slice is preferred over regular 5G, everything else being equal. +constexpr uint16_t kNetworkCostSlice = -2; + +// Aliases for correct wrap-around with network slice cost. +constexpr uint16_t kNetworkCostCellular5GSlice = // 248 + kNetworkCostCellular5G + kNetworkCostSlice; +constexpr uint16_t kNetworkCostCellular5GVpnSlice = // 249 + kNetworkCostCellular5G + kNetworkCostVpn + kNetworkCostSlice; +constexpr uint16_t kNetworkCostCellularSlice = // 898 + kNetworkCostCellular + kNetworkCostSlice; +constexpr uint16_t kNetworkCostCellularVpnSlice = // 899 + kNetworkCostCellular + kNetworkCostVpn + kNetworkCostSlice; + // alias constexpr uint16_t kNetworkCostHigh = kNetworkCostCellular; diff --git a/rtc_base/network_monitor.cc b/rtc_base/network_monitor.cc index 0733d8eea6f..2595b68a919 100644 --- a/rtc_base/network_monitor.cc +++ b/rtc_base/network_monitor.cc @@ -10,18 +10,26 @@ #include "rtc_base/network_monitor.h" -#include "rtc_base/checks.h" +#include "absl/strings/string_view.h" namespace webrtc { -const char* NetworkPreferenceToString(NetworkPreference preference) { +absl::string_view NetworkPreferenceToString(NetworkPreference preference) { switch (preference) { case NetworkPreference::NEUTRAL: return "NEUTRAL"; case NetworkPreference::NOT_PREFERRED: return "NOT_PREFERRED"; } - RTC_CHECK_NOTREACHED(); +} + +absl::string_view NetworkSliceToString(NetworkSlice network_slice) { + switch (network_slice) { + case NetworkSlice::NO_SLICE: + return "NO_SLICE"; + case NetworkSlice::UNIFIED_COMMUNICATIONS: + return "UNIFIED_COMMUNICATIONS"; + } } NetworkMonitorInterface::NetworkMonitorInterface() {} diff --git a/rtc_base/network_monitor.h b/rtc_base/network_monitor.h index 9d140707629..10889afdf71 100644 --- a/rtc_base/network_monitor.h +++ b/rtc_base/network_monitor.h @@ -46,7 +46,8 @@ enum class NetworkSlice { UNIFIED_COMMUNICATIONS = 1, }; -const char* NetworkPreferenceToString(NetworkPreference preference); +absl::string_view NetworkPreferenceToString(NetworkPreference preference); +absl::string_view NetworkSliceToString(NetworkSlice network_slice); // This interface is set onto a socket server, // where only the ip address is known at the time of binding. diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc index e53565f744d..c93de941e82 100644 --- a/rtc_base/network_unittest.cc +++ b/rtc_base/network_unittest.cc @@ -14,14 +14,15 @@ #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" #include "absl/strings/match.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" #include "api/field_trials.h" @@ -37,10 +38,10 @@ #include "rtc_base/network_monitor_factory.h" #include "rtc_base/physical_socket_server.h" #include "rtc_base/socket_address.h" -#include "rtc_base/thread.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" // IWYU pragma: begin_keep @@ -331,7 +332,7 @@ class NetworkTest : public ::testing::Test { protected: const FieldTrials field_trials_ = CreateTestFieldTrials(); const Environment env_ = CreateEnvironment(&field_trials_); - AutoThread main_thread_; + test::RunLoop main_thread_; bool callback_called_ = false; }; @@ -429,14 +430,14 @@ TEST_F(NetworkTest, TestUpdateNetworks) { EXPECT_EQ(NetworkManager::ENUMERATION_ALLOWED, manager.enumeration_permission()); manager.StartUpdating(); - Thread::Current()->ProcessMessages(0); + main_thread_.Flush(); EXPECT_TRUE(callback_called_); callback_called_ = false; // Callback should be triggered immediately when StartUpdating // is called, after network update signal is already sent. manager.StartUpdating(); EXPECT_TRUE(manager.started()); - Thread::Current()->ProcessMessages(0); + main_thread_.Flush(); EXPECT_TRUE(callback_called_); manager.StopUpdating(); EXPECT_TRUE(manager.started()); @@ -450,7 +451,7 @@ TEST_F(NetworkTest, TestUpdateNetworks) { // Callback should be triggered immediately after StartUpdating is called // when start_count_ is reset to 0. manager.StartUpdating(); - Thread::Current()->ProcessMessages(0); + main_thread_.Flush(); EXPECT_TRUE(callback_called_); } @@ -1510,10 +1511,89 @@ TEST_F(NetworkTest, NetworkCostVpn_VpnMoreExpensive) { delete net2; } -TEST_F(NetworkTest, GuessAdapterFromNetworkCost) { - FieldTrials field_trials = CreateTestFieldTrials( - "WebRTC-AddNetworkCostToVpn/Enabled/" - "WebRTC-UseDifferentiatedCellularCosts/Enabled/"); +class NetworkTestWithDifferentiatedCellular + : public NetworkTest, + public testing::WithParamInterface { + protected: + FieldTrials CreateFieldTrials(absl::string_view s = "") { + return CreateTestFieldTrials(absl::StrCat( + "WebRTC-UseDifferentiatedCellularCosts/", GetParam(), "/", s)); + } +}; + +INSTANTIATE_TEST_SUITE_P(NetworkTestWithDifferentiatedCellular, + NetworkTestWithDifferentiatedCellular, + testing::Values("Disabled", "Enabled")); + +TEST_P(NetworkTestWithDifferentiatedCellular, NetworkCostSlice_Default) { + FieldTrials field_trials = CreateFieldTrials(); + + IPAddress ip1; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + + Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64, + ADAPTER_TYPE_CELLULAR_5G); + net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS); + + Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64, + ADAPTER_TYPE_CELLULAR_5G); + + EXPECT_EQ(net1.GetCost(field_trials), net2.GetCost(field_trials)); +} + +TEST_P(NetworkTestWithDifferentiatedCellular, NetworkCostSlice_Disabled) { + FieldTrials field_trials = + CreateFieldTrials("WebRTC-UnifiedCommunications/Disabled/"); + + IPAddress ip1; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + + Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64, + ADAPTER_TYPE_CELLULAR_5G); + net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS); + + Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64, + ADAPTER_TYPE_CELLULAR_5G); + + EXPECT_EQ(net1.GetCost(field_trials), net2.GetCost(field_trials)); +} + +TEST_P(NetworkTestWithDifferentiatedCellular, + NetworkCostSlice_SliceLessExpensive) { + FieldTrials field_trials = + CreateFieldTrials("WebRTC-UnifiedCommunications/Enabled/"); + + IPAddress ip1; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + + Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64, + ADAPTER_TYPE_CELLULAR_5G); + net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS); + + Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64, + ADAPTER_TYPE_CELLULAR_5G); + + EXPECT_LT(net1.GetCost(field_trials), net2.GetCost(field_trials)); +} + +TEST_F(NetworkTest, NetworkCostSlice_IgnoredWhenInapplicable) { + FieldTrials field_trials = + CreateTestFieldTrials("WebRTC-UnifiedCommunications/Enabled/"); + + IPAddress ip1; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + + Network net1("wlan", "wlan", TruncateIP(ip1, 64), 64, ADAPTER_TYPE_WIFI); + net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS); + + Network net2("wlan", "wlan", TruncateIP(ip1, 64), 64, ADAPTER_TYPE_WIFI); + + EXPECT_EQ(net1.GetCost(field_trials), net2.GetCost(field_trials)); +} + +TEST_F(NetworkTest, GuessAdapterFromNetworkCost_Default) { + FieldTrials field_trials = + CreateTestFieldTrials("WebRTC-UseDifferentiatedCellularCosts/Enabled/"); IPAddress ip1; EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); @@ -1523,31 +1603,92 @@ TEST_F(NetworkTest, GuessAdapterFromNetworkCost) { continue; Network net1("em1", "em1", TruncateIP(ip1, 64), 64); net1.set_type(type); - auto [guess, vpn] = + + auto [guess, vpn, network_slice] = Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials)); + EXPECT_FALSE(vpn); if (type == ADAPTER_TYPE_LOOPBACK) { EXPECT_EQ(guess, ADAPTER_TYPE_ETHERNET); } else { EXPECT_EQ(type, guess); } + EXPECT_EQ(NetworkSlice::NO_SLICE, network_slice); } +} + +TEST_F(NetworkTest, GuessAdapterFromNetworkCost_Vpn) { + FieldTrials field_trials = CreateTestFieldTrials( + "WebRTC-AddNetworkCostToVpn/Enabled/" + "WebRTC-UseDifferentiatedCellularCosts/Enabled/"); + + IPAddress ip1; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); - // VPN for (auto type : kAllAdapterTypes) { if (type == ADAPTER_TYPE_VPN) continue; Network net1("em1", "em1", TruncateIP(ip1, 64), 64); net1.set_type(ADAPTER_TYPE_VPN); net1.set_underlying_type_for_vpn(type); - auto [guess, vpn] = + + auto [guess, vpn, network_slice] = Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials)); + EXPECT_TRUE(vpn); if (type == ADAPTER_TYPE_LOOPBACK) { EXPECT_EQ(guess, ADAPTER_TYPE_ETHERNET); } else { EXPECT_EQ(type, guess); } + EXPECT_EQ(NetworkSlice::NO_SLICE, network_slice); + } +} + +TEST_P(NetworkTestWithDifferentiatedCellular, + GuessAdapterFromNetworkCost_Slice) { + FieldTrials field_trials = CreateFieldTrials( + "WebRTC-AddNetworkCostToVpn/Enabled/" + "WebRTC-UnifiedCommunications/Enabled/"); + + bool differentiated_cellular_enabled = GetParam() == "Enabled"; + + IPAddress ip1; + EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + + constexpr AdapterType sliceable_cellular_types[] = {ADAPTER_TYPE_CELLULAR_5G, + ADAPTER_TYPE_CELLULAR}; + + for (AdapterType actual_type : sliceable_cellular_types) { + AdapterType expected_guess = + differentiated_cellular_enabled ? actual_type : ADAPTER_TYPE_CELLULAR; + + { + Network net1("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64); + net1.set_type(actual_type); + net1.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS); + + auto [guess, vpn, network_slice] = + Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials)); + + EXPECT_EQ(expected_guess, guess); + EXPECT_FALSE(vpn); + EXPECT_EQ(NetworkSlice::UNIFIED_COMMUNICATIONS, network_slice); + } + + { + Network net2("rmnet1", "rmnet1", TruncateIP(ip1, 64), 64); + net2.set_type(ADAPTER_TYPE_VPN); + net2.set_underlying_type_for_vpn(actual_type); + net2.set_network_slice(NetworkSlice::UNIFIED_COMMUNICATIONS); + + auto [guess, vpn, network_slice] = + Network::GuessAdapterFromNetworkCost(net2.GetCost(field_trials)); + + EXPECT_EQ(expected_guess, guess); + EXPECT_TRUE(vpn); + EXPECT_EQ(NetworkSlice::UNIFIED_COMMUNICATIONS, network_slice); + } } } @@ -1604,10 +1745,10 @@ TEST_F(NetworkTest, HardcodedVpn) { EXPECT_TRUE(NetworkManagerBase::IsVpnMacAddress(global)); EXPECT_FALSE( - NetworkManagerBase::IsVpnMacAddress(ArrayView(cisco, 5))); + NetworkManagerBase::IsVpnMacAddress(std::span(cisco, 5))); EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(five_bytes)); EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(unknown)); - EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(nullptr)); + EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress({})); } TEST(CompareNetworks, IrreflexivityTest) { diff --git a/rtc_base/null_socket_server_unittest.cc b/rtc_base/null_socket_server_unittest.cc index 3b47acc5df9..bd73efd3d8f 100644 --- a/rtc_base/null_socket_server_unittest.cc +++ b/rtc_base/null_socket_server_unittest.cc @@ -10,41 +10,44 @@ #include "rtc_base/null_socket_server.h" -#include #include -#include "api/test/rtc_error_matchers.h" +#include "api/environment/environment.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/socket_server.h" #include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" -#include "test/gmock.h" +#include "test/create_test_environment.h" #include "test/gtest.h" -#include "test/wait_until.h" +#include "test/run_loop.h" namespace webrtc { TEST(NullSocketServerTest, WaitAndSet) { - AutoThread main_thread; + test::RunLoop run_loop; NullSocketServer ss; auto thread = Thread::Create(); EXPECT_TRUE(thread->Start()); thread->PostTask([&ss] { ss.WakeUp(); }); // The process_io will be ignored. const bool process_io = true; - EXPECT_THAT( - WaitUntil([&] { return ss.Wait(SocketServer::kForever, process_io); }, - ::testing::IsTrue(), {.timeout = TimeDelta::Millis(5'000)}), - IsRtcOk()); + bool wait_result = false; + run_loop.PostTask([&] { + wait_result = ss.Wait(SocketServer::kForever, process_io); + run_loop.Quit(); + }); + run_loop.RunFor(TimeDelta::Seconds(5)); + EXPECT_TRUE(wait_result); } TEST(NullSocketServerTest, TestWait) { + Environment env = CreateTestEnvironment(); NullSocketServer ss; - int64_t start = TimeMillis(); + Timestamp start = env.clock().CurrentTime(); ss.Wait(TimeDelta::Millis(200), true); // The actual wait time is dependent on the resolution of the timer used by // the Event class. Allow for the event to signal ~20ms early. - EXPECT_GE(TimeSince(start), 180); + EXPECT_GE(env.clock().CurrentTime() - start, TimeDelta::Millis(180)); } } // namespace webrtc diff --git a/rtc_base/numerics/event_based_exponential_moving_average.cc b/rtc_base/numerics/event_based_exponential_moving_average.cc index 419902dc52b..072f1a1aed7 100644 --- a/rtc_base/numerics/event_based_exponential_moving_average.cc +++ b/rtc_base/numerics/event_based_exponential_moving_average.cc @@ -17,6 +17,8 @@ #include "rtc_base/checks.h" +namespace webrtc { + namespace { // For a normal distributed value, the 95% double sided confidence interval is @@ -25,8 +27,6 @@ constexpr double ninetyfive_percent_confidence = 1.96; } // namespace -namespace webrtc { - // `half_time` specifies how much weight will be given to old samples, // a sample gets exponentially less weight so that it's 50% // after `half_time` time units has passed. diff --git a/rtc_base/numerics/event_based_exponential_moving_average_unittest.cc b/rtc_base/numerics/event_based_exponential_moving_average_unittest.cc index 14ef9cb5e77..fcd7b733ea3 100644 --- a/rtc_base/numerics/event_based_exponential_moving_average_unittest.cc +++ b/rtc_base/numerics/event_based_exponential_moving_average_unittest.cc @@ -16,6 +16,8 @@ #include "test/gtest.h" +namespace webrtc { + namespace { constexpr int kHalfTime = 500; @@ -23,8 +25,6 @@ constexpr double kError = 0.1; } // namespace -namespace webrtc { - TEST(EventBasedExponentialMovingAverageTest, NoValue) { EventBasedExponentialMovingAverage average(kHalfTime); diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc index 1f5cdb7847d..dfbc46adbe1 100644 --- a/rtc_base/openssl_adapter.cc +++ b/rtc_base/openssl_adapter.cc @@ -196,7 +196,8 @@ bool OpenSSLAdapter::CleanupSSL() { OpenSSLAdapter::OpenSSLAdapter(Socket* socket, OpenSSLSessionCache* ssl_session_cache, - SSLCertificateVerifier* ssl_cert_verifier) + SSLCertificateVerifier* ssl_cert_verifier, + bool dtls) : SSLAdapter(socket), ssl_session_cache_(ssl_session_cache), ssl_cert_verifier_(ssl_cert_verifier), @@ -206,7 +207,7 @@ OpenSSLAdapter::OpenSSLAdapter(Socket* socket, ssl_write_needs_read_(false), ssl_(nullptr), ssl_ctx_(nullptr), - ssl_mode_(SSL_MODE_TLS), + ssl_mode_(dtls ? SSL_MODE_DTLS : SSL_MODE_TLS), ignore_bad_cert_(false), custom_cert_verifier_status_(false) { // If a factory is used, take a reference on the factory's SSL_CTX. diff --git a/rtc_base/openssl_adapter.h b/rtc_base/openssl_adapter.h index a015ab5468b..83f568bc8f9 100644 --- a/rtc_base/openssl_adapter.h +++ b/rtc_base/openssl_adapter.h @@ -51,7 +51,8 @@ class OpenSSLAdapter final : public SSLAdapter { // immutable after the the SSL connection starts. explicit OpenSSLAdapter(Socket* socket, OpenSSLSessionCache* ssl_session_cache = nullptr, - SSLCertificateVerifier* ssl_cert_verifier = nullptr); + SSLCertificateVerifier* ssl_cert_verifier = nullptr, + bool dtls = false); ~OpenSSLAdapter() override; void SetIgnoreBadCert(bool ignore) override; diff --git a/rtc_base/openssl_adapter_unittest.cc b/rtc_base/openssl_adapter_unittest.cc index 7f64cbfaa27..8d40fd8c6c0 100644 --- a/rtc_base/openssl_adapter_unittest.cc +++ b/rtc_base/openssl_adapter_unittest.cc @@ -26,10 +26,10 @@ #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" #include "rtc_base/ssl_certificate.h" -#include "rtc_base/ssl_stream_adapter.h" // IWYU pragma: keep -#include "rtc_base/thread.h" +#include "rtc_base/ssl_stream_adapter.h" // IWYU pragma: keep #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" // IWYU pragma: keep namespace webrtc { @@ -145,7 +145,7 @@ TEST(OpenSSLAdapterTest, TestTransformAlpnProtocols) { // Verifies that SSLStart works when OpenSSLAdapter is started in standalone // mode. TEST(OpenSSLAdapterTest, TestBeginSSLBeforeConnection) { - AutoThread main_thread; + test::RunLoop main_thread; Socket* async_socket = new MockAsyncSocket(); OpenSSLAdapter adapter(async_socket); EXPECT_EQ(adapter.StartSSL("webrtc.org"), 0); @@ -158,7 +158,7 @@ TEST(OpenSSLAdapterTest, TestBeginSSLBeforeConnection) { // build and run this test. TEST(OpenSSLAdaptorTest, TestRealSSLConnection) { PhysicalSocketServer socket_server; - AutoSocketServerThread main_thread(&socket_server); + test::RunLoop main_thread(&socket_server); constexpr absl::string_view kHostname = "webrtc.org"; constexpr int kPort = 443; @@ -213,7 +213,7 @@ TEST(OpenSSLAdaptorTest, TestRealSSLConnection) { // Verifies that the adapter factory can create new adapters. TEST(OpenSSLAdapterFactoryTest, CreateSingleOpenSSLAdapter) { - AutoThread main_thread; + test::RunLoop main_thread; OpenSSLAdapterFactory adapter_factory; Socket* async_socket = new MockAsyncSocket(); auto simple_adapter = std::unique_ptr( @@ -224,7 +224,7 @@ TEST(OpenSSLAdapterFactoryTest, CreateSingleOpenSSLAdapter) { // Verifies that setting a custom verifier still allows for adapters to be // created. TEST(OpenSSLAdapterFactoryTest, CreateWorksWithCustomVerifier) { - AutoThread main_thread; + test::RunLoop main_thread; MockCertVerifier* mock_verifier = new MockCertVerifier(); EXPECT_CALL(*mock_verifier, Verify(_)).WillRepeatedly(Return(true)); auto cert_verifier = std::unique_ptr(mock_verifier); diff --git a/rtc_base/openssl_session_cache_unittest.cc b/rtc_base/openssl_session_cache_unittest.cc index 4b4da722116..148934497e2 100644 --- a/rtc_base/openssl_session_cache_unittest.cc +++ b/rtc_base/openssl_session_cache_unittest.cc @@ -17,6 +17,8 @@ #include "rtc_base/ssl_stream_adapter.h" #include "test/gtest.h" +namespace webrtc { + namespace { // Use methods that avoid X509 objects if possible. SSL_CTX* NewDtlsContext() { @@ -44,8 +46,6 @@ SSL_SESSION* NewSslSession(SSL_CTX* ssl_ctx) { } // namespace -namespace webrtc { - TEST(OpenSSLSessionCache, DTLSModeSetCorrectly) { SSL_CTX* ssl_ctx = NewDtlsContext(); diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc index dd5dfe7c18f..e3a75db8f41 100644 --- a/rtc_base/openssl_stream_adapter.cc +++ b/rtc_base/openssl_stream_adapter.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,6 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" @@ -248,7 +248,7 @@ int stream_read(BIO* b, char* out, int outl) { size_t read; int error; StreamResult result = stream->Read( - MakeArrayView(reinterpret_cast(out), outl), read, error); + std::span(reinterpret_cast(out), outl), read, error); if (result == SR_SUCCESS) { return checked_cast(read); } else if (result == SR_BLOCK) { @@ -266,7 +266,7 @@ int stream_write(BIO* b, const char* in, int inl) { size_t written; int error; StreamResult result = stream->Write( - MakeArrayView(reinterpret_cast(in), inl), written, error); + std::span(reinterpret_cast(in), inl), written, error); if (result == SR_SUCCESS) { return checked_cast(written); } else if (result == SR_BLOCK) { @@ -292,6 +292,7 @@ long stream_ctrl(BIO* b, int cmd, long num, void* ptr) { case BIO_CTRL_PENDING: return 0; case BIO_CTRL_FLUSH: { + // Flush signals the end of a flight of handshake packets. StreamInterface* stream = static_cast(BIO_get_data(b)); RTC_DCHECK(stream); if (stream->Flush()) { @@ -365,7 +366,7 @@ void OpenSSLStreamAdapter::SetServerRole(SSLRole role) { SSLPeerCertificateDigestError OpenSSLStreamAdapter::SetPeerCertificateDigest( absl::string_view digest_alg, - ArrayView digest_val) { + std::span digest_val) { RTC_DCHECK(!peer_certificate_verified_); RTC_DCHECK(!HasPeerCertificateDigest()); size_t expected_len; @@ -497,6 +498,44 @@ bool OpenSSLStreamAdapter::ExportSrtpKeyingMaterial( return true; } +bool OpenSSLStreamAdapter::AppendSrtpKeyingMaterial( + ZeroOnFreeBuffer& key_buffer) { + // Compute size of keying material. + int selected_crypto_suite; + if (!GetDtlsSrtpCryptoSuite(&selected_crypto_suite)) { + RTC_LOG(LS_ERROR) << "No crypto suite"; + return false; + } + int key_len, salt_len; + if (!GetSrtpKeyAndSaltLengths(selected_crypto_suite, &key_len, &salt_len)) { + RTC_LOG(LS_ERROR) << "Unable to get key and salt len from crypto suite"; + return false; + } + int key_material_size = key_len * 2 + salt_len * 2; + // Arguments are: + // keying material/len -- a buffer to hold the keying material. + // label -- the exporter label. + // part of the RFC defining each exporter + // usage. We only use RFC 5764 for DTLS-SRTP. + // context/context_len -- a context to bind to for this connection; + // use_context optional, can be null, 0 (IN). Not used by WebRTC. + bool success = false; + key_buffer.AppendData( + key_material_size, [&](std::span keying_material) -> size_t { + int result = SSL_export_keying_material( + ssl_, keying_material.data(), keying_material.size(), + kDtlsSrtpExporterLabel.data(), kDtlsSrtpExporterLabel.size(), + nullptr, 0, false); + if (result != 0) { + success = true; + return keying_material.size(); + } else { + return 0; // Consider no data appended + } + }); + return success; +} + uint16_t OpenSSLStreamAdapter::GetPeerSignatureAlgorithm() const { if (state_ != SSL_CONNECTED) { return 0; @@ -607,8 +646,6 @@ void OpenSSLStreamAdapter::UpdateRetransmissionTimeout(int timeout_ms) { dtls_handshake_timeout_ms_ = timeout_ms; #ifdef OPENSSL_IS_BORINGSSL if (ssl_ctx_ != nullptr && ssl_mode_ == SSL_MODE_DTLS) { - // TODO (jonaso, webrtc:367395350): Switch to upcoming - // DTLSv1_update_timeout_duration. DTLSv1_set_initial_timeout_duration(ssl_, dtls_handshake_timeout_ms_); // Clear the DTLS timer timeout_task_.Stop(); @@ -627,7 +664,7 @@ void OpenSSLStreamAdapter::SetMTU(int mtu) { // // StreamInterface Implementation // -StreamResult OpenSSLStreamAdapter::Write(ArrayView data, +StreamResult OpenSSLStreamAdapter::Write(std::span data, size_t& written, int& error) { RTC_DLOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Write(" << data.size() << ")"; @@ -685,7 +722,7 @@ StreamResult OpenSSLStreamAdapter::Write(ArrayView data, // not reached } -StreamResult OpenSSLStreamAdapter::Read(ArrayView data, +StreamResult OpenSSLStreamAdapter::Read(std::span data, size_t& read, int& error) { RTC_DLOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Read(" << data.size() << ")"; @@ -1198,7 +1235,7 @@ bool OpenSSLStreamAdapter::VerifyPeerCertificate() { return false; } - Buffer computed_digest(0, EVP_MAX_MD_SIZE); + Buffer computed_digest = Buffer::CreateWithCapacity(EVP_MAX_MD_SIZE); if (!peer_cert_chain_->Get(0).ComputeDigest( peer_certificate_digest_algorithm_, computed_digest)) { RTC_LOG(LS_WARNING) << "Failed to compute peer cert digest."; diff --git a/rtc_base/openssl_stream_adapter.h b/rtc_base/openssl_stream_adapter.h index 6b05faeff60..b8e531de675 100644 --- a/rtc_base/openssl_stream_adapter.h +++ b/rtc_base/openssl_stream_adapter.h @@ -17,12 +17,12 @@ #include #include +#include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/ssl_certificate.h" #ifdef OPENSSL_IS_BORINGSSL @@ -82,7 +82,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { void SetServerRole(SSLRole role = SSL_SERVER) override; SSLPeerCertificateDigestError SetPeerCertificateDigest( absl::string_view digest_alg, - ArrayView digest_val) override; + std::span digest_val) override; std::unique_ptr GetPeerSSLCertChain() const override; @@ -95,8 +95,8 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { void UpdateRetransmissionTimeout(int timeout_ms) override; void SetMTU(int mtu) override; - StreamResult Read(ArrayView data, size_t& read, int& error) override; - StreamResult Write(ArrayView data, + StreamResult Read(std::span data, size_t& read, int& error) override; + StreamResult Write(std::span data, size_t& written, int& error) override; void Close() override; @@ -111,6 +111,8 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { // Key Extractor interface bool ExportSrtpKeyingMaterial( ZeroOnFreeBuffer& keying_material) override; + bool AppendSrtpKeyingMaterial( + ZeroOnFreeBuffer& keying_material) override; uint16_t GetPeerSignatureAlgorithm() const override; diff --git a/rtc_base/operations_chain_unittest.cc b/rtc_base/operations_chain_unittest.cc index 9f7ce22758b..667b189069e 100644 --- a/rtc_base/operations_chain_unittest.cc +++ b/rtc_base/operations_chain_unittest.cc @@ -25,6 +25,7 @@ #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -397,7 +398,7 @@ TEST(OperationsChainTest, IsEmpty) { } TEST(OperationsChainTest, OnChainEmptyCallback) { - AutoThread main_thread; + test::RunLoop main_thread; OperationTrackerProxy operation_tracker_proxy; operation_tracker_proxy.Initialize()->Wait(Event::kForever); diff --git a/rtc_base/physical_socket_server.cc b/rtc_base/physical_socket_server.cc index 4d986f17523..fb916fb50e3 100644 --- a/rtc_base/physical_socket_server.cc +++ b/rtc_base/physical_socket_server.cc @@ -9,11 +9,15 @@ */ #include "rtc_base/physical_socket_server.h" +#include + +#include #include #include #include #include #include +#include #include #include "api/async_dns_resolver.h" @@ -110,6 +114,8 @@ typedef char* SockOptArg; #endif // !defined(EPOLLRDHUP) #endif // defined(WEBRTC_LINUX) +namespace webrtc { + namespace { // RFC-3168, Section 5. ECN is the two least significant bits. @@ -117,7 +123,7 @@ constexpr uint8_t kEcnMask = 0x03; #if defined(WEBRTC_POSIX) -webrtc::EcnMarking EcnFromDs(uint8_t ds) { +EcnMarking EcnFromDs(uint8_t ds) { // RFC-3168, Section 5. constexpr uint8_t ECN_ECT1 = 0x01; constexpr uint8_t ECN_ECT0 = 0x02; @@ -125,15 +131,15 @@ webrtc::EcnMarking EcnFromDs(uint8_t ds) { const uint8_t ecn = ds & kEcnMask; if (ecn == ECN_ECT1) { - return webrtc::EcnMarking::kEct1; + return EcnMarking::kEct1; } if (ecn == ECN_ECT0) { - return webrtc::EcnMarking::kEct0; + return EcnMarking::kEct0; } if (ecn == ECN_CE) { - return webrtc::EcnMarking::kCe; + return EcnMarking::kCe; } - return webrtc::EcnMarking::kNotEct; + return EcnMarking::kNotEct; } #endif @@ -152,7 +158,6 @@ class ScopedSetTrue { } // namespace -namespace webrtc { PhysicalSocket::PhysicalSocket(PhysicalSocketServer* ss, SOCKET s) : ss_(ss), @@ -487,11 +492,14 @@ int PhysicalSocket::RecvFrom(ReceiveBuffer& buffer) { int64_t timestamp = -1; static constexpr int BUF_SIZE = 64 * 1024; buffer.payload.EnsureCapacity(BUF_SIZE); - - int received = DoReadFromSocket( - buffer.payload.data(), buffer.payload.capacity(), &buffer.source_address, - ×tamp, ecn_ ? &buffer.ecn : nullptr); - buffer.payload.SetSize(received > 0 ? received : 0); + int received; + buffer.payload.SetData(BUF_SIZE, [&](std::span payload) { + received = + DoReadFromSocket(payload.data(), payload.size(), &buffer.source_address, + ×tamp, ecn_ ? &buffer.ecn : nullptr); + // return 0 if received is -1, indicating error. + return std::max(received, 0); + }); if (received > 0 && timestamp != -1) { buffer.arrival_time = Timestamp::Micros(timestamp); } diff --git a/rtc_base/physical_socket_server_unittest.cc b/rtc_base/physical_socket_server_unittest.cc index 56ec60903aa..909c80e63a6 100644 --- a/rtc_base/physical_socket_server_unittest.cc +++ b/rtc_base/physical_socket_server_unittest.cc @@ -24,9 +24,9 @@ #include "rtc_base/socket_address.h" #include "rtc_base/socket_unittest.h" #include "rtc_base/test_utils.h" -#include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" #define MAYBE_SKIP_IPV4 \ @@ -130,7 +130,7 @@ class PhysicalSocketTest : public SocketTest { void WritableAfterPartialWrite(const IPAddress& loopback); FakePhysicalSocketServer server_; - AutoSocketServerThread thread_; + test::RunLoop thread_; bool fail_accept_; int max_send_size_; }; diff --git a/rtc_base/platform_thread.cc b/rtc_base/platform_thread.cc index 66bc1c42ff2..2fee9eb44cf 100644 --- a/rtc_base/platform_thread.cc +++ b/rtc_base/platform_thread.cc @@ -36,7 +36,10 @@ int Win32PriorityFromThreadPriority(ThreadPriority priority) { case ThreadPriority::kNormal: return THREAD_PRIORITY_NORMAL; case ThreadPriority::kHigh: + case ThreadPriority::kVideo: return THREAD_PRIORITY_ABOVE_NORMAL; + case ThreadPriority::kAudio: + return THREAD_PRIORITY_HIGHEST; case ThreadPriority::kRealtime: return THREAD_PRIORITY_TIME_CRITICAL; } @@ -81,8 +84,12 @@ bool SetPriority(ThreadPriority priority) { param.sched_priority = (low_prio + top_prio - 1) / 2; break; case ThreadPriority::kHigh: + case ThreadPriority::kVideo: param.sched_priority = std::max(top_prio - 2, low_prio); break; + case ThreadPriority::kAudio: + param.sched_priority = std::max(top_prio - 1, low_prio); + break; case ThreadPriority::kRealtime: param.sched_priority = top_prio; break; diff --git a/rtc_base/platform_thread.h b/rtc_base/platform_thread.h index 73bc171fb7f..50841974b51 100644 --- a/rtc_base/platform_thread.h +++ b/rtc_base/platform_thread.h @@ -27,6 +27,8 @@ enum class ThreadPriority { kLow = 1, kNormal, kHigh, + kVideo, + kAudio, kRealtime, }; diff --git a/rtc_base/rtc_certificate_generator_unittest.cc b/rtc_base/rtc_certificate_generator_unittest.cc index 06bb5812e4a..10b9b4b511f 100644 --- a/rtc_base/rtc_certificate_generator_unittest.cc +++ b/rtc_base/rtc_certificate_generator_unittest.cc @@ -24,6 +24,7 @@ #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -74,7 +75,7 @@ class RTCCertificateGeneratorTest : public ::testing::Test { protected: static constexpr TimeDelta kGenerationTimeoutMs = TimeDelta::Millis(10000); - AutoThread main_thread_; + test::RunLoop main_thread_; RTCCertificateGeneratorFixture fixture_; }; diff --git a/rtc_base/server_socket_adapters.cc b/rtc_base/server_socket_adapters.cc index 339afb78b09..85047e6f9ad 100644 --- a/rtc_base/server_socket_adapters.cc +++ b/rtc_base/server_socket_adapters.cc @@ -13,8 +13,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/socket.h" #include "rtc_base/socket_adapters.h" @@ -34,7 +34,7 @@ AsyncSSLServerSocket::AsyncSSLServerSocket(Socket* socket) void AsyncSSLServerSocket::ProcessInput(char* data, size_t* len) { // We only accept client hello messages. - const ArrayView client_hello = + const std::span client_hello = AsyncSSLSocket::SslClientHello(); if (*len < client_hello.size()) { return; @@ -51,7 +51,7 @@ void AsyncSSLServerSocket::ProcessInput(char* data, size_t* len) { // Clients should not send more data until the handshake is completed. RTC_DCHECK(*len == 0); - const ArrayView server_hello = + const std::span server_hello = AsyncSSLSocket::SslServerHello(); // Send a server hello back to the client. DirectSend(server_hello.data(), server_hello.size()); diff --git a/rtc_base/socket.cc b/rtc_base/socket.cc index 639e78bb809..744311ce9ad 100644 --- a/rtc_base/socket.cc +++ b/rtc_base/socket.cc @@ -10,7 +10,9 @@ #include "rtc_base/socket.h" +#include #include +#include #include "api/units/timestamp.h" #include "rtc_base/buffer.h" @@ -20,11 +22,14 @@ namespace webrtc { int Socket::RecvFrom(ReceiveBuffer& buffer) { static constexpr int BUF_SIZE = 64 * 1024; int64_t timestamp = -1; + int len; buffer.payload.EnsureCapacity(BUF_SIZE); - int len = RecvFrom(buffer.payload.data(), buffer.payload.capacity(), - &buffer.source_address, ×tamp); - buffer.payload.SetSize(len > 0 ? len : 0); - if (len > 0 && timestamp != -1) { + buffer.payload.SetData(BUF_SIZE, [&](std::span payload) { + len = RecvFrom(payload.data(), payload.size(), &buffer.source_address, + ×tamp); + return std::max(len, 0); + }); + if (!buffer.payload.empty() && timestamp != -1) { buffer.arrival_time = Timestamp::Micros(timestamp); } diff --git a/rtc_base/socket_adapters.cc b/rtc_base/socket_adapters.cc index 60d52b2bb6a..56b20bdd499 100644 --- a/rtc_base/socket_adapters.cc +++ b/rtc_base/socket_adapters.cc @@ -15,8 +15,8 @@ #include #include #include +#include -#include "api/array_view.h" #include "rtc_base/async_socket.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -135,8 +135,8 @@ static const uint8_t kSslClientHello[] = { }; // static -ArrayView AsyncSSLSocket::SslClientHello() { - // Implicit conversion directly from kSslClientHello to ArrayView fails when +std::span AsyncSSLSocket::SslClientHello() { + // Implicit conversion directly from kSslClientHello to std::span fails when // built with gcc. return {kSslClientHello, sizeof(kSslClientHello)}; } @@ -163,7 +163,7 @@ static const uint8_t kSslServerHello[] = { }; // static -ArrayView AsyncSSLSocket::SslServerHello() { +std::span AsyncSSLSocket::SslServerHello() { return {kSslServerHello, sizeof(kSslServerHello)}; } diff --git a/rtc_base/socket_adapters.h b/rtc_base/socket_adapters.h index 3abd888ad3e..e0954a9fe41 100644 --- a/rtc_base/socket_adapters.h +++ b/rtc_base/socket_adapters.h @@ -13,8 +13,8 @@ #include #include +#include -#include "api/array_view.h" #include "rtc_base/async_socket.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" @@ -59,8 +59,8 @@ class BufferedReadAdapter : public AsyncSocketAdapter { // fake SSL handshake. Used for "ssltcp" P2P functionality. class AsyncSSLSocket : public BufferedReadAdapter { public: - static ArrayView SslClientHello(); - static ArrayView SslServerHello(); + static std::span SslClientHello(); + static std::span SslServerHello(); explicit AsyncSSLSocket(Socket* socket); diff --git a/rtc_base/socket_unittest.cc b/rtc_base/socket_unittest.cc index 66c933eeade..ff3c0546f19 100644 --- a/rtc_base/socket_unittest.cc +++ b/rtc_base/socket_unittest.cc @@ -855,13 +855,13 @@ void SocketTest::TcpInternal(const IPAddress& loopback, EXPECT_EQ(sender->GetRemoteAddress(), receiver->GetLocalAddress()); // Create test data. - Buffer send_buffer(0, data_size); - Buffer recv_buffer(0, data_size); + Buffer send_buffer(Buffer::CreateWithCapacity(data_size)); + Buffer recv_buffer(Buffer::CreateWithCapacity(data_size)); for (size_t i = 0; i < data_size; ++i) { char ch = static_cast(i % 256); send_buffer.AppendData(&ch, sizeof(ch)); } - Buffer recved_data(0, data_size); + Buffer recved_data(Buffer::CreateWithCapacity(data_size)); // Send and receive a bunch of data. size_t sent_size = 0; diff --git a/rtc_base/ssl_adapter.cc b/rtc_base/ssl_adapter.cc index 098556da624..f68083e2e9a 100644 --- a/rtc_base/ssl_adapter.cc +++ b/rtc_base/ssl_adapter.cc @@ -23,8 +23,8 @@ std::unique_ptr SSLAdapterFactory::Create() { return std::make_unique(); } -SSLAdapter* SSLAdapter::Create(Socket* socket) { - return new OpenSSLAdapter(socket); +SSLAdapter* SSLAdapter::Create(Socket* socket, bool dtls) { + return new OpenSSLAdapter(socket, nullptr, nullptr, dtls); } /////////////////////////////////////////////////////////////////////////////// diff --git a/rtc_base/ssl_adapter.h b/rtc_base/ssl_adapter.h index 7b91c569288..08546cb3125 100644 --- a/rtc_base/ssl_adapter.h +++ b/rtc_base/ssl_adapter.h @@ -106,7 +106,7 @@ class SSLAdapter : public AsyncSocketAdapter { // Create the default SSL adapter for this platform. On failure, returns null // and deletes `socket`. Otherwise, the returned SSLAdapter takes ownership // of `socket`. - static SSLAdapter* Create(Socket* socket); + static SSLAdapter* Create(Socket* socket, bool dtls = false); private: // Not supported. diff --git a/rtc_base/ssl_adapter_unittest.cc b/rtc_base/ssl_adapter_unittest.cc index 79c987a89e3..a4a069b829d 100644 --- a/rtc_base/ssl_adapter_unittest.cc +++ b/rtc_base/ssl_adapter_unittest.cc @@ -30,6 +30,7 @@ #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/wait_until.h" namespace webrtc { @@ -291,7 +292,7 @@ class SSLAdapterTestBase : public ::testing::Test { protected: std::unique_ptr vss_; - AutoSocketServerThread thread_; + test::RunLoop thread_; std::unique_ptr server_; std::unique_ptr client_; std::unique_ptr cert_verifier_; diff --git a/rtc_base/ssl_certificate.cc b/rtc_base/ssl_certificate.cc index 403638cd670..e144d39bde1 100644 --- a/rtc_base/ssl_certificate.cc +++ b/rtc_base/ssl_certificate.cc @@ -13,13 +13,13 @@ #include #include #include +#include #include #include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/base64.h" #include "rtc_base/buffer.h" #include "rtc_base/openssl.h" @@ -83,7 +83,7 @@ std::unique_ptr SSLCertificate::GetStats() const { Buffer der_buffer; ToDER(&der_buffer); - ArrayView der_view(der_buffer); + std::span der_view(der_buffer); std::string der_base64 = Base64Encode(der_view); return std::make_unique(std::move(fingerprint), diff --git a/rtc_base/ssl_fingerprint.cc b/rtc_base/ssl_fingerprint.cc index f3f6fa62285..6911d8b5e80 100644 --- a/rtc_base/ssl_fingerprint.cc +++ b/rtc_base/ssl_fingerprint.cc @@ -14,11 +14,11 @@ #include #include #include +#include #include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/buffer.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" @@ -43,7 +43,7 @@ std::unique_ptr SSLFingerprint::CreateUnique( std::unique_ptr SSLFingerprint::Create( absl::string_view algorithm, const SSLCertificate& cert) { - Buffer digest(0, MessageDigest::kMaxSize); + Buffer digest = Buffer::CreateWithCapacity(MessageDigest::kMaxSize); bool ret = cert.ComputeDigest(algorithm, digest); if (!ret) { return nullptr; @@ -68,13 +68,13 @@ std::unique_ptr SSLFingerprint::CreateUniqueFromRfc4572( char value[MessageDigest::kMaxSize]; size_t value_len = - hex_decode_with_delimiter(ArrayView(value), fingerprint, ':'); + hex_decode_with_delimiter(std::span(value), fingerprint, ':'); if (!value_len) return nullptr; return std::make_unique( algorithm, - ArrayView(reinterpret_cast(value), value_len)); + std::span(reinterpret_cast(value), value_len)); } std::unique_ptr SSLFingerprint::CreateFromCertificate( @@ -96,13 +96,13 @@ std::unique_ptr SSLFingerprint::CreateFromCertificate( } SSLFingerprint::SSLFingerprint(absl::string_view algorithm, - ArrayView digest_view) + std::span digest_view) : algorithm(algorithm), digest(digest_view.data(), digest_view.size()) {} SSLFingerprint::SSLFingerprint(absl::string_view algorithm, const uint8_t* digest_in, size_t digest_len) - : SSLFingerprint(algorithm, MakeArrayView(digest_in, digest_len)) {} + : SSLFingerprint(algorithm, std::span(digest_in, digest_len)) {} bool SSLFingerprint::operator==(const SSLFingerprint& other) const { return algorithm == other.algorithm && digest == other.digest; diff --git a/rtc_base/ssl_fingerprint.h b/rtc_base/ssl_fingerprint.h index 89757f48656..103e4a29d7c 100644 --- a/rtc_base/ssl_fingerprint.h +++ b/rtc_base/ssl_fingerprint.h @@ -15,10 +15,10 @@ #include #include +#include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_certificate.h" @@ -53,7 +53,7 @@ struct RTC_EXPORT SSLFingerprint { const RTCCertificate& cert); SSLFingerprint(absl::string_view algorithm, - ArrayView digest_view); + std::span digest_view); // TODO(steveanton): Remove once downstream projects have moved off of this. SSLFingerprint(absl::string_view algorithm, const uint8_t* digest_in, diff --git a/rtc_base/ssl_identity.cc b/rtc_base/ssl_identity.cc index 2186d3a60f9..f68797afa31 100644 --- a/rtc_base/ssl_identity.cc +++ b/rtc_base/ssl_identity.cc @@ -18,11 +18,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #ifdef OPENSSL_IS_BORINGSSL #include "rtc_base/boringssl_identity.h" @@ -206,7 +206,7 @@ std::string SSLIdentity::DerToPem(absl::string_view pem_type, StringBuilder result; result << "-----BEGIN " << pem_type << "-----\n"; - ArrayView data_view(data, length); + std::span data_view(data, length); std::string b64_encoded = Base64Encode(data_view); // Divide the Base-64 encoded data into 64-character chunks, as per 4.3.2.4 // of RFC 1421. diff --git a/rtc_base/ssl_identity_unittest.cc b/rtc_base/ssl_identity_unittest.cc index 136ef601fe7..69d7b2a1028 100644 --- a/rtc_base/ssl_identity_unittest.cc +++ b/rtc_base/ssl_identity_unittest.cc @@ -259,7 +259,7 @@ class SSLIdentityTest : public ::testing::Test { EXPECT_EQ(expected_len, digest.size()); // Repeat digest computation for the identity as a sanity check. - Buffer digest1(0, MessageDigest::kMaxSize); + Buffer digest1 = Buffer::CreateWithCapacity(MessageDigest::kMaxSize); std::memset(digest1.data(), 0xff, expected_len); EXPECT_TRUE(identity->certificate().ComputeDigest(algorithm, digest1)); EXPECT_EQ(expected_len, digest1.size()); @@ -291,7 +291,7 @@ class SSLIdentityTest : public ::testing::Test { void TestDigestForFixedCert(absl::string_view algorithm, size_t expected_len, const unsigned char* expected_digest) { - Buffer digest(0, MessageDigest::kMaxSize); + Buffer digest(Buffer::CreateWithCapacity(MessageDigest::kMaxSize)); ASSERT_TRUE(expected_len <= digest.capacity()); diff --git a/rtc_base/ssl_stream_adapter.cc b/rtc_base/ssl_stream_adapter.cc index d02318bc684..59216f0357c 100644 --- a/rtc_base/ssl_stream_adapter.cc +++ b/rtc_base/ssl_stream_adapter.cc @@ -15,13 +15,13 @@ #include #include #include +#include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "rtc_base/openssl_stream_adapter.h" #include "rtc_base/ssl_identity.h" @@ -181,7 +181,7 @@ bool SSLStreamAdapter::SetPeerCertificateDigest( SSLPeerCertificateDigestError* error) { unsigned char* nonconst_val = const_cast(digest_val); SSLPeerCertificateDigestError ret = SetPeerCertificateDigest( - digest_alg, ArrayView(nonconst_val, digest_len)); + digest_alg, std::span(nonconst_val, digest_len)); if (error) *error = ret; return ret == SSLPeerCertificateDigestError::NONE; diff --git a/rtc_base/ssl_stream_adapter.h b/rtc_base/ssl_stream_adapter.h index be7af301cd2..75e672902bf 100644 --- a/rtc_base/ssl_stream_adapter.h +++ b/rtc_base/ssl_stream_adapter.h @@ -17,14 +17,15 @@ #include #include #include +#include #include #include #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/field_trials_view.h" #include "rtc_base/buffer.h" +#include "rtc_base/checks.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/stream.h" @@ -190,9 +191,9 @@ class SSLStreamAdapter : public StreamInterface { // Returns SSLPeerCertificateDigestError::NONE if successful. virtual SSLPeerCertificateDigestError SetPeerCertificateDigest( absl::string_view digest_alg, - ArrayView digest_val) = 0; + std::span digest_val) = 0; [[deprecated( - "Use SetPeerCertificateDigest with ArrayView instead")]] virtual bool + "Use SetPeerCertificateDigest with std::span instead")]] virtual bool SetPeerCertificateDigest(absl::string_view digest_alg, const unsigned char* digest_val, size_t digest_len, @@ -218,7 +219,18 @@ class SSLStreamAdapter : public StreamInterface { virtual bool GetSslVersionBytes(int* version) const = 0; // Key Exporter interface from RFC 5705 - virtual bool ExportSrtpKeyingMaterial( + // The buffer must be preinitialized with a `size()` that will fit exactly + // the keying material. + [[deprecated("Use AppendSrtpKeyingMaterial")]] virtual bool + ExportSrtpKeyingMaterial(ZeroOnFreeBuffer& keying_material) { + RTC_DCHECK_NOTREACHED() << "Use AppendSrtpKeyingMaterial"; + return false; + } + + // Extract the keys and append them to the buffer. The function will compute + // the amount of keying material needed and append this many bytes. + // The buffer size will grow by the number of bytes appended. + virtual bool AppendSrtpKeyingMaterial( ZeroOnFreeBuffer& keying_material) = 0; // Returns the signature algorithm or 0 if not applicable. diff --git a/rtc_base/ssl_stream_adapter_unittest.cc b/rtc_base/ssl_stream_adapter_unittest.cc index 2bde4292699..8bd26c3873a 100644 --- a/rtc_base/ssl_stream_adapter_unittest.cc +++ b/rtc_base/ssl_stream_adapter_unittest.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,29 +33,28 @@ #include "absl/memory/memory.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/crypto/crypto_options.h" #include "api/field_trials.h" #include "api/sequence_checker.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/test/rtc_error_matchers.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/buffer.h" #include "rtc_base/buffer_queue.h" #include "rtc_base/callback_list.h" #include "rtc_base/checks.h" #include "rtc_base/crypto_random.h" -#include "rtc_base/fake_clock.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/stream.h" #include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" #include "test/create_test_field_trials.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" #include "test/wait_until.h" namespace webrtc { @@ -211,7 +211,7 @@ class SSLStreamAdapterTestBase; class StreamWrapper : public StreamInterface { public: explicit StreamWrapper(std::unique_ptr stream) - : stream_(std::move(stream)) { + : stream_(std::move(stream)), flush_count_(0) { stream_->SetEventCallback([this](int events, int err) { RTC_DCHECK_RUN_ON(&callback_sequence_); callbacks_.Send(events, err); @@ -231,14 +231,19 @@ class StreamWrapper : public StreamInterface { StreamState GetState() const override { return stream_->GetState(); } void Close() override { stream_->Close(); } + bool Flush() override { + flush_count_++; + return stream_->Flush(); + } + size_t GetFlushCountForTesting() { return flush_count_; } - StreamResult Read(ArrayView buffer, + StreamResult Read(std::span buffer, size_t& read, int& error) override { return stream_->Read(buffer, read, error); } - StreamResult Write(ArrayView data, + StreamResult Write(std::span data, size_t& written, int& error) override { return stream_->Write(data, written, error); @@ -247,6 +252,7 @@ class StreamWrapper : public StreamInterface { private: const std::unique_ptr stream_; CallbackList callbacks_; + size_t flush_count_; }; class SSLDummyStream final : public StreamInterface { @@ -271,7 +277,7 @@ class SSLDummyStream final : public StreamInterface { StreamState GetState() const override { return SS_OPEN; } - StreamResult Read(ArrayView buffer, + StreamResult Read(std::span buffer, size_t& read, int& error) override { StreamResult r; @@ -312,13 +318,13 @@ class SSLDummyStream final : public StreamInterface { } // Write to the outgoing FifoBuffer - StreamResult WriteData(ArrayView data, + StreamResult WriteData(std::span data, size_t& written, int& error) { return out_->Write(data, written, error); } - StreamResult Write(ArrayView data, + StreamResult Write(std::span data, size_t& written, int& error) override; @@ -326,6 +332,7 @@ class SSLDummyStream final : public StreamInterface { RTC_LOG(LS_INFO) << "Closing outbound stream"; out_->Close(); } + bool Flush() override { return in_->Flush(); } private: void PostEvent(int events, int err) { @@ -355,7 +362,7 @@ class BufferQueueStream : public StreamInterface { StreamState GetState() const override { return SS_OPEN; } // Reading a buffer queue stream will either succeed or block. - StreamResult Read(ArrayView buffer, + StreamResult Read(std::span buffer, size_t& read, int& error) override { const bool was_writable = buffer_.is_writable(); @@ -369,7 +376,7 @@ class BufferQueueStream : public StreamInterface { } // Writing to a buffer queue stream will either succeed or block. - StreamResult Write(ArrayView data, + StreamResult Write(std::span data, size_t& written, int& error) override { const bool was_readable = buffer_.is_readable(); @@ -384,6 +391,7 @@ class BufferQueueStream : public StreamInterface { // A buffer queue stream can not be closed. void Close() override {} + bool Flush() { return false; } protected: void NotifyReadableForTest() { PostEvent(SE_READ, 0); } @@ -511,8 +519,8 @@ class SSLStreamAdapterTestBase : public ::testing::Test { } void SetPeerIdentitiesByDigest(bool correct, bool expect_success) { - Buffer server_digest(0, EVP_MAX_MD_SIZE); - Buffer client_digest(0, EVP_MAX_MD_SIZE); + Buffer server_digest(Buffer::CreateWithCapacity(EVP_MAX_MD_SIZE)); + Buffer client_digest(Buffer::CreateWithCapacity(EVP_MAX_MD_SIZE)); SSLPeerCertificateDigestError err; SSLPeerCertificateDigestError expected_err = expect_success ? SSLPeerCertificateDigestError::NONE @@ -583,20 +591,21 @@ class SSLStreamAdapterTestBase : public ::testing::Test { (server_ssl_->GetState() == SS_OPEN); }, ::testing::IsTrue(), - {.timeout = handshake_wait_, .clock = &clock_}), + {.timeout = handshake_wait_, .clock = &time_controller_}), IsRtcOk()); } else { - EXPECT_THAT(WaitUntil([&] { return client_ssl_->GetState(); }, - ::testing::Eq(SS_CLOSED), - {.timeout = handshake_wait_, .clock = &clock_}), - IsRtcOk()); + EXPECT_THAT( + WaitUntil([&] { return client_ssl_->GetState(); }, + ::testing::Eq(SS_CLOSED), + {.timeout = handshake_wait_, .clock = &time_controller_}), + IsRtcOk()); } } - // This tests that we give up after 12 DTLS resends. + // This tests that we give up after one hour. // Only works for BoringSSL which allows advancing the fake clock. void TestHandshakeTimeout() { - int64_t time_start = clock_.TimeNanos(); + Timestamp time_start = time_controller_.GetClock()->CurrentTime(); TimeDelta time_increment = TimeDelta::Millis(1000); if (!dtls_) { @@ -624,16 +633,16 @@ class SSLStreamAdapterTestBase : public ::testing::Test { // Now wait for the handshake to timeout (or fail after an hour of simulated // time). while (client_ssl_->GetState() == SS_OPENING && - (TimeDiff(clock_.TimeNanos(), time_start) < - 3600 * kNumNanosecsPerSec)) { + (time_controller_.GetClock()->CurrentTime() - time_start < + TimeDelta::Minutes(60))) { EXPECT_THAT(WaitUntil( [&] { return !((client_ssl_->GetState() == SS_OPEN) && (server_ssl_->GetState() == SS_OPEN)); }, - ::testing::IsTrue(), {.clock = &clock_}), + ::testing::IsTrue(), {.clock = &time_controller_}), IsRtcOk()); - clock_.AdvanceTime(time_increment); + time_controller_.AdvanceTime(time_increment); } EXPECT_EQ(client_ssl_->GetState(), SS_CLOSED); } @@ -663,7 +672,7 @@ class SSLStreamAdapterTestBase : public ::testing::Test { server_ssl_->IsTlsConnected(); }, ::testing::IsTrue(), - {.timeout = handshake_wait_, .clock = &clock_}), + {.timeout = handshake_wait_, .clock = &time_controller_}), IsRtcOk()); // Until the identity has been verified, the state should still be @@ -679,8 +688,8 @@ class SSLStreamAdapterTestBase : public ::testing::Test { // Collect both of the certificate digests; needs to be done before calling // SetPeerCertificateDigest as that may reset the identity. - Buffer server_digest(0, EVP_MAX_MD_SIZE); - Buffer client_digest(0, EVP_MAX_MD_SIZE); + Buffer server_digest(Buffer::CreateWithCapacity(EVP_MAX_MD_SIZE)); + Buffer client_digest(Buffer::CreateWithCapacity(EVP_MAX_MD_SIZE)); ASSERT_THAT(server_identity(), NotNull()); ASSERT_TRUE(server_identity()->certificate().ComputeDigest( @@ -754,12 +763,12 @@ class SSLStreamAdapterTestBase : public ::testing::Test { RTC_LOG(LS_VERBOSE) << "Damaging packet"; memcpy(&buf[0], data, data_len); buf[data_len - 1]++; - return from->WriteData(MakeArrayView(&buf[0], data_len), written, error); + return from->WriteData(std::span(&buf[0], data_len), written, error); } return from->WriteData( - MakeArrayView(reinterpret_cast(data), data_len), - written, error); + std::span(reinterpret_cast(data), data_len), written, + error); } void SetDelay(int delay) { delay_ = delay; } @@ -855,8 +864,7 @@ class SSLStreamAdapterTestBase : public ::testing::Test { return server_ssl_->GetIdentityForTesting(); } - AutoThread main_thread_; - ScopedFakeClock clock_; + GlobalSimulatedTimeController time_controller_{Timestamp::Micros(1234567)}; std::string client_cert_pem_; std::string client_private_key_pem_; KeyParams client_key_type_; @@ -918,7 +926,7 @@ class SSLStreamAdapterTestDTLSBase : public SSLStreamAdapterTestBase { size_t sent; int error; StreamResult rv = - client_ssl_->Write(MakeArrayView(packet, packet_size_), sent, error); + client_ssl_->Write(std::span(packet, packet_size_), sent, error); if (rv == SR_SUCCESS) { RTC_LOG(LS_VERBOSE) << "Sent: " << sent_; sent_++; @@ -975,19 +983,19 @@ class SSLStreamAdapterTestDTLSBase : public SSLStreamAdapterTestBase { WriteData(); - EXPECT_THAT( - WaitUntil([&] { return sent_; }, ::testing::Eq(count_), - {.timeout = TimeDelta::Millis(10000), .clock = &clock_}), - IsRtcOk()); + EXPECT_THAT(WaitUntil([&] { return sent_; }, ::testing::Eq(count_), + {.timeout = TimeDelta::Millis(10000), + .clock = &time_controller_}), + IsRtcOk()); RTC_LOG(LS_INFO) << "sent_ == " << sent_; if (damage_) { - clock_.AdvanceTime(TimeDelta::Millis(2000)); + time_controller_.AdvanceTime(TimeDelta::Millis(2000)); EXPECT_EQ(0U, received_.size()); } else if (loss_ == 0) { EXPECT_THAT(WaitUntil([&] { return received_.size(); }, ::testing::Eq(static_cast(sent_)), - {.clock = &clock_}), + {.clock = &time_controller_}), IsRtcOk()); } else { RTC_LOG(LS_INFO) << "Sent " << sent_ << " packets; received " @@ -1008,7 +1016,7 @@ class SSLStreamAdapterTestDTLSBase : public SSLStreamAdapterTestBase { std::set received_; }; -webrtc::StreamResult SSLDummyStream::Write(ArrayView data, +webrtc::StreamResult SSLDummyStream::Write(std::span data, size_t& written, int& error) { RTC_LOG(LS_VERBOSE) << "Writing to loopback " << data.size(); @@ -1178,11 +1186,13 @@ class SSLStreamAdapterTestDTLS : public SSLStreamAdapterTestDTLSBase { #endif // Test that we can make a handshake work if the first packet in // each direction is lost. This gives us predictable loss -// rather than having to tune random +// rather than having to tune random. TEST_F(SSLStreamAdapterTestDTLS, MAYBE_TestDTLSConnectWithLostFirstPacketNoDelay) { SetLoseFirstPacket(true); TestHandshake(); + // 2 client flights and 1 resend. + EXPECT_EQ(client_buffer_.GetFlushCountForTesting(), 3u); } #ifdef OPENSSL_IS_BORINGSSL @@ -1192,7 +1202,7 @@ TEST_F(SSLStreamAdapterTestDTLS, #define MAYBE_TestDTLSConnectWithLostFirstPacketDelay2s \ DISABLED_TestDTLSConnectWithLostFirstPacketDelay2s #endif -// Test a handshake with loss and delay +// Test a handshake with loss and delay. TEST_F(SSLStreamAdapterTestDTLS, MAYBE_TestDTLSConnectWithLostFirstPacketDelay2s) { SetLoseFirstPacket(true); @@ -1211,6 +1221,8 @@ TEST_F(SSLStreamAdapterTestDTLS, TEST_F(SSLStreamAdapterTestDTLS, MAYBE_TestDTLSConnectTimeout) { SetLoss(100); TestHandshakeTimeout(); + // 1 flush for the initial send, 13 for the resends. + EXPECT_EQ(client_buffer_.GetFlushCountForTesting(), 14u); } // Test transfer -- trivial @@ -1418,10 +1430,34 @@ TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpExporter) { ZeroOnFreeBuffer server_out = ZeroOnFreeBuffer::CreateUninitializedWithSize( 2 * (key_len + salt_len)); - +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" EXPECT_TRUE(client_ssl_->ExportSrtpKeyingMaterial(client_out)); EXPECT_TRUE(server_ssl_->ExportSrtpKeyingMaterial(server_out)); EXPECT_EQ(client_out, server_out); +#pragma clang diagnostic pop + + ZeroOnFreeBuffer append_client_out; + ZeroOnFreeBuffer append_server_out; + + EXPECT_TRUE(client_ssl_->AppendSrtpKeyingMaterial(append_client_out)); + EXPECT_TRUE(server_ssl_->AppendSrtpKeyingMaterial(append_server_out)); + EXPECT_EQ(client_out, append_client_out); + EXPECT_EQ(client_out, append_server_out); +} + +TEST_F(SSLStreamAdapterTestDTLS, TestDTLSSrtpExporterWithAppend) { + const std::vector crypto_suites = {kSrtpAes128CmSha1_80}; + SetDtlsSrtpCryptoSuites(crypto_suites, true); + SetDtlsSrtpCryptoSuites(crypto_suites, false); + + TestHandshake(); + ZeroOnFreeBuffer client_out; + ZeroOnFreeBuffer server_out; + + EXPECT_TRUE(client_ssl_->AppendSrtpKeyingMaterial(client_out)); + EXPECT_TRUE(server_ssl_->AppendSrtpKeyingMaterial(server_out)); + EXPECT_EQ(client_out, server_out); } // Test not yet valid certificates are not rejected. diff --git a/rtc_base/stream.h b/rtc_base/stream.h index a8191567d52..07da2266e34 100644 --- a/rtc_base/stream.h +++ b/rtc_base/stream.h @@ -13,10 +13,10 @@ #include #include +#include #include #include "absl/functional/any_invocable.h" -#include "api/array_view.h" #include "api/sequence_checker.h" #include "rtc_base/checks.h" #include "rtc_base/system/no_unique_address.h" @@ -74,10 +74,10 @@ class RTC_EXPORT StreamInterface { // SR_EOS: the end-of-stream has been reached, or the stream is in the // SS_CLOSED state. - virtual StreamResult Read(ArrayView buffer, + virtual StreamResult Read(std::span buffer, size_t& read, int& error) = 0; - virtual StreamResult Write(ArrayView data, + virtual StreamResult Write(std::span data, size_t& written, int& error) = 0; diff --git a/rtc_base/string_encode.cc b/rtc_base/string_encode.cc index 6b0b1e00494..717b8323900 100644 --- a/rtc_base/string_encode.cc +++ b/rtc_base/string_encode.cc @@ -11,11 +11,11 @@ #include "rtc_base/string_encode.h" #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" namespace webrtc { @@ -91,7 +91,7 @@ std::string hex_encode_with_delimiter(absl::string_view source, return s; } -size_t hex_decode_with_delimiter(ArrayView cbuffer, +size_t hex_decode_with_delimiter(std::span cbuffer, absl::string_view source, char delimiter) { if (cbuffer.empty()) @@ -131,7 +131,7 @@ size_t hex_decode_with_delimiter(ArrayView cbuffer, return bufpos; } -size_t hex_decode(ArrayView buffer, absl::string_view source) { +size_t hex_decode(std::span buffer, absl::string_view source) { return hex_decode_with_delimiter(buffer, source, 0); } diff --git a/rtc_base/string_encode.h b/rtc_base/string_encode.h index fb2a6e021a4..187d8379a42 100644 --- a/rtc_base/string_encode.h +++ b/rtc_base/string_encode.h @@ -14,12 +14,12 @@ #include #include +#include #include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/string_to_number.h" #include "rtc_base/strings/string_format.h" // IWYU pragma: keep @@ -34,13 +34,13 @@ std::string hex_encode(absl::string_view str); std::string hex_encode_with_delimiter(absl::string_view source, char delimiter); // hex_decode converts ascii hex to binary. -size_t hex_decode(ArrayView buffer, absl::string_view source); +size_t hex_decode(std::span buffer, absl::string_view source); // hex_decode, assuming that there is a delimiter between every byte // pair. // `delimiter` == 0 means no delimiter // If the buffer is too short or the data is invalid, we return 0. -size_t hex_decode_with_delimiter(ArrayView buffer, +size_t hex_decode_with_delimiter(std::span buffer, absl::string_view source, char delimiter); diff --git a/rtc_base/string_encode_unittest.cc b/rtc_base/string_encode_unittest.cc index d26cd6b82ed..701e52d2434 100644 --- a/rtc_base/string_encode_unittest.cc +++ b/rtc_base/string_encode_unittest.cc @@ -11,11 +11,11 @@ #include "rtc_base/string_encode.h" #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -39,7 +39,7 @@ class HexEncodeTest : public ::testing::Test { TEST_F(HexEncodeTest, TestWithNoDelimiter) { std::string encoded = hex_encode(data_view_); EXPECT_EQ("80818283848586878889", encoded); - dec_res_ = hex_decode(ArrayView(decoded_), encoded); + dec_res_ = hex_decode(std::span(decoded_), encoded); ASSERT_EQ(sizeof(data_), dec_res_); ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); } @@ -48,7 +48,7 @@ TEST_F(HexEncodeTest, TestWithNoDelimiter) { TEST_F(HexEncodeTest, TestWithDelimiter) { std::string encoded = hex_encode_with_delimiter(data_view_, ':'); EXPECT_EQ("80:81:82:83:84:85:86:87:88:89", encoded); - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, ':'); + dec_res_ = hex_decode_with_delimiter(std::span(decoded_), encoded, ':'); ASSERT_EQ(sizeof(data_), dec_res_); ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); } @@ -56,7 +56,7 @@ TEST_F(HexEncodeTest, TestWithDelimiter) { // Test that encoding with one delimiter and decoding with another fails. TEST_F(HexEncodeTest, TestWithWrongDelimiter) { std::string encoded = hex_encode_with_delimiter(data_view_, ':'); - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, '/'); + dec_res_ = hex_decode_with_delimiter(std::span(decoded_), encoded, '/'); ASSERT_EQ(0U, dec_res_); } @@ -64,7 +64,7 @@ TEST_F(HexEncodeTest, TestWithWrongDelimiter) { TEST_F(HexEncodeTest, TestExpectedDelimiter) { std::string encoded = hex_encode(data_view_); EXPECT_EQ(sizeof(data_) * 2, encoded.size()); - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, ':'); + dec_res_ = hex_decode_with_delimiter(std::span(decoded_), encoded, ':'); ASSERT_EQ(0U, dec_res_); } @@ -72,7 +72,7 @@ TEST_F(HexEncodeTest, TestExpectedDelimiter) { TEST_F(HexEncodeTest, TestExpectedNoDelimiter) { std::string encoded = hex_encode_with_delimiter(data_view_, ':'); EXPECT_EQ(sizeof(data_) * 3 - 1, encoded.size()); - dec_res_ = hex_decode(ArrayView(decoded_), encoded); + dec_res_ = hex_decode(std::span(decoded_), encoded); ASSERT_EQ(0U, dec_res_); } @@ -80,7 +80,7 @@ TEST_F(HexEncodeTest, TestExpectedNoDelimiter) { TEST_F(HexEncodeTest, TestZeroLengthNoDelimiter) { std::string encoded = hex_encode(""); EXPECT_TRUE(encoded.empty()); - dec_res_ = hex_decode(ArrayView(decoded_), encoded); + dec_res_ = hex_decode(std::span(decoded_), encoded); ASSERT_EQ(0U, dec_res_); } @@ -88,47 +88,47 @@ TEST_F(HexEncodeTest, TestZeroLengthNoDelimiter) { TEST_F(HexEncodeTest, TestZeroLengthWithDelimiter) { std::string encoded = hex_encode_with_delimiter("", ':'); EXPECT_TRUE(encoded.empty()); - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, ':'); + dec_res_ = hex_decode_with_delimiter(std::span(decoded_), encoded, ':'); ASSERT_EQ(0U, dec_res_); } // Test that decoding into a too-small output buffer fails. TEST_F(HexEncodeTest, TestDecodeTooShort) { dec_res_ = - hex_decode_with_delimiter(ArrayView(decoded_, 4), "0123456789", 0); + hex_decode_with_delimiter(std::span(decoded_, 4), "0123456789", 0); ASSERT_EQ(0U, dec_res_); ASSERT_EQ(0x7f, decoded_[4]); } // Test that decoding non-hex data fails. TEST_F(HexEncodeTest, TestDecodeBogusData) { - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), "axyz", 0); + dec_res_ = hex_decode_with_delimiter(std::span(decoded_), "axyz", 0); ASSERT_EQ(0U, dec_res_); } // Test that decoding an odd number of hex characters fails. TEST_F(HexEncodeTest, TestDecodeOddHexDigits) { - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), "012", 0); + dec_res_ = hex_decode_with_delimiter(std::span(decoded_), "012", 0); ASSERT_EQ(0U, dec_res_); } // Test that decoding a string with too many delimiters fails. TEST_F(HexEncodeTest, TestDecodeWithDelimiterTooManyDelimiters) { - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_, 4), + dec_res_ = hex_decode_with_delimiter(std::span(decoded_, 4), "01::23::45::67", ':'); ASSERT_EQ(0U, dec_res_); } // Test that decoding a string with a leading delimiter fails. TEST_F(HexEncodeTest, TestDecodeWithDelimiterLeadingDelimiter) { - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_, 4), + dec_res_ = hex_decode_with_delimiter(std::span(decoded_, 4), ":01:23:45:67", ':'); ASSERT_EQ(0U, dec_res_); } // Test that decoding a string with a trailing delimiter fails. TEST_F(HexEncodeTest, TestDecodeWithDelimiterTrailingDelimiter) { - dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_, 4), + dec_res_ = hex_decode_with_delimiter(std::span(decoded_, 4), "01:23:45:67:", ':'); ASSERT_EQ(0U, dec_res_); } diff --git a/rtc_base/strings/string_builder.cc b/rtc_base/strings/string_builder.cc index 094bbd19394..af8e7e85c20 100644 --- a/rtc_base/strings/string_builder.cc +++ b/rtc_base/strings/string_builder.cc @@ -13,18 +13,18 @@ #include #include #include +#include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_minmax.h" namespace webrtc { -SimpleStringBuilder::SimpleStringBuilder(ArrayView buffer) +SimpleStringBuilder::SimpleStringBuilder(std::span buffer) : buffer_(buffer) { buffer_[0] = '\0'; - RTC_DCHECK(IsConsistent()); + RTC_CHECK(IsConsistent()); } SimpleStringBuilder& SimpleStringBuilder::operator<<(char ch) { @@ -32,13 +32,13 @@ SimpleStringBuilder& SimpleStringBuilder::operator<<(char ch) { } SimpleStringBuilder& SimpleStringBuilder::operator<<(absl::string_view str) { - RTC_DCHECK_LT(size_ + str.length(), buffer_.size()) + RTC_CHECK_LT(size_ + str.length(), buffer_.size()) << "Buffer size was insufficient"; const size_t chars_added = SafeMin(str.length(), buffer_.size() - size_ - 1); memcpy(&buffer_[size_], str.data(), chars_added); size_ += chars_added; buffer_[size_] = '\0'; - RTC_DCHECK(IsConsistent()); + RTC_CHECK(IsConsistent()); return *this; } @@ -98,7 +98,7 @@ SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) { if (len >= 0) { const size_t chars_added = SafeMin(len, buffer_.size() - 1 - size_); size_ += chars_added; - RTC_DCHECK_EQ(len, chars_added) << "Buffer size was insufficient"; + RTC_CHECK_EQ(len, chars_added) << "Buffer size was insufficient"; } else { // This should never happen, but we're paranoid, so re-write the // terminator in case vsnprintf() overwrote it. @@ -106,7 +106,7 @@ SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) { buffer_[size_] = '\0'; } va_end(args); - RTC_DCHECK(IsConsistent()); + RTC_CHECK(IsConsistent()); return *this; } diff --git a/rtc_base/strings/string_builder.h b/rtc_base/strings/string_builder.h index 9f6fb33b4e2..4a3df25ba35 100644 --- a/rtc_base/strings/string_builder.h +++ b/rtc_base/strings/string_builder.h @@ -12,13 +12,13 @@ #define RTC_BASE_STRINGS_STRING_BUILDER_H_ #include +#include #include #include #include "absl/strings/has_absl_stringify.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" namespace webrtc { @@ -29,7 +29,7 @@ namespace webrtc { // read via `str()`. class SimpleStringBuilder { public: - explicit SimpleStringBuilder(ArrayView buffer); + explicit SimpleStringBuilder(std::span buffer); SimpleStringBuilder(const SimpleStringBuilder&) = delete; SimpleStringBuilder& operator=(const SimpleStringBuilder&) = delete; @@ -76,7 +76,7 @@ class SimpleStringBuilder { // size allows the buffer to be stack allocated, which helps performance. // Having a fixed size is furthermore useful to avoid unnecessary resizing // while building it. - const ArrayView buffer_; + const std::span buffer_; // Represents the number of characters written to the buffer. // This does not include the terminating '\0'. @@ -155,11 +155,8 @@ class StringBuilder { size_t size() const { return str_.size(); } - std::string Release() { - std::string ret = std::move(str_); - str_.clear(); - return ret; - } + // Moves out the internal std::string. + std::string Release() { return std::move(str_); } // Allows appending a printf style formatted string. StringBuilder& AppendFormat(const char* fmt, ...) diff --git a/rtc_base/strings/string_builder_unittest.cc b/rtc_base/strings/string_builder_unittest.cc index cb453130621..4764ac4ceaf 100644 --- a/rtc_base/strings/string_builder_unittest.cc +++ b/rtc_base/strings/string_builder_unittest.cc @@ -14,7 +14,6 @@ #include #include "absl/strings/string_view.h" -#include "rtc_base/checks.h" #include "test/gmock.h" #include "test/gtest.h" @@ -81,18 +80,13 @@ TEST(SimpleStringBuilder, CanUseAbslStringForCustomTypes) { // These tests are safe to run if we have death test support or if DCHECKs are // off. -#if (GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)) || !RTC_DCHECK_IS_ON +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) TEST(SimpleStringBuilderDeathTest, BufferOverrunConstCharP) { char sb_buf[4]; SimpleStringBuilder sb(sb_buf); const char* const msg = "This is just too much"; -#if RTC_DCHECK_IS_ON EXPECT_DEATH(sb << msg, ""); -#else - sb << msg; - EXPECT_THAT(sb.str(), ::testing::StrEq("Thi")); -#endif } TEST(SimpleStringBuilderDeathTest, BufferOverrunStdString) { @@ -100,41 +94,21 @@ TEST(SimpleStringBuilderDeathTest, BufferOverrunStdString) { SimpleStringBuilder sb(sb_buf); sb << 12; const std::string msg = "Aw, come on!"; -#if RTC_DCHECK_IS_ON EXPECT_DEATH(sb << msg, ""); -#else - sb << msg; - EXPECT_THAT(sb.str(), ::testing::StrEq("12A")); -#endif } TEST(SimpleStringBuilderDeathTest, BufferOverrunInt) { char sb_buf[4]; SimpleStringBuilder sb(sb_buf); constexpr int num = -12345; -#if RTC_DCHECK_IS_ON EXPECT_DEATH(sb << num, ""); -#else - sb << num; - // If we run into the end of the buffer, resonable results are either that - // the append has no effect or that it's truncated at the point where the - // buffer ends. - EXPECT_THAT(sb.str(), - ::testing::AnyOf(::testing::StrEq(""), ::testing::StrEq("-12"))); -#endif } TEST(SimpleStringBuilderDeathTest, BufferOverrunDouble) { char sb_buf[5]; SimpleStringBuilder sb(sb_buf); constexpr double num = 123.456; -#if RTC_DCHECK_IS_ON EXPECT_DEATH(sb << num, ""); -#else - sb << num; - EXPECT_THAT(sb.str(), - ::testing::AnyOf(::testing::StrEq(""), ::testing::StrEq("123."))); -#endif } TEST(SimpleStringBuilderDeathTest, BufferOverrunConstCharPAlreadyFull) { @@ -142,12 +116,7 @@ TEST(SimpleStringBuilderDeathTest, BufferOverrunConstCharPAlreadyFull) { SimpleStringBuilder sb(sb_buf); sb << 123; const char* const msg = "This is just too much"; -#if RTC_DCHECK_IS_ON EXPECT_DEATH(sb << msg, ""); -#else - sb << msg; - EXPECT_THAT(sb.str(), ::testing::StrEq("123")); -#endif } TEST(SimpleStringBuilderDeathTest, BufferOverrunIntAlreadyFull) { @@ -155,12 +124,7 @@ TEST(SimpleStringBuilderDeathTest, BufferOverrunIntAlreadyFull) { SimpleStringBuilder sb(sb_buf); sb << "xyz"; constexpr int num = -12345; -#if RTC_DCHECK_IS_ON EXPECT_DEATH(sb << num, ""); -#else - sb << num; - EXPECT_THAT(sb.str(), ::testing::StrEq("xyz")); -#endif } #endif diff --git a/rtc_base/strong_alias.h b/rtc_base/strong_alias.h index 548253de6ca..8071a7e4438 100644 --- a/rtc_base/strong_alias.h +++ b/rtc_base/strong_alias.h @@ -68,6 +68,13 @@ class StrongAlias { protected: UnderlyingType value_; + + private: + // Helper function for using abseil hash types. + template + friend H AbslHashValue(H h, const StrongAlias& st) { + return H::combine(std::move(h), st.value_); + } }; } // namespace webrtc diff --git a/rtc_base/synchronization/DEPS b/rtc_base/synchronization/DEPS index 4ed1f2444bc..f3806553a76 100644 --- a/rtc_base/synchronization/DEPS +++ b/rtc_base/synchronization/DEPS @@ -1,11 +1,11 @@ specific_include_rules = { - "mutex_abseil\.h": [ + "mutex_abseil\\.h": [ "+absl/synchronization" ], - ".*_benchmark\.cc": [ + ".*_benchmark\\.cc": [ "+benchmark", ], - ".*_unittest\.cc": [ + ".*_unittest\\.cc": [ "+benchmark", ] } diff --git a/rtc_base/system/BUILD.gn b/rtc_base/system/BUILD.gn index c1181618e98..653466e5f1a 100644 --- a/rtc_base/system/BUILD.gn +++ b/rtc_base/system/BUILD.gn @@ -58,6 +58,10 @@ rtc_source_set("unused") { sources = [ "unused.h" ] } +rtc_source_set("plan_b_only") { + sources = [ "plan_b_only.h" ] +} + rtc_source_set("assume") { sources = [ "assume.h" ] } diff --git a/rtc_base/system/DEPS b/rtc_base/system/DEPS index ab9449f70a9..eaf2d4c2bef 100644 --- a/rtc_base/system/DEPS +++ b/rtc_base/system/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - "warn_current_thread_is_deadlocked\.cc": [ + "warn_current_thread_is_deadlocked\\.cc": [ "+sdk/android/native_api/stacktrace/stacktrace.h", ], } diff --git a/rtc_base/system/plan_b_only.h b/rtc_base/system/plan_b_only.h new file mode 100644 index 00000000000..aa5fd0326ad --- /dev/null +++ b/rtc_base/system/plan_b_only.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_SYSTEM_PLAN_B_ONLY_H_ +#define RTC_BASE_SYSTEM_PLAN_B_ONLY_H_ + +#if defined(WEBRTC_DEPRECATE_PLAN_B) +#define PLAN_B_ONLY [[deprecated]] +#else +#define PLAN_B_ONLY +#endif + +#ifdef __clang__ +#define RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#define RTC_ALLOW_PLAN_B_DEPRECATION_END() _Pragma("clang diagnostic pop") +#elif defined(_MSC_VER) +#define RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() \ + __pragma(warning(push)) __pragma(warning(disable : 4996)) +#define RTC_ALLOW_PLAN_B_DEPRECATION_END() __pragma(warning(pop)) +#else +#define RTC_ALLOW_PLAN_B_DEPRECATION_BEGIN() +#define RTC_ALLOW_PLAN_B_DEPRECATION_END() +#endif + +#endif // RTC_BASE_SYSTEM_PLAN_B_ONLY_H_ diff --git a/rtc_base/task_queue_for_test.h b/rtc_base/task_queue_for_test.h index d4879e0ba68..8c153d6dc16 100644 --- a/rtc_base/task_queue_for_test.h +++ b/rtc_base/task_queue_for_test.h @@ -44,9 +44,9 @@ class TaskQueueForTest { public: explicit TaskQueueForTest( std::unique_ptr task_queue); - explicit TaskQueueForTest( - absl::string_view name = "TestQueue", - TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL); + explicit TaskQueueForTest(absl::string_view name = "TestQueue", + TaskQueueFactory::Priority priority = + TaskQueueFactory::Priority::kNormal); TaskQueueForTest(const TaskQueueForTest&) = delete; TaskQueueForTest& operator=(const TaskQueueForTest&) = delete; ~TaskQueueForTest(); diff --git a/rtc_base/task_queue_gcd.cc b/rtc_base/task_queue_gcd.cc index fb870e76370..887db42672e 100644 --- a/rtc_base/task_queue_gcd.cc +++ b/rtc_base/task_queue_gcd.cc @@ -31,13 +31,18 @@ namespace webrtc { namespace { +// TODO(crbug.com/470337728): make use of QoS instead of dispatch queue +// priorities to enable greater fidelity mapping of AUDIO and VIDEO +// priorities. int TaskQueuePriorityToGCD(TaskQueueFactory::Priority priority) { switch (priority) { - case TaskQueueFactory::Priority::NORMAL: + case TaskQueueFactory::Priority::kNormal: return DISPATCH_QUEUE_PRIORITY_DEFAULT; - case TaskQueueFactory::Priority::HIGH: + case TaskQueueFactory::Priority::kHigh: + case TaskQueueFactory::Priority::kAudio: + case TaskQueueFactory::Priority::kVideo: return DISPATCH_QUEUE_PRIORITY_HIGH; - case TaskQueueFactory::Priority::LOW: + case TaskQueueFactory::Priority::kLow: return DISPATCH_QUEUE_PRIORITY_LOW; } } diff --git a/rtc_base/task_queue_stdlib.cc b/rtc_base/task_queue_stdlib.cc index 7fdb1fa1c84..5595ff0fd93 100644 --- a/rtc_base/task_queue_stdlib.cc +++ b/rtc_base/task_queue_stdlib.cc @@ -37,12 +37,16 @@ namespace { ThreadPriority TaskQueuePriorityToThreadPriority( TaskQueueFactory::Priority priority) { switch (priority) { - case TaskQueueFactory::Priority::HIGH: + case TaskQueueFactory::Priority::kHigh: return ThreadPriority::kRealtime; - case TaskQueueFactory::Priority::LOW: + case TaskQueueFactory::Priority::kLow: return ThreadPriority::kLow; - case TaskQueueFactory::Priority::NORMAL: + case TaskQueueFactory::Priority::kNormal: return ThreadPriority::kNormal; + case TaskQueueFactory::Priority::kVideo: + return ThreadPriority::kVideo; + case TaskQueueFactory::Priority::kAudio: + return ThreadPriority::kAudio; } } diff --git a/rtc_base/task_queue_stdlib_unittest.cc b/rtc_base/task_queue_stdlib_unittest.cc index dc675910ad4..65a45eed4fe 100644 --- a/rtc_base/task_queue_stdlib_unittest.cc +++ b/rtc_base/task_queue_stdlib_unittest.cc @@ -54,7 +54,7 @@ TEST(TaskQueueStdlib, AvoidsSpammingLogOnInactivity) { StringPtrLogSink stream(&log_output); LogMessage::AddLogToStream(&stream, LS_VERBOSE); auto task_queue = CreateTaskQueueStdlibFactory()->CreateTaskQueue( - "test", TaskQueueFactory::Priority::NORMAL); + "test", TaskQueueFactory::Priority::kNormal); auto wait_duration = Event::kDefaultWarnDuration + TimeDelta::Seconds(1); Thread::SleepMs(wait_duration.ms()); EXPECT_EQ(log_output.length(), 0u); diff --git a/rtc_base/task_queue_unittest.cc b/rtc_base/task_queue_unittest.cc index 9ad911cfc73..c8096e99657 100644 --- a/rtc_base/task_queue_unittest.cc +++ b/rtc_base/task_queue_unittest.cc @@ -55,7 +55,7 @@ TEST(TaskQueueTest, DISABLED_PostDelayedHighRes) { static const char kQueueName[] = "PostDelayedHighRes"; Event event; - TaskQueueForTest queue(kQueueName, TaskQueueFactory::Priority::HIGH); + TaskQueueForTest queue(kQueueName, TaskQueueFactory::Priority::kHigh); uint32_t start = TimeMillis(); queue.PostDelayedTask( diff --git a/rtc_base/task_queue_win.cc b/rtc_base/task_queue_win.cc index 490ea8315d8..a453149d2d1 100644 --- a/rtc_base/task_queue_win.cc +++ b/rtc_base/task_queue_win.cc @@ -57,12 +57,16 @@ void CALLBACK InitializeQueueThread(ULONG_PTR param) { ThreadPriority TaskQueuePriorityToThreadPriority( TaskQueueFactory::Priority priority) { switch (priority) { - case TaskQueueFactory::Priority::HIGH: + case TaskQueueFactory::Priority::kHigh: return ThreadPriority::kRealtime; - case TaskQueueFactory::Priority::LOW: + case TaskQueueFactory::Priority::kLow: return ThreadPriority::kLow; - case TaskQueueFactory::Priority::NORMAL: + case TaskQueueFactory::Priority::kNormal: return ThreadPriority::kNormal; + case TaskQueueFactory::Priority::kVideo: + return ThreadPriority::kVideo; + case TaskQueueFactory::Priority::kAudio: + return ThreadPriority::kAudio; } } diff --git a/rtc_base/text2pcap.cc b/rtc_base/text2pcap.cc new file mode 100644 index 00000000000..8b0e73d20ec --- /dev/null +++ b/rtc_base/text2pcap.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "rtc_base/text2pcap.h" + +#include +#include +#include + +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { + +std::string Text2Pcap::DumpPacket(bool outbound, + std::span payload, + int64_t timestamp_ms) { + webrtc::StringBuilder s; + s << "\n" << (outbound ? "O " : "I "); + + int64_t remaining = timestamp_ms % (24 * 60 * 60 * kNumMillisecsPerSec); + int hours = remaining / (24 * 60 * 60 * kNumMillisecsPerSec); + remaining = remaining % (60 * 60 * kNumMillisecsPerSec); + int minutes = remaining / (60 * 60 * kNumMillisecsPerSec); + remaining = remaining % (60 * kNumMillisecsPerSec); + int seconds = remaining / kNumMillisecsPerSec; + int ms = remaining % kNumMillisecsPerSec; + s.AppendFormat("%02d:%02d:%02d.%03d", hours, minutes, seconds, ms); + s << " 0000"; + for (uint8_t byte : payload) { + s.AppendFormat(" %02x", byte); + } + return s.str(); +} + +} // namespace webrtc diff --git a/rtc_base/text2pcap.h b/rtc_base/text2pcap.h new file mode 100644 index 00000000000..5826e97ff4c --- /dev/null +++ b/rtc_base/text2pcap.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef RTC_BASE_TEXT2PCAP_H_ +#define RTC_BASE_TEXT2PCAP_H_ + +#include +#include +#include + +namespace webrtc { + +class Text2Pcap { + public: + // Dumps the packet in text2pcap format, returning the formatted string. + // The format is described on + // https://www.wireshark.org/docs/man-pages/text2pcap.html + // and resulting logs can be turned into a PCAP file that can be opened + // with the Wireshark tool using a command line along the lines off + // text2pcap -D -u 1000,2000 -t %H:%M:%S.%f log.txt out.pcap + // Returns the text2pcap formatted log which is typically prefixed with a + // newline and has a grep-able suffix (e.g. ` # SCTP_PACKET` or ` # RTP_DUMP`) + // for easy extraction from logs. + static std::string DumpPacket(bool outbound, + std::span payload, + int64_t timestamp_ms); +}; + +} // namespace webrtc + +#endif // RTC_BASE_TEXT2PCAP_H_ diff --git a/rtc_base/thread.cc b/rtc_base/thread.cc index be460f13084..81a7b6b6013 100644 --- a/rtc_base/thread.cc +++ b/rtc_base/thread.cc @@ -1,5 +1,5 @@ /* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * Copyright 2004 The WebRTC Project Authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -76,6 +77,7 @@ void* objc_autoreleasePoolPush(void); void objc_autoreleasePoolPop(void* pool); } +namespace webrtc { namespace { class ScopedAutoReleasePool { public: @@ -86,11 +88,11 @@ class ScopedAutoReleasePool { void* const pool_; }; } // namespace +} // namespace webrtc #endif namespace webrtc { - ThreadManager* ThreadManager::Instance() { static ThreadManager* const thread_manager = new ThreadManager(); return thread_manager; @@ -389,7 +391,7 @@ void Thread::DoDestroy() { ThreadManager::Remove(this); // Clear. CurrentTaskQueueSetter set_current(this); - messages_ = {}; + messages_.clear(); delayed_messages_ = {}; } @@ -436,13 +438,13 @@ absl::AnyInvocable Thread::Get(int cmsWait) { TimeDiff(delayed_messages_.top().run_time_ms, msCurrent); break; } - messages_.push(std::move(delayed_messages_.top().functor)); + messages_.push_back(std::move(delayed_messages_.top().functor)); delayed_messages_.pop(); } // Pull a message off the message queue, if available. if (!messages_.empty()) { absl::AnyInvocable task = std::move(messages_.front()); - messages_.pop(); + messages_.pop_front(); return task; } } @@ -494,9 +496,9 @@ void Thread::PostTaskImpl(absl::AnyInvocable task, { MutexLock lock(&mutex_); - messages_.push(std::move(task)); + messages_.push_back(std::move(task)); + WakeUpSocketServer(); } - WakeUpSocketServer(); } void Thread::PostDelayedTaskImpl(absl::AnyInvocable task, @@ -524,8 +526,8 @@ void Thread::PostDelayedTaskImpl(absl::AnyInvocable task, // will be misordered, and then only briefly. This is probably ok. ++delayed_next_num_; RTC_DCHECK_NE(0, delayed_next_num_); + WakeUpSocketServer(); } - WakeUpSocketServer(); } int Thread::GetDelay() { @@ -762,8 +764,12 @@ void Thread::BlockingCallImpl(FunctionView functor, RTC_DCHECK(this->IsInvokeToThreadAllowed(this)); RTC_DCHECK_RUN_ON(this); could_be_blocking_call_count_++; + ++running_synchronous_blocking_call_count_; #endif functor(); +#if RTC_DCHECK_IS_ON + --running_synchronous_blocking_call_count_; +#endif return; } @@ -963,4 +969,15 @@ AutoSocketServerThread::~AutoSocketServerThread() { } } +bool Thread::HasPendingTasks() const { + RTC_DCHECK_RUN_ON(this); +#if RTC_DCHECK_IS_ON + // If you've hit this, then there's a cooperative task running from inside a + // blocking call. + RTC_DCHECK_EQ(running_synchronous_blocking_call_count_, 0); +#endif + MutexLock lock(&mutex_); + return !messages_.empty(); +} + } // namespace webrtc diff --git a/rtc_base/thread.h b/rtc_base/thread.h index feb8cf78c72..24372722b39 100644 --- a/rtc_base/thread.h +++ b/rtc_base/thread.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -316,6 +317,13 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public TaskQueueBase { // ProcessMessages occasionally. virtual void Run(); + // Returns true if there are pending tasks in the message queue. + // Cooperative tasks can use this to know if they should yield. + // If a task yields, it is up to the task itself how or if to + // continue the ongoing operation. Typically this can be handled + // by using PostTask() to queue up a continuation task. + bool HasPendingTasks() const; + // Convenience method to invoke a functor on another thread. // Blocks the current thread until execution is complete. // Ex: thread.BlockingCall([&] { result = MyFunctionReturningBool(); }); @@ -497,7 +505,7 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public TaskQueueBase { // Called by the ThreadManager when being unset as the current thread. void ClearCurrentTaskQueue(); - std::queue> messages_ RTC_GUARDED_BY(mutex_); + std::deque> messages_ RTC_GUARDED_BY(mutex_); std::priority_queue delayed_messages_ RTC_GUARDED_BY(mutex_); uint32_t delayed_next_num_ RTC_GUARDED_BY(mutex_); #if RTC_DCHECK_IS_ON @@ -545,6 +553,16 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public TaskQueueBase { friend class ThreadManager; int dispatch_warning_ms_ RTC_GUARDED_BY(this) = kSlowDispatchLoggingThreshold; + +#if RTC_DCHECK_IS_ON + // This is used to catch if a cooperative task ends up being called from + // within another task. If that happens, the risk is that a full yield won't + // actually happen, so this is to help with ensuring we catch when things + // don't run as expected since webrtc can be configured in many ways and + // sometimes virtual thread concepts such as worker and network threads, can + // map to the same thread object. + int running_synchronous_blocking_call_count_ RTC_GUARDED_BY(this) = 0; +#endif }; // AutoThread automatically installs itself at construction diff --git a/rtc_base/thread_unittest.cc b/rtc_base/thread_unittest.cc index f8b031523a9..ce011e7a296 100644 --- a/rtc_base/thread_unittest.cc +++ b/rtc_base/thread_unittest.cc @@ -10,10 +10,12 @@ #include "rtc_base/thread.h" +#include #include #include #include #include +#include #include #include @@ -26,6 +28,7 @@ #include "api/units/time_delta.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_udp_socket.h" +#include "rtc_base/byte_order.h" #include "rtc_base/checks.h" #include "rtc_base/event.h" #include "rtc_base/fake_clock.h" @@ -42,6 +45,7 @@ #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" #include "test/testsupport/rtc_expect_death.h" #include "test/wait_until.h" @@ -81,8 +85,9 @@ class MessageClient : public TestGenerator { ~MessageClient() { delete socket_; } void OnValue(int value) { - int result = Next(value); - EXPECT_GE(socket_->Send(&result, sizeof(result)), 0); + std::array octets; + SetLE32(octets, Next(value)); + EXPECT_GE(socket_->Send(octets.data(), octets.size()), 0); } private: @@ -113,8 +118,7 @@ class SocketClient : public TestGenerator { void OnPacket(AsyncPacketSocket* socket, const ReceivedIpPacket& packet) { EXPECT_EQ(packet.payload().size(), sizeof(uint32_t)); - uint32_t prev = - reinterpret_cast(packet.payload().data())[0]; + uint32_t prev = GetLE32(packet.payload()); uint32_t result = Next(prev); post_thread_->PostDelayedTask([post_handler_ = post_handler_, @@ -869,6 +873,54 @@ TEST(ThreadPostDelayedTaskTest, IsCurrentTaskQueue) { EXPECT_EQ(TaskQueueBase::Current(), current_tq); } +// Uses `HasPendingTasks()` to detect when to yield to another posted task. +TEST(ThreadCooperativeTest, TaskTriggersHasPendingTasks) { + test::RunLoop loop; + auto thread = Thread::Create(); + thread->Start(); + + bool was_interrupted = false; + Event task_started; + + // Post a long running task that checks for pending tasks. + thread->PostTask( + [&was_interrupted, &loop, &task_started, thread = thread.get()] { + task_started.Set(); + while (!thread->HasPendingTasks()) { + // Busy loop/simulated work + } + loop.PostTask([&was_interrupted, &loop] { + was_interrupted = true; + loop.Quit(); + }); + }); + + // Wait for the task to start to ensure that the task doesn't + // run first. + task_started.Wait(Event::kForever); + + // Post a task that interrupts the busy loop. + thread->PostTask([] {}); + + loop.Run(); + EXPECT_TRUE(was_interrupted); +} + +TEST(ThreadCooperativeTest, HasPendingTasksClearedAfterTask) { + std::unique_ptr thread(Thread::Create()); + thread->Start(); + + // Initially false. + thread->BlockingCall([&] { EXPECT_FALSE(thread->HasPendingTasks()); }); + + // Post task. + thread->PostTask([&] {}); + + // Use `BlockingCall` to post normal task which implicitly blocks and waits + // for its functor to run, at which point the queue will be empty again. + thread->BlockingCall([&] { EXPECT_FALSE(thread->HasPendingTasks()); }); +} + class ThreadFactory : public TaskQueueFactory { public: std::unique_ptr CreateTaskQueue( diff --git a/rtc_base/timestamp_aligner_unittest.cc b/rtc_base/timestamp_aligner_unittest.cc index d8df07a0c1c..fad4274bf09 100644 --- a/rtc_base/timestamp_aligner_unittest.cc +++ b/rtc_base/timestamp_aligner_unittest.cc @@ -13,10 +13,12 @@ #include #include #include -#include +#include +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/random.h" -#include "rtc_base/time_utils.h" +#include "system_wrappers/include/clock.h" #include "test/gtest.h" namespace webrtc { @@ -52,55 +54,60 @@ class TimestampAlignerForTest : public TimestampAligner { void TestTimestampFilter(double rel_freq_error) { TimestampAlignerForTest timestamp_aligner_for_test; TimestampAligner timestamp_aligner; - const int64_t kEpoch = 10000; - const int64_t kJitterUs = 5000; - const int64_t kIntervalUs = 33333; // 30 FPS + + constexpr Timestamp kSystemStart = Timestamp::Micros(123456); + SimulatedClock clock(kSystemStart); + + const Timestamp kEpoch = Timestamp::Micros(10000); + const TimeDelta kJitter = TimeDelta::Micros(5000); + const TimeDelta kInterval = TimeDelta::Micros(33333); // 30 FPS const int kWindowSize = 100; const int kNumFrames = 3 * kWindowSize; - int64_t interval_error_us = kIntervalUs * rel_freq_error; - int64_t system_start_us = TimeMicros(); + TimeDelta interval_error = kInterval * rel_freq_error; Random random(17); - int64_t prev_translated_time_us = system_start_us; + Timestamp prev_translated_time = kSystemStart; for (int i = 0; i < kNumFrames; i++) { // Camera time subject to drift. - int64_t camera_time_us = kEpoch + i * (kIntervalUs + interval_error_us); - int64_t system_time_us = system_start_us + i * kIntervalUs; + Timestamp camera_time = kEpoch + i * (kInterval + interval_error); + Timestamp system_time = kSystemStart + i * kInterval; // And system time readings are subject to jitter. - int64_t system_measured_us = system_time_us + random.Rand(kJitterUs); + Timestamp system_measured = + system_time + TimeDelta::Micros(random.Rand(kJitter.us())); int64_t offset_us = timestamp_aligner_for_test.UpdateOffset( - camera_time_us, system_measured_us); + camera_time.us(), system_measured.us()); - int64_t filtered_time_us = camera_time_us + offset_us; - int64_t translated_time_us = timestamp_aligner_for_test.ClipTimestamp( - filtered_time_us, system_measured_us); + Timestamp filtered_time = camera_time + TimeDelta::Micros(offset_us); + Timestamp translated_time = + Timestamp::Micros(timestamp_aligner_for_test.ClipTimestamp( + filtered_time.us(), system_measured.us())); // Check that we get identical result from the all-in-one helper method. - ASSERT_EQ(translated_time_us, timestamp_aligner.TranslateTimestamp( - camera_time_us, system_measured_us)); + ASSERT_EQ(translated_time.us(), + timestamp_aligner.TranslateTimestamp(camera_time.us(), + system_measured.us())); - EXPECT_LE(translated_time_us, system_measured_us); - EXPECT_GE(translated_time_us, - prev_translated_time_us + kNumMicrosecsPerMillisec); + EXPECT_LE(translated_time, system_measured); + EXPECT_GE(translated_time, prev_translated_time + TimeDelta::Millis(1)); // The relative frequency error contributes to the expected error // by a factor which is the difference between the current time // and the average of earlier sample times. - int64_t expected_error_us = - kJitterUs / 2 + - rel_freq_error * kIntervalUs * MeanTimeDifference(i, kWindowSize); + TimeDelta expected_error = + kJitter / 2 + + rel_freq_error * kInterval * MeanTimeDifference(i, kWindowSize); - int64_t bias_us = filtered_time_us - translated_time_us; - EXPECT_GE(bias_us, 0); + TimeDelta bias = filtered_time - translated_time; + EXPECT_GE(bias, TimeDelta::Zero()); if (i == 0) { - EXPECT_EQ(translated_time_us, system_measured_us); + EXPECT_EQ(translated_time, system_measured); } else { - EXPECT_NEAR(filtered_time_us, system_time_us + expected_error_us, - 2.0 * kJitterUs / sqrt(std::max(i, kWindowSize))); + EXPECT_NEAR(filtered_time.us(), (system_time + expected_error).us(), + 2.0 * kJitter.us() / sqrt(std::max(i, kWindowSize))); } // If the camera clock runs too fast (rel_freq_error > 0.0), The // bias is expected to roughly cancel the expected error from the @@ -108,11 +115,11 @@ void TestTimestampFilter(double rel_freq_error) { // measurement noise. The tolerances here were selected after some // trial and error. if (i < 10 || rel_freq_error <= 0.0) { - EXPECT_LE(bias_us, 3000); + EXPECT_LE(bias, TimeDelta::Micros(3000)); } else { - EXPECT_NEAR(bias_us, expected_error_us, 1500); + EXPECT_NEAR(bias.us(), expected_error.us(), 1500); } - prev_translated_time_us = translated_time_us; + prev_translated_time = translated_time; } } @@ -152,36 +159,40 @@ TEST(TimestampAlignerTest, ClipToMonotonous) { // {0, c1, c1 + c2}, we exhibit non-monotonous behaviour if and only // if c1 > s1 + 2 s2 + 4 c2. const int kNumSamples = 3; - const int64_t kCaptureTimeUs[kNumSamples] = {0, 80000, 90001}; - const int64_t kSystemTimeUs[kNumSamples] = {0, 10000, 20000}; - const int64_t expected_offset_us[kNumSamples] = {0, -35000, -46667}; + const Timestamp kCaptureTime[kNumSamples] = { + Timestamp::Micros(0), Timestamp::Micros(80000), Timestamp::Micros(90001)}; + const Timestamp kSystemTime[kNumSamples] = { + Timestamp::Micros(0), Timestamp::Micros(10000), Timestamp::Micros(20000)}; + const TimeDelta expected_offset[kNumSamples] = {TimeDelta::Micros(0), + TimeDelta::Micros(-35000), + TimeDelta::Micros(-46667)}; // Non-monotonic translated timestamps can happen when only for // translated timestamps in the future. Which is tolerated if // `timestamp_aligner.clip_bias_us` is large enough. Instead of // changing that private member for this test, just add the bias to // `kSystemTimeUs` when calling ClipTimestamp. - const int64_t kClipBiasUs = 100000; + const TimeDelta kClipBias = TimeDelta::Micros(100000); bool did_clip = false; - int64_t prev_timestamp_us = std::numeric_limits::min(); + std::optional prev_timestamp; for (int i = 0; i < kNumSamples; i++) { - int64_t offset_us = - timestamp_aligner.UpdateOffset(kCaptureTimeUs[i], kSystemTimeUs[i]); - EXPECT_EQ(offset_us, expected_offset_us[i]); - - int64_t translated_timestamp_us = kCaptureTimeUs[i] + offset_us; - int64_t clip_timestamp_us = timestamp_aligner.ClipTimestamp( - translated_timestamp_us, kSystemTimeUs[i] + kClipBiasUs); - if (translated_timestamp_us <= prev_timestamp_us) { + TimeDelta offset = TimeDelta::Micros(timestamp_aligner.UpdateOffset( + kCaptureTime[i].us(), kSystemTime[i].us())); + EXPECT_EQ(offset, expected_offset[i]); + + Timestamp translated_timestamp = kCaptureTime[i] + offset; + Timestamp clip_timestamp = + Timestamp::Micros(timestamp_aligner.ClipTimestamp( + translated_timestamp.us(), (kSystemTime[i] + kClipBias).us())); + if (prev_timestamp && translated_timestamp <= *prev_timestamp) { did_clip = true; - EXPECT_EQ(clip_timestamp_us, - prev_timestamp_us + kNumMicrosecsPerMillisec); + EXPECT_EQ(clip_timestamp, *prev_timestamp + TimeDelta::Millis(1)); } else { // No change from clipping. - EXPECT_EQ(clip_timestamp_us, translated_timestamp_us); + EXPECT_EQ(clip_timestamp, translated_timestamp); } - prev_timestamp_us = clip_timestamp_us; + prev_timestamp = clip_timestamp; } EXPECT_TRUE(did_clip); } @@ -190,17 +201,23 @@ TEST(TimestampAlignerTest, TranslateTimestampWithoutStateUpdate) { TimestampAligner timestamp_aligner; constexpr int kNumSamples = 4; - constexpr int64_t kCaptureTimeUs[kNumSamples] = {0, 80000, 90001, 100000}; - constexpr int64_t kSystemTimeUs[kNumSamples] = {0, 10000, 20000, 30000}; - constexpr int64_t kQueryCaptureTimeOffsetUs[kNumSamples] = {0, 123, -321, - 345}; + constexpr Timestamp kCaptureTime[kNumSamples] = { + Timestamp::Micros(0), Timestamp::Micros(80000), Timestamp::Micros(90001), + Timestamp::Micros(100000)}; + constexpr Timestamp kSystemTime[kNumSamples] = { + Timestamp::Micros(0), Timestamp::Micros(10000), Timestamp::Micros(20000), + Timestamp::Micros(30000)}; + constexpr TimeDelta kQueryCaptureTimeOffset[kNumSamples] = { + TimeDelta::Micros(0), TimeDelta::Micros(123), TimeDelta::Micros(-321), + TimeDelta::Micros(345)}; for (int i = 0; i < kNumSamples; i++) { - int64_t reference_timestamp = timestamp_aligner.TranslateTimestamp( - kCaptureTimeUs[i], kSystemTimeUs[i]); - EXPECT_EQ(reference_timestamp - kQueryCaptureTimeOffsetUs[i], + Timestamp reference_timestamp = + Timestamp::Micros(timestamp_aligner.TranslateTimestamp( + kCaptureTime[i].us(), kSystemTime[i].us())); + EXPECT_EQ((reference_timestamp - kQueryCaptureTimeOffset[i]).us(), timestamp_aligner.TranslateTimestamp( - kCaptureTimeUs[i] - kQueryCaptureTimeOffsetUs[i])); + (kCaptureTime[i] - kQueryCaptureTimeOffset[i]).us())); } } diff --git a/rtc_base/trace_categories.h b/rtc_base/trace_categories.h index e6b3ab52730..45db2d55388 100644 --- a/rtc_base/trace_categories.h +++ b/rtc_base/trace_categories.h @@ -16,10 +16,13 @@ #define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 1 #include "rtc_base/system/rtc_export.h" -#include "third_party/perfetto/include/perfetto/tracing/track_event.h" // IWYU pragma: export -#include "third_party/perfetto/include/perfetto/tracing/track_event_category_registry.h" -#include "third_party/perfetto/include/perfetto/tracing/track_event_legacy.h" // IWYU pragma: export - +// IWYU pragma: begin_exports +#include "third_party/perfetto/include/perfetto/tracing/track_event.h" // nogncheck +// IWYU pragma: end_exports +#include "third_party/perfetto/include/perfetto/tracing/track_event_category_registry.h" // nogncheck +// IWYU pragma: begin_exports +#include "third_party/perfetto/include/perfetto/tracing/track_event_legacy.h" // nogncheck +// IWYU pragma: end_exports PERFETTO_DEFINE_TEST_CATEGORY_PREFIXES("webrtc-test"); PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS( diff --git a/rtc_base/trace_event.h b/rtc_base/trace_event.h index 9ec70884db4..067663b785d 100644 --- a/rtc_base/trace_event.h +++ b/rtc_base/trace_event.h @@ -21,10 +21,10 @@ // IWYU pragma: begin_exports #if defined(RTC_USE_PERFETTO) #include "rtc_base/trace_categories.h" +#include "third_party/perfetto/include/perfetto/tracing/event_context.h" // nogncheck +#include "third_party/perfetto/include/perfetto/tracing/track.h" // nogncheck +#include "third_party/perfetto/include/perfetto/tracing/track_event_args.h" // nogncheck #endif -#include "third_party/perfetto/include/perfetto/tracing/event_context.h" -#include "third_party/perfetto/include/perfetto/tracing/track.h" -#include "third_party/perfetto/include/perfetto/tracing/track_event_args.h" // IWYU pragma: end_exports #if !defined(RTC_USE_PERFETTO) diff --git a/rtc_base/unique_id_generator.cc b/rtc_base/unique_id_generator.cc index d09f35bedf6..a6cac6b3143 100644 --- a/rtc_base/unique_id_generator.cc +++ b/rtc_base/unique_id_generator.cc @@ -13,11 +13,11 @@ #include #include #include +#include #include #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/crypto_random.h" #include "rtc_base/string_to_number.h" @@ -26,7 +26,7 @@ namespace webrtc { UniqueRandomIdGenerator::UniqueRandomIdGenerator() : known_ids_() {} -UniqueRandomIdGenerator::UniqueRandomIdGenerator(ArrayView known_ids) +UniqueRandomIdGenerator::UniqueRandomIdGenerator(std::span known_ids) : known_ids_(known_ids.begin(), known_ids.end()) {} UniqueRandomIdGenerator::~UniqueRandomIdGenerator() = default; @@ -49,7 +49,7 @@ bool UniqueRandomIdGenerator::AddKnownId(uint32_t value) { } UniqueStringGenerator::UniqueStringGenerator() : unique_number_generator_() {} -UniqueStringGenerator::UniqueStringGenerator(ArrayView known_ids) { +UniqueStringGenerator::UniqueStringGenerator(std::span known_ids) { for (const std::string& str : known_ids) { AddKnownId(str); } diff --git a/rtc_base/unique_id_generator.h b/rtc_base/unique_id_generator.h index 64491c4eba9..4dd31ee261e 100644 --- a/rtc_base/unique_id_generator.h +++ b/rtc_base/unique_id_generator.h @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include "absl/strings/string_view.h" -#include "api/array_view.h" #include "api/sequence_checker.h" #include "rtc_base/checks.h" #include "rtc_base/synchronization/mutex.h" @@ -41,7 +41,7 @@ class UniqueNumberGenerator { typedef TIntegral value_type; UniqueNumberGenerator(); // Creates a generator that will never return any value from the given list. - explicit UniqueNumberGenerator(ArrayView known_ids); + explicit UniqueNumberGenerator(std::span known_ids); ~UniqueNumberGenerator(); // Generates a number that this generator has never produced before. @@ -74,7 +74,7 @@ class UniqueRandomIdGenerator { typedef uint32_t value_type; UniqueRandomIdGenerator(); // Create a generator that will never return any value from the given list. - explicit UniqueRandomIdGenerator(ArrayView known_ids); + explicit UniqueRandomIdGenerator(std::span known_ids); ~UniqueRandomIdGenerator(); // Generates a random id that this generator has never produced before. @@ -105,7 +105,7 @@ class UniqueStringGenerator { public: typedef std::string value_type; UniqueStringGenerator(); - explicit UniqueStringGenerator(ArrayView known_ids); + explicit UniqueStringGenerator(std::span known_ids); ~UniqueStringGenerator(); std::string GenerateString(); @@ -126,7 +126,7 @@ UniqueNumberGenerator::UniqueNumberGenerator() : counter_(0) {} template UniqueNumberGenerator::UniqueNumberGenerator( - ArrayView known_ids) + std::span known_ids) : counter_(0), known_ids_(known_ids.begin(), known_ids.end()) {} template diff --git a/rtc_base/virtual_socket_unittest.cc b/rtc_base/virtual_socket_unittest.cc index 9fadd190dd3..e3081b8c849 100644 --- a/rtc_base/virtual_socket_unittest.cc +++ b/rtc_base/virtual_socket_unittest.cc @@ -9,22 +9,26 @@ */ #include +#include #include +#include #include #include #include #include #include +#include #include +#include #include "absl/memory/memory.h" #include "api/environment/environment.h" #include "api/transport/ecn_marking.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_udp_socket.h" -#include "rtc_base/fake_clock.h" -#include "rtc_base/gunit.h" +#include "rtc_base/byte_order.h" #include "rtc_base/ip_address.h" #include "rtc_base/logging.h" #include "rtc_base/net_helpers.h" @@ -39,11 +43,15 @@ #include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" namespace webrtc { namespace { +using ::testing::ContainerEq; +using ::testing::Eq; using ::testing::NotNull; +using ::testing::Pointwise; using testing::SSE_CLOSE; using testing::SSE_ERROR; using testing::SSE_OPEN; @@ -70,8 +78,9 @@ struct Sender { uint32_t size = std::clamp(rate * delay / 1000, sizeof(uint32_t), 4096); count += size; - memcpy(dummy, &cur_time, sizeof(cur_time)); - socket->Send(dummy, size, options); + + SetLE64(dummy, static_cast(cur_time)); + socket->Send(dummy.data(), size, options); last_send = cur_time; return NextDelay(); @@ -91,7 +100,7 @@ struct Sender { uint32_t rate; // bytes per second uint32_t count; int64_t last_send; - char dummy[4096]; + std::array dummy; }; struct Receiver { @@ -133,9 +142,9 @@ struct Receiver { count += packet.payload().size(); sec_count += packet.payload().size(); - uint32_t send_time = - *reinterpret_cast(packet.payload().data()); - uint32_t recv_time = env.clock().TimeInMilliseconds(); + uint32_t send_time = GetLE32(packet.payload()); + uint32_t recv_time = + static_cast(env.clock().TimeInMilliseconds()); uint32_t delay = recv_time - send_time; sum += delay; sum_sq += delay * delay; @@ -158,8 +167,9 @@ struct Receiver { class VirtualSocketServerTest : public ::testing::Test { public: VirtualSocketServerTest() - : ss_(&fake_clock_), - thread_(&ss_), + : time_controller_(Timestamp::Millis(1000), &ss_), + env_(CreateTestEnvironment( + CreateTestEnvironmentOptions{.time = &time_controller_})), kIPv4AnyAddress(IPAddress(INADDR_ANY), 0), kIPv6AnyAddress(IPAddress(in6addr_any), 0) {} @@ -176,9 +186,10 @@ class VirtualSocketServerTest : public ::testing::Test { } else if (post_ip.family() == AF_INET6) { in6_addr post_ip6 = post_ip.ipv6_address(); in6_addr pre_ip6 = pre_ip.ipv6_address(); - uint32_t* post_as_ints = reinterpret_cast(&post_ip6.s6_addr); - uint32_t* pre_as_ints = reinterpret_cast(&pre_ip6.s6_addr); - EXPECT_EQ(post_as_ints[3], pre_as_ints[3]); + std::span post_bytes = post_ip6.s6_addr; + std::span pre_bytes = pre_ip6.s6_addr; + EXPECT_THAT(post_bytes.subspan(12, 4), + Pointwise(Eq(), pre_bytes.subspan(12, 4))); } } @@ -196,7 +207,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_TRUE(client1_any_addr.IsAnyIP()); TestClient client1( std::make_unique(env_, std::move(socket)), - &fake_clock_); + &time_controller_); // Create client2 bound to the address route. std::unique_ptr socket2 = @@ -206,7 +217,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_FALSE(client2_addr.IsAnyIP()); TestClient client2( std::make_unique(env_, std::move(socket2)), - &fake_clock_); + &time_controller_); // Client1 sends to client2, client2 should see the default address as // client1's address. @@ -231,14 +242,14 @@ class VirtualSocketServerTest : public ::testing::Test { TestClient client1( std::make_unique(env_, std::move(socket)), - &fake_clock_); + &time_controller_); SocketAddress client2_addr; { std::unique_ptr socket2 = ss_.Create(initial_addr.family(), SOCK_DGRAM); TestClient client2( std::make_unique(env_, std::move(socket2)), - &fake_clock_); + &time_controller_); EXPECT_EQ(3, client2.SendTo("foo", 3, server_addr)); EXPECT_TRUE(client1.CheckNextPacket("foo", 3, &client2_addr)); @@ -252,7 +263,7 @@ class VirtualSocketServerTest : public ::testing::Test { SocketAddress empty = EmptySocketAddressWithFamily(initial_addr.family()); for (int i = 0; i < 10; i++) { TestClient client2(AsyncUDPSocket::Create(env_, empty, ss_), - &fake_clock_); + &time_controller_); SocketAddress next_client2_addr; EXPECT_EQ(3, client2.SendTo("foo", 3, server_addr)); @@ -309,7 +320,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN)); EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE)); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // Client still connecting EXPECT_EQ(client->GetState(), Socket::CS_CONNECTING); @@ -328,7 +339,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(accepted->GetLocalAddress(), server->GetLocalAddress()); EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress()); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // Client has connected EXPECT_EQ(client->GetState(), Socket::CS_CONNECTED); @@ -359,7 +370,7 @@ class VirtualSocketServerTest : public ::testing::Test { // Attempt connect to non-listening socket EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // No pending server connections EXPECT_FALSE(sink.Check(server.get(), SSE_READ)); @@ -398,7 +409,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_FALSE(sink.Check(server.get(), SSE_READ)); server->Close(); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // Result: connection failed EXPECT_EQ(client->GetState(), Socket::CS_CLOSED); @@ -414,13 +425,13 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(0, server->Listen(5)); EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // Server close while socket is in accept queue EXPECT_TRUE(sink.Check(server.get(), SSE_READ)); server->Close(); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // Result: connection failed EXPECT_EQ(client->GetState(), Socket::CS_CLOSED); @@ -437,7 +448,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(0, server->Listen(5)); EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // Server accepts connection EXPECT_TRUE(sink.Check(server.get(), SSE_READ)); @@ -452,7 +463,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(client->GetState(), Socket::CS_CONNECTING); client->Close(); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // Result: accepted socket closes EXPECT_EQ(accepted->GetState(), Socket::CS_CLOSED); @@ -478,7 +489,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(0, a->Connect(b->GetLocalAddress())); EXPECT_EQ(0, b->Connect(a->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(sink.Check(a.get(), SSE_OPEN)); EXPECT_EQ(a->GetState(), Socket::CS_CONNECTED); @@ -492,7 +503,7 @@ class VirtualSocketServerTest : public ::testing::Test { b->Close(); EXPECT_EQ(1, a->Send("b", 1)); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); char buffer[10]; EXPECT_FALSE(sink.Check(b.get(), SSE_READ)); @@ -526,59 +537,62 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(0, a->Connect(b->GetLocalAddress())); EXPECT_EQ(0, b->Connect(a->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); const size_t kBufferSize = 2000; ss_.set_send_buffer_capacity(kBufferSize); ss_.set_recv_buffer_capacity(kBufferSize); const size_t kDataSize = 5000; - char send_buffer[kDataSize], recv_buffer[kDataSize]; + std::vector send_buffer(kDataSize); + std::vector recv_buffer(kDataSize); for (size_t i = 0; i < kDataSize; ++i) send_buffer[i] = static_cast(i % 256); - memset(recv_buffer, 0, sizeof(recv_buffer)); + std::fill(recv_buffer.begin(), recv_buffer.end(), 0); size_t send_pos = 0, recv_pos = 0; // Can't send more than send buffer in one write - int result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + int result = a->Send(&send_buffer[send_pos], kDataSize - send_pos); EXPECT_EQ(static_cast(kBufferSize), result); send_pos += result; - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_FALSE(sink.Check(a.get(), SSE_WRITE)); EXPECT_TRUE(sink.Check(b.get(), SSE_READ)); // Receive buffer is already filled, fill send buffer again - result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + result = a->Send(&send_buffer[send_pos], kDataSize - send_pos); EXPECT_EQ(static_cast(kBufferSize), result); send_pos += result; - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_FALSE(sink.Check(a.get(), SSE_WRITE)); EXPECT_FALSE(sink.Check(b.get(), SSE_READ)); // No more room in send or receive buffer - result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + result = a->Send(&send_buffer[send_pos], kDataSize - send_pos); EXPECT_EQ(-1, result); EXPECT_TRUE(a->IsBlocking()); // Read a subset of the data - result = b->Recv(recv_buffer + recv_pos, 500, nullptr); + result = b->Recv(&recv_buffer[recv_pos], 500, nullptr); EXPECT_EQ(500, result); recv_pos += result; - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(sink.Check(a.get(), SSE_WRITE)); EXPECT_TRUE(sink.Check(b.get(), SSE_READ)); // Room for more on the sending side - result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + result = a->Send(&send_buffer[send_pos], kDataSize - send_pos); EXPECT_EQ(500, result); send_pos += result; // Empty the recv buffer while (true) { - result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos, nullptr); + if (recv_pos >= kDataSize) + break; + result = b->Recv(&recv_buffer[recv_pos], kDataSize - recv_pos, nullptr); if (result < 0) { EXPECT_EQ(-1, result); EXPECT_TRUE(b->IsBlocking()); @@ -587,12 +601,14 @@ class VirtualSocketServerTest : public ::testing::Test { recv_pos += result; } - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(sink.Check(b.get(), SSE_READ)); // Continue to empty the recv buffer while (true) { - result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos, nullptr); + if (recv_pos >= kDataSize) + break; + result = b->Recv(&recv_buffer[recv_pos], kDataSize - recv_pos, nullptr); if (result < 0) { EXPECT_EQ(-1, result); EXPECT_TRUE(b->IsBlocking()); @@ -602,16 +618,18 @@ class VirtualSocketServerTest : public ::testing::Test { } // Send last of the data - result = a->Send(send_buffer + send_pos, kDataSize - send_pos); + result = a->Send(&send_buffer[send_pos], kDataSize - send_pos); EXPECT_EQ(500, result); send_pos += result; - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(sink.Check(b.get(), SSE_READ)); // Receive the last of the data while (true) { - result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos, nullptr); + if (recv_pos >= kDataSize) + break; + result = b->Recv(&recv_buffer[recv_pos], kDataSize - recv_pos, nullptr); if (result < 0) { EXPECT_EQ(-1, result); EXPECT_TRUE(b->IsBlocking()); @@ -620,13 +638,13 @@ class VirtualSocketServerTest : public ::testing::Test { recv_pos += result; } - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_FALSE(sink.Check(b.get(), SSE_READ)); // The received data matches the sent data EXPECT_EQ(kDataSize, send_pos); EXPECT_EQ(kDataSize, recv_pos); - EXPECT_EQ(0, memcmp(recv_buffer, send_buffer, kDataSize)); + EXPECT_EQ(recv_buffer, send_buffer); } void TcpSendsPacketsInOrderTest(const SocketAddress& initial_addr) { @@ -643,20 +661,20 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(0, a->Connect(b->GetLocalAddress())); EXPECT_EQ(0, b->Connect(a->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); // First, deliver all packets in 0 ms. - char buffer[2] = {0, 0}; + std::array buffer = {0, 0}; const char cNumPackets = 10; for (char i = 0; i < cNumPackets; ++i) { buffer[0] = '0' + i; - EXPECT_EQ(1, a->Send(buffer, 1)); + EXPECT_EQ(1, a->Send(buffer.data(), 1)); } - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); for (char i = 0; i < cNumPackets; ++i) { - EXPECT_EQ(1, b->Recv(buffer, sizeof(buffer), nullptr)); + EXPECT_EQ(1, b->Recv(buffer.data(), buffer.size(), nullptr)); EXPECT_EQ(static_cast('0' + i), buffer[0]); } @@ -670,13 +688,13 @@ class VirtualSocketServerTest : public ::testing::Test { for (char i = 0; i < cNumPackets; ++i) { buffer[0] = 'A' + i; - EXPECT_EQ(1, a->Send(buffer, 1)); + EXPECT_EQ(1, a->Send(buffer.data(), 1)); } - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Seconds(2)); for (char i = 0; i < cNumPackets; ++i) { - EXPECT_EQ(1, b->Recv(buffer, sizeof(buffer), nullptr)); + EXPECT_EQ(1, b->Recv(buffer.data(), buffer.size(), nullptr)); EXPECT_EQ(static_cast('A' + i), buffer[0]); } } @@ -698,15 +716,15 @@ class VirtualSocketServerTest : public ::testing::Test { uint32_t bandwidth = 64 * 1024; ss_.set_bandwidth(bandwidth); - Thread* pthMain = Thread::Current(); + Thread* pthMain = time_controller_.GetMainThread(); Sender sender(env_, pthMain, std::move(send_socket), 80 * 1024); Receiver receiver(env_, pthMain, std::move(recv_socket), bandwidth); // Allow the sender to run for 5 (simulated) seconds, then be stopped for 5 // seconds. - SIMULATED_WAIT(false, 5000, fake_clock_); + time_controller_.AdvanceTime(TimeDelta::Millis(5000)); sender.periodic.Stop(); - SIMULATED_WAIT(false, 5000, fake_clock_); + time_controller_.AdvanceTime(TimeDelta::Millis(5000)); // Ensure the observed bandwidth fell within a reasonable margin of error. EXPECT_TRUE(receiver.count >= 5 * 3 * bandwidth / 4); @@ -740,7 +758,7 @@ class VirtualSocketServerTest : public ::testing::Test { EXPECT_EQ(recv_socket->GetLocalAddress().family(), initial_addr.family()); ASSERT_EQ(0, send_socket->Connect(recv_socket->GetLocalAddress())); - Thread* pthMain = Thread::Current(); + Thread* pthMain = time_controller_.GetMainThread(); // Avg packet size is 2K, so at 200KB/s for 10s, we should see about // 1000 packets, which is necessary to get a good distribution. Sender sender(env_, pthMain, std::move(send_socket), 100 * 2 * 1024); @@ -748,10 +766,10 @@ class VirtualSocketServerTest : public ::testing::Test { // Simulate 10 seconds of packets being sent, then check the observed delay // distribution. - SIMULATED_WAIT(false, 10000, fake_clock_); + time_controller_.AdvanceTime(TimeDelta::Millis(10000)); sender.periodic.Stop(); receiver.periodic.Stop(); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); const double sample_mean = receiver.sum / receiver.samples; double num = @@ -800,19 +818,19 @@ class VirtualSocketServerTest : public ::testing::Test { if (shouldSucceed) { EXPECT_EQ(0, client->Connect(server->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(sink.Check(server.get(), SSE_READ)); std::unique_ptr accepted = absl::WrapUnique(server->Accept(&accept_address)); EXPECT_TRUE(nullptr != accepted); EXPECT_NE(kEmptyAddr, accept_address); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN)); EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress()); } else { // Check that the connection failed. EXPECT_EQ(-1, client->Connect(server->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_FALSE(sink.Check(server.get(), SSE_READ)); EXPECT_TRUE(nullptr == server->Accept(&accept_address)); @@ -834,13 +852,13 @@ class VirtualSocketServerTest : public ::testing::Test { SocketAddress bound_server_addr = socket->GetLocalAddress(); TestClient client1( std::make_unique(env_, std::move(socket)), - &fake_clock_); + &time_controller_); std::unique_ptr socket2 = ss_.Create(AF_INET, SOCK_DGRAM); socket2->Bind(client_addr); TestClient client2( std::make_unique(env_, std::move(socket2)), - &fake_clock_); + &time_controller_); SocketAddress client2_addr; if (shouldSucceed) { @@ -864,14 +882,14 @@ class VirtualSocketServerTest : public ::testing::Test { TestClient client1( std::make_unique(env_, std::move(socket)), - &fake_clock_); + &time_controller_); SocketAddress client2_addr; std::unique_ptr socket2 = ss_.Create(initial_addr.family(), SOCK_DGRAM); TestClient client2( std::make_unique(env_, std::move(socket2)), - &fake_clock_); + &time_controller_); client2.SendTo("foo", 3, server_addr); std::unique_ptr packet_1 = client1.NextPacket(); @@ -892,10 +910,9 @@ class VirtualSocketServerTest : public ::testing::Test { } protected: - ScopedFakeClock fake_clock_; - const Environment env_ = CreateTestEnvironment(); VirtualSocketServer ss_; - AutoSocketServerThread thread_; + GlobalSimulatedTimeController time_controller_; + const Environment env_; const SocketAddress kIPv4AnyAddress; const SocketAddress kIPv6AnyAddress; }; @@ -1087,7 +1104,7 @@ TEST_F(VirtualSocketServerTest, SetSendingBlockedWithUdpSocket) { socket1->Bind(kIPv4AnyAddress); socket2->Bind(kIPv4AnyAddress); TestClient client1(std::make_unique(env_, std::move(socket1)), - &fake_clock_); + &time_controller_); ss_.SetSendingBlocked(true); EXPECT_EQ(-1, client1.SendTo("foo", 3, socket2->GetLocalAddress())); @@ -1117,20 +1134,21 @@ TEST_F(VirtualSocketServerTest, SetSendingBlockedWithTcpSocket) { // Connect sockets. EXPECT_EQ(0, socket1->Connect(socket2->GetLocalAddress())); EXPECT_EQ(0, socket2->Connect(socket1->GetLocalAddress())); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); - char data[kBufferSize] = {}; + std::vector data(kBufferSize); // First Send call will fill the send buffer but not send anything. ss_.SetSendingBlocked(true); - EXPECT_EQ(static_cast(kBufferSize), socket1->Send(data, kBufferSize)); - ss_.ProcessMessagesUntilIdle(); + EXPECT_EQ(static_cast(kBufferSize), + socket1->Send(data.data(), kBufferSize)); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_FALSE(sink.Check(socket1.get(), SSE_WRITE)); EXPECT_FALSE(sink.Check(socket2.get(), SSE_READ)); EXPECT_FALSE(socket1->IsBlocking()); // Since the send buffer is full, next Send will result in EWOULDBLOCK. - EXPECT_EQ(-1, socket1->Send(data, kBufferSize)); + EXPECT_EQ(-1, socket1->Send(data.data(), kBufferSize)); EXPECT_FALSE(sink.Check(socket1.get(), SSE_WRITE)); EXPECT_FALSE(sink.Check(socket2.get(), SSE_READ)); EXPECT_TRUE(socket1->IsBlocking()); @@ -1138,7 +1156,7 @@ TEST_F(VirtualSocketServerTest, SetSendingBlockedWithTcpSocket) { // When sending is unblocked, the buffered data should be sent and // SignalWriteEvent should fire. ss_.SetSendingBlocked(false); - ss_.ProcessMessagesUntilIdle(); + time_controller_.AdvanceTime(TimeDelta::Zero()); EXPECT_TRUE(sink.Check(socket1.get(), SSE_WRITE)); EXPECT_TRUE(sink.Check(socket2.get(), SSE_READ)); } diff --git a/rtc_base/win/create_direct3d_device.cc b/rtc_base/win/create_direct3d_device.cc index 1854fc57cb2..ce9e003a160 100644 --- a/rtc_base/win/create_direct3d_device.cc +++ b/rtc_base/win/create_direct3d_device.cc @@ -12,6 +12,8 @@ #include +namespace webrtc { + namespace { FARPROC LoadD3D11Function(const char* function_name) { @@ -30,8 +32,6 @@ GetCreateDirect3D11DeviceFromDXGIDevice() { } // namespace -namespace webrtc { - bool ResolveCoreWinRTDirect3DDelayload() { return GetCreateDirect3D11DeviceFromDXGIDevice(); } diff --git a/rtc_base/win/get_activation_factory.cc b/rtc_base/win/get_activation_factory.cc index 0559fe7ac2a..b875f7bf1f8 100644 --- a/rtc_base/win/get_activation_factory.cc +++ b/rtc_base/win/get_activation_factory.cc @@ -15,6 +15,8 @@ #include "rtc_base/win/hstring.h" +namespace webrtc { + namespace { FARPROC LoadComBaseFunction(const char* function_name) { @@ -32,8 +34,6 @@ decltype(&::RoGetActivationFactory) GetRoGetActivationFactoryFunction() { } // namespace -namespace webrtc { - bool ResolveCoreWinRTDelayload() { return GetRoGetActivationFactoryFunction() && ResolveCoreWinRTStringDelayload(); diff --git a/rtc_base/win/hstring.cc b/rtc_base/win/hstring.cc index d92112f6c64..a16050493ff 100644 --- a/rtc_base/win/hstring.cc +++ b/rtc_base/win/hstring.cc @@ -15,6 +15,8 @@ #include +namespace webrtc { + namespace { FARPROC LoadComBaseFunction(const char* function_name) { @@ -39,8 +41,6 @@ decltype(&::WindowsDeleteString) GetWindowsDeleteString() { } // namespace -namespace webrtc { - bool ResolveCoreWinRTStringDelayload() { return GetWindowsDeleteString() && GetWindowsCreateString(); } diff --git a/rtc_base/win/windows_version.cc b/rtc_base/win/windows_version.cc index f059ea258a5..5c1c5581f1b 100644 --- a/rtc_base/win/windows_version.cc +++ b/rtc_base/win/windows_version.cc @@ -24,6 +24,8 @@ #if !defined(WINUWP) +namespace webrtc { + namespace { typedef BOOL(WINAPI* GetProductInfoPtr)(DWORD, DWORD, DWORD, DWORD, PDWORD); @@ -167,6 +169,8 @@ class RegKey { } // namespace +} // namespace webrtc + #endif // !defined(WINUWP) namespace webrtc { diff --git a/rtc_base/win32_unittest.cc b/rtc_base/win32_unittest.cc index 902f91532a6..863376f7668 100644 --- a/rtc_base/win32_unittest.cc +++ b/rtc_base/win32_unittest.cc @@ -12,7 +12,6 @@ #include -#include "rtc_base/gunit.h" #include "rtc_base/ip_address.h" #include "rtc_base/net_helpers.h" #include "test/gtest.h" diff --git a/rtc_base/zero_memory.h b/rtc_base/zero_memory.h index 6229a1e3f66..5af33ad8c81 100644 --- a/rtc_base/zero_memory.h +++ b/rtc_base/zero_memory.h @@ -13,10 +13,9 @@ #include +#include #include -#include "api/array_view.h" - namespace webrtc { // Fill memory with zeros in a way that the compiler doesn't optimize it away @@ -26,7 +25,7 @@ void ExplicitZeroMemory(void* ptr, size_t len); template ::value && std::is_trivial::value>::type* = nullptr> -void ExplicitZeroMemory(ArrayView a) { +void ExplicitZeroMemory(std::span a) { ExplicitZeroMemory(a.data(), a.size()); } diff --git a/rtc_base/zero_memory_unittest.cc b/rtc_base/zero_memory_unittest.cc index 5a8804c216a..dde2898d6c5 100644 --- a/rtc_base/zero_memory_unittest.cc +++ b/rtc_base/zero_memory_unittest.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "api/array_view.h" #include "test/gtest.h" namespace webrtc { @@ -30,13 +30,13 @@ TEST(ZeroMemoryTest, TestZeroMemory) { } } -TEST(ZeroMemoryTest, TestZeroArrayView) { +TEST(ZeroMemoryTest, TestZeroSpan) { static const size_t kBufferSize = 32; uint8_t buffer[kBufferSize]; for (size_t i = 0; i < kBufferSize; i++) { buffer[i] = static_cast(i + 1); } - ExplicitZeroMemory(ArrayView(buffer, sizeof(buffer))); + ExplicitZeroMemory(std::span(buffer, sizeof(buffer))); for (size_t i = 0; i < kBufferSize; i++) { EXPECT_EQ(buffer[i], 0); } diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index 2e31a4ef63f..b964e43859b 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -29,10 +29,7 @@ group("rtc_tools") { deps += [ ":tools_unittests" ] } if (rtc_include_tests && rtc_enable_protobuf) { - deps += [ - ":rtp_analyzer", - "network_tester", - ] + deps += [ "network_tester" ] } if (rtc_include_tests && rtc_enable_protobuf && !build_with_chromium) { deps += [ @@ -62,6 +59,7 @@ rtc_library("video_file_reader") { "../rtc_base:logging", "../rtc_base:refcount", "../rtc_base:stringutils", + "//third_party/abseil-cpp/absl/cleanup", "//third_party/abseil-cpp/absl/strings", ] } @@ -98,7 +96,6 @@ rtc_library("video_quality_analysis") { ] deps = [ ":video_file_reader", - "../api:array_view", "../api:make_ref_counted", "../api:scoped_refptr", "../api/numerics", @@ -141,7 +138,6 @@ if (!is_component_build) { ":video_file_reader", ":video_file_writer", ":video_quality_analysis", - "../api:array_view", "../api:make_ref_counted", "../api:scoped_refptr", "../api/test/metrics:chrome_perf_dashboard_metrics_exporter", @@ -196,7 +192,6 @@ if (!is_component_build) { "../rtc_base:rtc_json", "../rtc_base:stringutils", "../rtc_base:threading", - "../rtc_base/system:file_wrapper", "../system_wrappers", "../test:call_config_utils", "../test:encoder_settings", @@ -291,6 +286,12 @@ if (!build_with_chromium) { "rtc_event_log_visualizer/alerts.h", "rtc_event_log_visualizer/analyze_audio.cc", "rtc_event_log_visualizer/analyze_audio.h", + "rtc_event_log_visualizer/analyze_bwe.cc", + "rtc_event_log_visualizer/analyze_bwe.h", + "rtc_event_log_visualizer/analyze_connectivity.cc", + "rtc_event_log_visualizer/analyze_connectivity.h", + "rtc_event_log_visualizer/analyze_rtp_rtcp.cc", + "rtc_event_log_visualizer/analyze_rtp_rtcp.h", "rtc_event_log_visualizer/analyzer.cc", "rtc_event_log_visualizer/analyzer.h", "rtc_event_log_visualizer/analyzer_common.cc", @@ -307,6 +308,7 @@ if (!build_with_chromium) { "../api:candidate", "../api:dtls_transport_interface", "../api:field_trials", + "../api:field_trials_view", "../api:function_view", "../api:make_ref_counted", "../api:rtp_headers", @@ -417,7 +419,6 @@ if (!build_with_chromium) { "../api/video_codecs:scalability_mode", "../rtc_base:checks", "../rtc_base:stringutils", - "../rtc_base/system:file_wrapper", "//api:create_frame_generator", "//api:frame_generator_api", "//api/environment", @@ -619,17 +620,4 @@ if (rtc_include_tests) { } # unpack_aecdump } } - - if (rtc_enable_protobuf) { - copy("rtp_analyzer") { - sources = [ - "py_event_log_analyzer/misc.py", - "py_event_log_analyzer/pb_parse.py", - "py_event_log_analyzer/rtp_analyzer.py", - "py_event_log_analyzer/rtp_analyzer.sh", - ] - outputs = [ "$root_build_dir/{{source_file_part}}" ] - deps = [ "../logging:rtc_event_log_proto" ] - } # rtp_analyzer - } } diff --git a/rtc_tools/DEPS b/rtc_tools/DEPS index 49c1d01f1cb..2a5e29e31ea 100644 --- a/rtc_tools/DEPS +++ b/rtc_tools/DEPS @@ -21,7 +21,7 @@ include_rules = [ ] specific_include_rules = { - ".*ivf_converter\.cc": [ + ".*ivf_converter\\.cc": [ "+absl/debugging/failure_signal_handler.h", "+absl/debugging/symbolize.h", "+modules/video_coding/codecs/vp8/include/vp8.h", @@ -30,19 +30,19 @@ specific_include_rules = { "+modules/video_coding/utility/ivf_file_writer.h", "+modules/video_coding/codecs/h264/include/h264.h", ], - ".*video_replay\.cc": [ + ".*video_replay\\.cc": [ "+absl/strings/str_format.h", "+absl/strings/str_split.h", "+modules/video_coding/include/video_error_codes.h", "+modules/video_coding/utility/ivf_file_writer.h", ], - ".*video_encoder\.cc": [ + ".*video_encoder\\.cc": [ "+modules/video_coding/codecs/av1/av1_svc_config.h", "+modules/video_coding/include/video_codec_interface.h", "+modules/video_coding/include/video_error_codes.h", "+modules/video_coding/svc/scalability_mode_util.h", ], - ".*encoded_image_file_writer\.(cc|h)": [ + ".*encoded_image_file_writer\\.(cc|h)": [ "+modules/video_coding/include/video_codec_interface.h", "+modules/video_coding/svc/scalability_mode_util.h", "+modules/video_coding/utility/ivf_file_writer.h", diff --git a/rtc_tools/data_channel_benchmark/BUILD.gn b/rtc_tools/data_channel_benchmark/BUILD.gn index 43bdbc4dc80..f1aee05687e 100644 --- a/rtc_tools/data_channel_benchmark/BUILD.gn +++ b/rtc_tools/data_channel_benchmark/BUILD.gn @@ -25,6 +25,7 @@ rtc_library("grpc_signaling") { ":signaling_grpc_proto", ":signaling_interface", "../../api:jsep", + "../../api:sequence_checker", "../../rtc_base:checks", "../../rtc_base:logging", "../../rtc_base:threading", diff --git a/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc index e4a4f5aec0e..b78e51f0df3 100644 --- a/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc +++ b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc @@ -122,10 +122,10 @@ class DataChannelServerObserverImpl : public webrtc::DataChannelObserver { // Allow the transport buffer to be drained before starting again. if (buffer_ && dc_->buffered_amount() <= ok_to_resume_sending_threshold_) { total_queued_up_ += buffer_->size(); - dc_->SendAsync(*buffer_, [this, buffer = buffer_](webrtc::RTCError err) { - OnSendAsyncComplete(err, buffer); + dc_->SendAsync(*buffer_, [this, buffer = std::move(buffer_)]( + webrtc::RTCError err) mutable { + OnSendAsyncComplete(err, std::move(buffer)); }); - buffer_ = nullptr; } } @@ -142,29 +142,37 @@ class DataChannelServerObserverImpl : public webrtc::DataChannelObserver { void StartSending() { RTC_CHECK(remaining_data_) << "Error: no data to send"; std::string data(std::min(setup_.packet_size, remaining_data_), '0'); - webrtc::DataBuffer* data_buffer = - new webrtc::DataBuffer(webrtc::CopyOnWriteBuffer(data), true); + auto data_buffer = std::make_unique( + webrtc::CopyOnWriteBuffer(data), true); total_queued_up_ = data_buffer->size(); - dc_->SendAsync(*data_buffer, - [this, data_buffer = data_buffer](webrtc::RTCError err) { - OnSendAsyncComplete(err, data_buffer); - }); + dc_->SendAsync(*data_buffer, [this, data_buffer = std::move(data_buffer)]( + webrtc::RTCError err) mutable { + OnSendAsyncComplete(err, std::move(data_buffer)); + }); } const struct SetupMessage& parameters() const { return setup_; } private: - void OnSendAsyncComplete(webrtc::RTCError error, webrtc::DataBuffer* buffer) { + void OnSendAsyncComplete(webrtc::RTCError error, + std::unique_ptr buffer) { total_queued_up_ -= buffer->size(); if (!error.ok()) { - RTC_CHECK_EQ(error.type(), webrtc::RTCErrorType::RESOURCE_EXHAUSTED); + // If the error is NOT "Buffer Full" (Resource Exhausted), + // it means the network failed or client disconnected. We should stop. + if (error.type() != webrtc::RTCErrorType::RESOURCE_EXHAUSTED) { + RTC_LOG(LS_ERROR) << "Send failed with error: " + << ToString(error.type()) << ". Ending session."; + return; + } + + // If we are here, it IS Resource Exhausted, so we wait and retry. RTC_CHECK(!buffer_); - // Buffer saturated. Retry when OnBufferedAmountChange() detects we can. - buffer_ = buffer; + buffer_ = std::move(buffer); return; } - signaling_thread_->PostTask([this, buffer = buffer, - remaining_data = remaining_data_]() { + signaling_thread_->PostTask([this, buffer = std::move(buffer), + remaining_data = remaining_data_]() mutable { fprintf(stderr, "Progress: %zu / %zu (%zu%%)\n", (setup_.transfer_size - remaining_data), setup_.transfer_size, (100 - remaining_data * 100 / setup_.transfer_size)); @@ -172,7 +180,6 @@ class DataChannelServerObserverImpl : public webrtc::DataChannelObserver { if (!remaining_data) { RTC_CHECK(!total_queued_up_); // We're done. - delete buffer; return; } @@ -181,8 +188,9 @@ class DataChannelServerObserverImpl : public webrtc::DataChannelObserver { } total_queued_up_ += buffer->size(); - dc_->SendAsync(*buffer, [this, buffer = buffer](webrtc::RTCError err) { - OnSendAsyncComplete(err, buffer); + dc_->SendAsync(*buffer, [this, buffer = std::move(buffer)]( + webrtc::RTCError err) mutable { + OnSendAsyncComplete(err, std::move(buffer)); }); }); } @@ -194,7 +202,7 @@ class DataChannelServerObserverImpl : public webrtc::DataChannelObserver { size_t remaining_data_ = 0u; size_t total_queued_up_ = 0u; struct SetupMessage setup_; - webrtc::DataBuffer* buffer_ = nullptr; + std::unique_ptr buffer_; const uint64_t ok_to_resume_sending_threshold_ = webrtc::DataChannelInterface::MaxSendQueueSize() / 2; }; @@ -325,6 +333,10 @@ int RunClient() { absl::GetFlag(FLAGS_force_fieldtrials))); auto grpc_client = webrtc::GrpcSignalingClientInterface::Create( server_address + ":" + std::to_string(port)); + if (!grpc_client->Connect()) { + fprintf(stderr, "Failed to connect to server\n"); + return 1; + } webrtc::PeerConnectionClient client(factory.get(), grpc_client->signaling_client()); @@ -345,13 +357,12 @@ int RunClient() { // Connect to the server. if (!grpc_client->Start()) { - fprintf(stderr, "Failed to connect to server\n"); + fprintf(stderr, "Failed to start the benchmark threads\n"); return 1; } // Wait for the data channel to be received got_data_channel.Wait(webrtc::Event::kForever); - absl::Cleanup unregister_observer( [data_channel] { data_channel->UnregisterObserver(); }); diff --git a/rtc_tools/data_channel_benchmark/grpc_signaling.cc b/rtc_tools/data_channel_benchmark/grpc_signaling.cc index a92995fe55f..49a9b8d0017 100644 --- a/rtc_tools/data_channel_benchmark/grpc_signaling.cc +++ b/rtc_tools/data_channel_benchmark/grpc_signaling.cc @@ -23,6 +23,7 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "api/jsep.h" +#include "api/sequence_checker.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/thread.h" @@ -41,9 +42,7 @@ using GrpcSignaling::SignalingMessage; template class SessionData : public SignalingInterface { public: - SessionData() {} - explicit SessionData(T* stream) : stream_(stream) {} - void SetStream(T* stream) { stream_ = stream; } + explicit SessionData(T* stream) : stream_(stream) { RTC_CHECK(stream_); } void SendIceCandidate(const ::webrtc::IceCandidate* candidate) override { RTC_LOG(LS_INFO) << "SendIceCandidate"; @@ -53,7 +52,6 @@ class SessionData : public SignalingInterface { proto_candidate->set_description(serialized_candidate); proto_candidate->set_mid(candidate->sdp_mid()); proto_candidate->set_mline_index(candidate->sdp_mline_index()); - stream_->Write(message); } @@ -69,7 +67,6 @@ class SessionData : public SignalingInterface { else if (sdp->GetType() == SdpType::kAnswer) message.mutable_description()->set_type(SessionDescription::ANSWER); message.mutable_description()->set_content(serialized_sdp); - stream_->Write(message); } @@ -87,8 +84,7 @@ class SessionData : public SignalingInterface { ice_candidate_callback_ = callback; } - T* stream_; - + T* const stream_; std::function)> ice_candidate_callback_; std::function)> @@ -203,7 +199,6 @@ class GrpcNegotiationServer : public GrpcSignalingServerInterface, int requested_port_; int selected_port_; bool oneshot_; - std::unique_ptr server_; std::unique_ptr server_stop_thread_; }; @@ -220,28 +215,40 @@ class GrpcNegotiationClient : public GrpcSignalingClientInterface { if (reading_thread_) reading_thread_->Stop(); } - - bool Start() override { + bool Connect() override { + RTC_DCHECK_RUN_ON(&main_thread_); + RTC_CHECK(!session_); if (!channel_->WaitForConnected( absl::ToChronoTime(absl::Now() + absl::Seconds(3)))) { return false; } - stream_ = stub_->Connect(&context_); - session_.SetStream(stream_.get()); + session_ = std::make_unique(stream_.get()); + return true; + } + bool Start() override { + RTC_DCHECK_RUN_ON(&main_thread_); + if (!session_) { + RTC_DCHECK_NOTREACHED(); + return false; + } reading_thread_ = Thread::Create(); reading_thread_->Start(); reading_thread_->PostTask([this] { - ProcessMessages(stream_.get(), &session_); + ProcessMessages(stream_.get(), session_.get()); }); return true; } - SignalingInterface* signaling_client() override { return &session_; } + SignalingInterface* signaling_client() override { + RTC_DCHECK_RUN_ON(&main_thread_); + return session_.get(); + } private: + SequenceChecker main_thread_; std::shared_ptr channel_; std::unique_ptr stub_; std::unique_ptr reading_thread_; @@ -249,7 +256,7 @@ class GrpcNegotiationClient : public GrpcSignalingClientInterface { std::unique_ptr< ::grpc::ClientReaderWriter> stream_; - ClientSessionData session_; + std::unique_ptr session_; }; } // namespace diff --git a/rtc_tools/data_channel_benchmark/grpc_signaling.h b/rtc_tools/data_channel_benchmark/grpc_signaling.h index 8aefe5ae32f..735e576b1ae 100644 --- a/rtc_tools/data_channel_benchmark/grpc_signaling.h +++ b/rtc_tools/data_channel_benchmark/grpc_signaling.h @@ -52,6 +52,8 @@ class GrpcSignalingClientInterface { virtual ~GrpcSignalingClientInterface() = default; // Connect the client to the gRPC server. + virtual bool Connect() = 0; + // Starts the reading thread. Must be connected. virtual bool Start() = 0; virtual SignalingInterface* signaling_client() = 0; diff --git a/rtc_tools/data_channel_benchmark/peer_connection_client.cc b/rtc_tools/data_channel_benchmark/peer_connection_client.cc index 7be633151b1..37d6e956e51 100644 --- a/rtc_tools/data_channel_benchmark/peer_connection_client.cc +++ b/rtc_tools/data_channel_benchmark/peer_connection_client.cc @@ -41,18 +41,20 @@ #include "rtc_base/thread.h" #include "rtc_tools/data_channel_benchmark/signaling_interface.h" +namespace webrtc { + namespace { constexpr char kStunServer[] = "stun:stun.l.google.com:19302"; class SetLocalDescriptionObserverAdapter - : public webrtc::SetLocalDescriptionObserverInterface { + : public SetLocalDescriptionObserverInterface { public: - using Callback = std::function; - static webrtc::scoped_refptr Create( + using Callback = std::function; + static scoped_refptr Create( Callback callback) { - return webrtc::scoped_refptr( - new webrtc::RefCountedObject( + return scoped_refptr( + new RefCountedObject( std::move(callback))); } @@ -61,7 +63,7 @@ class SetLocalDescriptionObserverAdapter ~SetLocalDescriptionObserverAdapter() override = default; private: - void OnSetLocalDescriptionComplete(webrtc::RTCError error) override { + void OnSetLocalDescriptionComplete(RTCError error) override { callback_(std::move(error)); } @@ -69,13 +71,13 @@ class SetLocalDescriptionObserverAdapter }; class SetRemoteDescriptionObserverAdapter - : public webrtc::SetRemoteDescriptionObserverInterface { + : public SetRemoteDescriptionObserverInterface { public: - using Callback = std::function; - static webrtc::scoped_refptr Create( + using Callback = std::function; + static scoped_refptr Create( Callback callback) { - return webrtc::scoped_refptr( - new webrtc::RefCountedObject( + return scoped_refptr( + new RefCountedObject( std::move(callback))); } @@ -84,7 +86,7 @@ class SetRemoteDescriptionObserverAdapter ~SetRemoteDescriptionObserverAdapter() override = default; private: - void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { + void OnSetRemoteDescriptionComplete(RTCError error) override { callback_(std::move(error)); } @@ -92,16 +94,16 @@ class SetRemoteDescriptionObserverAdapter }; class CreateSessionDescriptionObserverAdapter - : public webrtc::CreateSessionDescriptionObserver { + : public CreateSessionDescriptionObserver { public: - using Success = std::function; - using Failure = std::function; + using Success = std::function; + using Failure = std::function; - static webrtc::scoped_refptr Create( + static scoped_refptr Create( Success success, Failure failure) { - return webrtc::scoped_refptr( - new webrtc::RefCountedObject( + return scoped_refptr( + new RefCountedObject( std::move(success), std::move(failure))); } @@ -110,13 +112,9 @@ class CreateSessionDescriptionObserverAdapter ~CreateSessionDescriptionObserverAdapter() override = default; private: - void OnSuccess(webrtc::SessionDescriptionInterface* desc) override { - success_(desc); - } + void OnSuccess(SessionDescriptionInterface* desc) override { success_(desc); } - void OnFailure(webrtc::RTCError error) override { - failure_(std::move(error)); - } + void OnFailure(RTCError error) override { failure_(std::move(error)); } Success success_; Failure failure_; @@ -124,8 +122,6 @@ class CreateSessionDescriptionObserverAdapter } // namespace -namespace webrtc { - PeerConnectionClient::PeerConnectionClient( PeerConnectionFactoryInterface* factory, SignalingInterface* signaling) diff --git a/rtc_tools/frame_analyzer/frame_analyzer.cc b/rtc_tools/frame_analyzer/frame_analyzer.cc index 71367c66403..05e1ac5d6dc 100644 --- a/rtc_tools/frame_analyzer/frame_analyzer.cc +++ b/rtc_tools/frame_analyzer/frame_analyzer.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/strings/match.h" -#include "api/array_view.h" #include "api/scoped_refptr.h" #include "api/test/metrics/chrome_perf_dashboard_metrics_exporter.h" #include "api/test/metrics/global_metrics_logger_and_exporter.h" @@ -138,7 +138,7 @@ class FrameAnalyzerMetricsExporter : public webrtc::test::MetricsExporter { FrameAnalyzerMetricsExporter& operator=(const FrameAnalyzerMetricsExporter&) = delete; - bool Export(webrtc::ArrayView metrics) override { + bool Export(std::span metrics) override { for (const webrtc::test::Metric& metric : metrics) { PrintMetric(metric); } diff --git a/rtc_tools/frame_analyzer/video_color_aligner.cc b/rtc_tools/frame_analyzer/video_color_aligner.cc index b4515883a09..f63a190d4a6 100644 --- a/rtc_tools/frame_analyzer/video_color_aligner.cc +++ b/rtc_tools/frame_analyzer/video_color_aligner.cc @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "api/array_view.h" #include "api/make_ref_counted.h" #include "api/scoped_refptr.h" #include "api/video/i420_buffer.h" @@ -36,11 +36,11 @@ namespace { // Helper function for AdjustColors(). This functions calculates a single output // row for y with the given color coefficients. The u/v channels are assumed to // be subsampled by a factor of 2, which is the case of I420. -void CalculateYChannel(ArrayView y_data, - ArrayView u_data, - ArrayView v_data, +void CalculateYChannel(std::span y_data, + std::span u_data, + std::span v_data, const std::array& coeff, - ArrayView output) { + std::span output) { RTC_CHECK_EQ(y_data.size(), output.size()); // Each u/v element represents two y elements. Make sure we have enough to // cover the Y values. @@ -75,11 +75,11 @@ void CalculateYChannel(ArrayView y_data, // Helper function for AdjustColors(). This functions calculates a single output // row for either u or v, with the given color coefficients. Y, U, and V are // assumed to be the same size, i.e. no subsampling. -void CalculateUVChannel(ArrayView y_data, - ArrayView u_data, - ArrayView v_data, +void CalculateUVChannel(std::span y_data, + std::span u_data, + std::span v_data, const std::array& coeff, - ArrayView output) { + std::span output) { RTC_CHECK_EQ(y_data.size(), u_data.size()); RTC_CHECK_EQ(y_data.size(), v_data.size()); RTC_CHECK_EQ(y_data.size(), output.size()); @@ -201,13 +201,13 @@ scoped_refptr AdjustColors( // Fill in the adjusted data row by row. for (int y = 0; y < frame->height(); ++y) { const int half_y = y / 2; - ArrayView y_row(frame->DataY() + frame->StrideY() * y, + std::span y_row(frame->DataY() + frame->StrideY() * y, frame->width()); - ArrayView u_row(frame->DataU() + frame->StrideU() * half_y, + std::span u_row(frame->DataU() + frame->StrideU() * half_y, frame->ChromaWidth()); - ArrayView v_row(frame->DataV() + frame->StrideV() * half_y, + std::span v_row(frame->DataV() + frame->StrideV() * half_y, frame->ChromaWidth()); - ArrayView output_y_row( + std::span output_y_row( adjusted_frame->MutableDataY() + adjusted_frame->StrideY() * y, frame->width()); @@ -215,13 +215,13 @@ scoped_refptr AdjustColors( // Chroma channels only exist every second row for I420. if (y % 2 == 0) { - ArrayView downscaled_y_row( + std::span downscaled_y_row( downscaled_y_plane.data() + frame->ChromaWidth() * half_y, frame->ChromaWidth()); - ArrayView output_u_row( + std::span output_u_row( adjusted_frame->MutableDataU() + adjusted_frame->StrideU() * half_y, frame->ChromaWidth()); - ArrayView output_v_row( + std::span output_v_row( adjusted_frame->MutableDataV() + adjusted_frame->StrideV() * half_y, frame->ChromaWidth()); diff --git a/rtc_tools/network_tester/BUILD.gn b/rtc_tools/network_tester/BUILD.gn index fd98be799b2..f39654771e0 100644 --- a/rtc_tools/network_tester/BUILD.gn +++ b/rtc_tools/network_tester/BUILD.gn @@ -90,7 +90,6 @@ if (rtc_enable_protobuf) { "../../api:rtc_error_matchers", "../../api/environment", "../../api/environment:environment_factory", - "../../rtc_base:gunit_helpers", "../../rtc_base:random", "../../rtc_base:threading", "../../rtc_base:timeutils", diff --git a/rtc_tools/py_event_log_analyzer/README.md b/rtc_tools/py_event_log_analyzer/README.md deleted file mode 100644 index 12f436c3d72..00000000000 --- a/rtc_tools/py_event_log_analyzer/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# RTP log analyzer -This file describes how to set up and use the RTP log analyzer. - -## Build - -```shell -$ autoninja -C out/my_build webrtc:rtp_analyzer -``` - -## Usage - -```shell -./out/my_build/rtp_analyzer.sh [options] /path/to/rtc_event.log -``` - -where `/path/to/rtc_event.log` is a recorded RTC event log, which is stored in -protobuf format. Such logs are generated in multiple ways, e.g. by Chrome -through the chrome://webrtc-internals page. - -Use `--help` for the options. - -The script requires Python (2.7 or 3+) and it has the following dependencies: -Dependencies (available on pip): -- matplotlib (http://matplotlib.org/) -- numpy (http://www.numpy.org/) -- protobuf (https://pypi.org/project/protobuf/) diff --git a/rtc_tools/py_event_log_analyzer/misc.py b/rtc_tools/py_event_log_analyzer/misc.py deleted file mode 100644 index c21f0c466be..00000000000 --- a/rtc_tools/py_event_log_analyzer/misc.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -"""Utility functions for calculating statistics. -""" - -from __future__ import division -import collections -import sys - - -def CountReordered(sequence_numbers): - """Returns number of reordered indices. - - A reordered index is an index `i` for which sequence_numbers[i] >= - sequence_numbers[i + 1] - """ - return sum(1 for (s1, s2) in zip(sequence_numbers, sequence_numbers[1:]) - if s1 >= s2) - - -def SsrcNormalizedSizeTable(data_points): - """Counts proportion of data for every SSRC. - - Args: - data_points: list of pb_parse.DataPoint - - Returns: - A dictionary mapping from every SSRC in the data points. The - value of an SSRC `s` is the proportion of sizes of packets with - SSRC `s` to the total size of all packets. - - """ - mapping = collections.defaultdict(int) - for point in data_points: - mapping[point.ssrc] += point.size - return NormalizeCounter(mapping) - - -def NormalizeCounter(counter): - """Returns a normalized version of the dictionary `counter`. - - Does not modify `counter`. - - Returns: - A new dictionary, in which every value in `counter` - has been divided by the total to sum up to 1. - """ - total = sum(counter.values()) - return {key: counter[key] / total for key in counter} - - -def Unwrap(data, mod): - """Returns `data` unwrapped modulo `mod`. Does not modify data. - - Adds integer multiples of mod to all elements of data except the - first, such that all pairs of consecutive elements (a, b) satisfy - -mod / 2 <= b - a < mod / 2. - - E.g. Unwrap([0, 1, 2, 0, 1, 2, 7, 8], 3) -> [0, 1, 2, 3, - 4, 5, 4, 5] - """ - lst = data[:] - for i in range(1, len(data)): - lst[i] = lst[i - 1] + (lst[i] - lst[i - 1] + mod // 2) % mod - (mod // - 2) - return lst - - -def SsrcDirections(data_points): - ssrc_is_incoming = {} - for point in data_points: - ssrc_is_incoming[point.ssrc] = point.incoming - return ssrc_is_incoming - - -# Python 2/3-compatible input function -if sys.version_info[0] <= 2: - get_input = raw_input # pylint: disable=invalid-name -else: - get_input = input # pylint: disable=invalid-name diff --git a/rtc_tools/py_event_log_analyzer/misc_test.py b/rtc_tools/py_event_log_analyzer/misc_test.py deleted file mode 100755 index 1ee17419a2b..00000000000 --- a/rtc_tools/py_event_log_analyzer/misc_test.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -"""Run the tests with - - python misc_test.py -or - python3 misc_test.py -""" - -from __future__ import division -from __future__ import absolute_import -import random -import unittest -from six.moves import range -from six.moves import zip - -import misc - -class TestMisc(unittest.TestCase): - def testUnwrapMod3(self): - data = [0, 1, 2, 0, -1, -2, -3, -4] - unwrapped_3 = misc.Unwrap(data, 3) - self.assertEqual([0, 1, 2, 3, 2, 1, 0, -1], unwrapped_3) - - def testUnwrapMod4(self): - data = [0, 1, 2, 0, -1, -2, -3, -4] - unwrapped_4 = misc.Unwrap(data, 4) - self.assertEqual([0, 1, 2, 0, -1, -2, -3, -4], unwrapped_4) - - def testDataShouldNotChangeAfterUnwrap(self): - data = [0, 1, 2, 0, -1, -2, -3, -4] - _ = misc.Unwrap(data, 4) - - self.assertEqual([0, 1, 2, 0, -1, -2, -3, -4], data) - - def testRandomlyMultiplesOfModAdded(self): - # `unwrap` definition says only multiples of mod are added. - random_data = [random.randint(0, 9) for _ in range(100)] - - for mod in range(1, 100): - random_data_unwrapped_mod = misc.Unwrap(random_data, mod) - - for (old_a, a) in zip(random_data, random_data_unwrapped_mod): - self.assertEqual((old_a - a) % mod, 0) - - def testRandomlyAgainstInequalityDefinition(self): - # Data has to satisfy -mod/2 <= difference < mod/2 for every - # difference between consecutive values after unwrap. - random_data = [random.randint(0, 9) for _ in range(100)] - - for mod in range(1, 100): - random_data_unwrapped_mod = misc.Unwrap(random_data, mod) - - for (a, b) in zip(random_data_unwrapped_mod, - random_data_unwrapped_mod[1:]): - self.assertTrue(-mod / 2 <= b - a < mod / 2) - - def testRandomlyDataShouldNotChangeAfterUnwrap(self): - random_data = [random.randint(0, 9) for _ in range(100)] - random_data_copy = random_data[:] - for mod in range(1, 100): - _ = misc.Unwrap(random_data, mod) - - self.assertEqual(random_data, random_data_copy) - - -if __name__ == "__main__": - unittest.main() diff --git a/rtc_tools/py_event_log_analyzer/pb_parse.py b/rtc_tools/py_event_log_analyzer/pb_parse.py deleted file mode 100644 index 23e6ae4487f..00000000000 --- a/rtc_tools/py_event_log_analyzer/pb_parse.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -"""Parses protobuf RTC dumps.""" - -from __future__ import division -import struct -import pyproto.logging.rtc_event_log.rtc_event_log_pb2 as rtc_pb - - -class DataPoint(object): - """Simple container class for RTP events.""" - - def __init__(self, rtp_header_str, packet_size, arrival_timestamp_us, - incoming): - """Builds a data point by parsing an RTP header, size and arrival time. - - RTP header structure is defined in RFC 3550 section 5.1. - """ - self.size = packet_size - self.arrival_timestamp_ms = arrival_timestamp_us / 1000 - self.incoming = incoming - header = struct.unpack_from("!HHII", rtp_header_str, 0) - (first2header_bytes, self.sequence_number, self.timestamp, - self.ssrc) = header - self.payload_type = first2header_bytes & 0b01111111 - self.marker_bit = (first2header_bytes & 0b10000000) >> 7 - - -def ParseProtobuf(file_path): - """Parses RTC event log from protobuf file. - - Args: - file_path: path to protobuf file of RTC event stream - - Returns: - all RTP packet events from the event stream as a list of DataPoints - """ - event_stream = rtc_pb.EventStream() - with open(file_path, "rb") as f: - event_stream.ParseFromString(f.read()) - - return [ - DataPoint(event.rtp_packet.header, event.rtp_packet.packet_length, - event.timestamp_us, event.rtp_packet.incoming) - for event in event_stream.stream if event.HasField("rtp_packet") - ] diff --git a/rtc_tools/py_event_log_analyzer/rtp_analyzer.py b/rtc_tools/py_event_log_analyzer/rtp_analyzer.py deleted file mode 100644 index 5844997877d..00000000000 --- a/rtc_tools/py_event_log_analyzer/rtp_analyzer.py +++ /dev/null @@ -1,340 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -"""Displays statistics and plots graphs from RTC protobuf dump.""" - -from __future__ import division -from __future__ import print_function - -from __future__ import absolute_import -import collections -import optparse -import os -import sys -from six.moves import range -from six.moves import zip - -import matplotlib.pyplot as plt -import numpy - -import misc -import pb_parse - - -class RTPStatistics: - """Has methods for calculating and plotting RTP stream statistics.""" - - BANDWIDTH_SMOOTHING_WINDOW_SIZE = 10 - PLOT_RESOLUTION_MS = 50 - - def __init__(self, data_points): - """Initializes object with data_points and computes simple statistics. - - Computes percentages of number of packets and packet sizes by - SSRC. - - Args: - data_points: list of pb_parse.DataPoints on which statistics are - calculated. - - """ - - self.data_points = data_points - self.ssrc_frequencies = misc.NormalizeCounter( - collections.Counter([pt.ssrc for pt in self.data_points])) - self.ssrc_size_table = misc.SsrcNormalizedSizeTable(self.data_points) - self.bandwidth_kbps = None - self.smooth_bw_kbps = None - - def PrintHeaderStatistics(self): - print("{:>6}{:>14}{:>14}{:>6}{:>6}{:>3}{:>11}".format( - "SeqNo", "TimeStamp", "SendTime", "Size", "PT", "M", "SSRC")) - for point in self.data_points: - print("{:>6}{:>14}{:>14}{:>6}{:>6}{:>3}{:>11}".format( - point.sequence_number, point.timestamp, - int(point.arrival_timestamp_ms), point.size, point.payload_type, - point.marker_bit, "0x{:x}".format(point.ssrc))) - - def PrintSsrcInfo(self, ssrc_id, ssrc): - """Prints packet and size statistics for a given SSRC. - - Args: - ssrc_id: textual identifier of SSRC printed beside statistics for it. - ssrc: SSRC by which to filter data and display statistics - """ - filtered_ssrc = [point for point in self.data_points if point.ssrc == ssrc] - payloads = misc.NormalizeCounter( - collections.Counter([point.payload_type for point in filtered_ssrc])) - - payload_info = "payload type(s): {}".format(", ".join( - str(payload) for payload in payloads)) - print("{} 0x{:x} {}, {:.2f}% packets, {:.2f}% data".format( - ssrc_id, ssrc, payload_info, self.ssrc_frequencies[ssrc] * 100, - self.ssrc_size_table[ssrc] * 100)) - print(" packet sizes:") - (bin_counts, - bin_bounds) = numpy.histogram([point.size for point in filtered_ssrc], - bins=5, - density=False) - bin_proportions = bin_counts / sum(bin_counts) - print("\n".join([ - " {:.1f} - {:.1f}: {:.2f}%".format(bin_bounds[i], bin_bounds[i + 1], - bin_proportions[i] * 100) - for i in range(len(bin_proportions)) - ])) - - def ChooseSsrc(self): - """Queries user for SSRC.""" - - if len(self.ssrc_frequencies) == 1: - chosen_ssrc = list(self.ssrc_frequencies.keys())[0] - self.PrintSsrcInfo("", chosen_ssrc) - return chosen_ssrc - - ssrc_is_incoming = misc.SsrcDirections(self.data_points) - incoming = [ssrc for ssrc in ssrc_is_incoming if ssrc_is_incoming[ssrc]] - outgoing = [ssrc for ssrc in ssrc_is_incoming if not ssrc_is_incoming[ssrc]] - - print("\nIncoming:\n") - for (i, ssrc) in enumerate(incoming): - self.PrintSsrcInfo(i, ssrc) - - print("\nOutgoing:\n") - for (i, ssrc) in enumerate(outgoing): - self.PrintSsrcInfo(i + len(incoming), ssrc) - - while True: - chosen_index = int(misc.get_input("choose one> ")) - if 0 <= chosen_index < len(self.ssrc_frequencies): - return (incoming + outgoing)[chosen_index] - print("Invalid index!") - - def FilterSsrc(self, chosen_ssrc): - """Filters and wraps data points. - - Removes data points with `ssrc != chosen_ssrc`. Unwraps sequence - numbers and timestamps for the chosen selection. - """ - self.data_points = [ - point for point in self.data_points if point.ssrc == chosen_ssrc - ] - unwrapped_sequence_numbers = misc.Unwrap( - [point.sequence_number for point in self.data_points], 2**16 - 1) - for (data_point, sequence_number) in zip(self.data_points, - unwrapped_sequence_numbers): - data_point.sequence_number = sequence_number - - unwrapped_timestamps = misc.Unwrap( - [point.timestamp for point in self.data_points], 2**32 - 1) - - for (data_point, timestamp) in zip(self.data_points, unwrapped_timestamps): - data_point.timestamp = timestamp - - def PrintSequenceNumberStatistics(self): - seq_no_set = set(point.sequence_number for point in self.data_points) - missing_sequence_numbers = max(seq_no_set) - min(seq_no_set) + ( - 1 - len(seq_no_set)) - print("Missing sequence numbers: {} out of {} ({:.2f}%)".format( - missing_sequence_numbers, len(seq_no_set), - 100 * missing_sequence_numbers / len(seq_no_set))) - print("Duplicated packets: {}".format( - len(self.data_points) - len(seq_no_set))) - print("Reordered packets: {}".format( - misc.CountReordered( - [point.sequence_number for point in self.data_points]))) - - def EstimateFrequency(self, always_query_sample_rate): - """Estimates frequency and updates data. - - Guesses the most probable frequency by looking at changes in - timestamps (RFC 3550 section 5.1), calculates clock drifts and - sending time of packets. Updates `self.data_points` with changes - in delay and send time. - """ - delta_timestamp = (self.data_points[-1].timestamp - - self.data_points[0].timestamp) - delta_arr_timestamp = float((self.data_points[-1].arrival_timestamp_ms - - self.data_points[0].arrival_timestamp_ms)) - freq_est = delta_timestamp / delta_arr_timestamp - - freq_vec = [8, 16, 32, 48, 90] - freq = None - for f in freq_vec: - if abs((freq_est - f) / f) < 0.05: - freq = f - - print("Estimated frequency: {:.3f}kHz".format(freq_est)) - if freq is None or always_query_sample_rate: - if not always_query_sample_rate: - print("Frequency could not be guessed.", end=" ") - freq = int(misc.get_input("Input frequency (in kHz)> ")) - else: - print("Guessed frequency: {}kHz".format(freq)) - - for point in self.data_points: - point.real_send_time_ms = (point.timestamp - - self.data_points[0].timestamp) / freq - point.delay = point.arrival_timestamp_ms - point.real_send_time_ms - - def PrintDurationStatistics(self): - """Prints delay, clock drift and bitrate statistics.""" - - min_delay = min(point.delay for point in self.data_points) - - for point in self.data_points: - point.absdelay = point.delay - min_delay - - stream_duration_sender = self.data_points[-1].real_send_time_ms / 1000 - print("Stream duration at sender: {:.1f} seconds".format( - stream_duration_sender)) - - arrival_timestamps_ms = [ - point.arrival_timestamp_ms for point in self.data_points - ] - stream_duration_receiver = (max(arrival_timestamps_ms) - - min(arrival_timestamps_ms)) / 1000 - print("Stream duration at receiver: {:.1f} seconds".format( - stream_duration_receiver)) - - print("Clock drift: {:.2f}%".format( - 100 * (stream_duration_receiver / stream_duration_sender - 1))) - - total_size = sum(point.size for point in self.data_points) * 8 / 1000 - print("Send average bitrate: {:.2f} kbps".format(total_size / - stream_duration_sender)) - - print("Receive average bitrate: {:.2f} kbps".format( - total_size / stream_duration_receiver)) - - def RemoveReordered(self): - last = self.data_points[0] - data_points_ordered = [last] - for point in self.data_points[1:]: - if point.sequence_number > last.sequence_number and ( - point.real_send_time_ms > last.real_send_time_ms): - data_points_ordered.append(point) - last = point - self.data_points = data_points_ordered - - def ComputeBandwidth(self): - """Computes bandwidth averaged over several consecutive packets. - - The number of consecutive packets used in the average is - BANDWIDTH_SMOOTHING_WINDOW_SIZE. Averaging is done with - numpy.correlate. - """ - start_ms = self.data_points[0].real_send_time_ms - stop_ms = self.data_points[-1].real_send_time_ms - (self.bandwidth_kbps, _) = numpy.histogram( - [point.real_send_time_ms for point in self.data_points], - bins=numpy.arange(start_ms, stop_ms, RTPStatistics.PLOT_RESOLUTION_MS), - weights=[ - point.size * 8 / RTPStatistics.PLOT_RESOLUTION_MS - for point in self.data_points - ]) - correlate_filter = ( - numpy.ones(RTPStatistics.BANDWIDTH_SMOOTHING_WINDOW_SIZE) / - RTPStatistics.BANDWIDTH_SMOOTHING_WINDOW_SIZE) - self.smooth_bw_kbps = numpy.correlate(self.bandwidth_kbps, correlate_filter) - - def PlotStatistics(self): - """Plots changes in delay and average bandwidth.""" - - start_ms = self.data_points[0].real_send_time_ms - stop_ms = self.data_points[-1].real_send_time_ms - time_axis = numpy.arange(start_ms / 1000, stop_ms / 1000, - RTPStatistics.PLOT_RESOLUTION_MS / 1000) - - delay = CalculateDelay(start_ms, stop_ms, RTPStatistics.PLOT_RESOLUTION_MS, - self.data_points) - - plt.figure(1) - plt.plot(time_axis, delay[:len(time_axis)]) - plt.xlabel("Send time [s]") - plt.ylabel("Relative transport delay [ms]") - - plt.figure(2) - plt.plot(time_axis[:len(self.smooth_bw_kbps)], self.smooth_bw_kbps) - plt.xlabel("Send time [s]") - plt.ylabel("Bandwidth [kbps]") - - plt.show() - - -def CalculateDelay(start, stop, step, points): - """Quantizes the time coordinates for the delay. - - Quantizes points by rounding the timestamps downwards to the nearest - point in the time sequence start, start+step, start+2*step... Takes - the average of the delays of points rounded to the same. Returns - masked array, in which time points with no value are masked. - - """ - grouped_delays = [[] for _ in numpy.arange(start, stop + step, step)] - rounded_value_index = lambda x: int((x - start) / step) - for point in points: - grouped_delays[rounded_value_index(point.real_send_time_ms)].append( - point.absdelay) - regularized_delays = [ - numpy.average(arr) if arr else -1 for arr in grouped_delays - ] - return numpy.ma.masked_values(regularized_delays, -1) - - -def main(): - usage = "Usage: %prog [options] " - parser = optparse.OptionParser(usage=usage) - parser.add_option("--dump_header_to_stdout", - default=False, - action="store_true", - help="print header info to stdout; similar to rtp_analyze") - parser.add_option("--query_sample_rate", - default=False, - action="store_true", - help="always query user for real sample rate") - - parser.add_option("--working_directory", - default=None, - action="store", - help="directory in which to search for relative paths") - - (options, args) = parser.parse_args() - - if len(args) < 1: - parser.print_help() - sys.exit(0) - - input_file = args[0] - - if options.working_directory and not os.path.isabs(input_file): - input_file = os.path.join(options.working_directory, input_file) - - data_points = pb_parse.ParseProtobuf(input_file) - rtp_stats = RTPStatistics(data_points) - - if options.dump_header_to_stdout: - print("Printing header info to stdout.", file=sys.stderr) - rtp_stats.PrintHeaderStatistics() - sys.exit(0) - - chosen_ssrc = rtp_stats.ChooseSsrc() - print("Chosen SSRC: 0X{:X}".format(chosen_ssrc)) - - rtp_stats.FilterSsrc(chosen_ssrc) - - print("Statistics:") - rtp_stats.PrintSequenceNumberStatistics() - rtp_stats.EstimateFrequency(options.query_sample_rate) - rtp_stats.PrintDurationStatistics() - rtp_stats.RemoveReordered() - rtp_stats.ComputeBandwidth() - rtp_stats.PlotStatistics() - - -if __name__ == "__main__": - main() diff --git a/rtc_tools/py_event_log_analyzer/rtp_analyzer.sh b/rtc_tools/py_event_log_analyzer/rtp_analyzer.sh deleted file mode 100755 index 7467e493d2a..00000000000 --- a/rtc_tools/py_event_log_analyzer/rtp_analyzer.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -BASE_DIR=`dirname $0` -python "${BASE_DIR}/rtp_analyzer.py" $@ --working_dir $BASE_DIR diff --git a/rtc_tools/py_event_log_analyzer/rtp_analyzer_test.py b/rtc_tools/py_event_log_analyzer/rtp_analyzer_test.py deleted file mode 100755 index a078c3098f6..00000000000 --- a/rtc_tools/py_event_log_analyzer/rtp_analyzer_test.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -"""Run the tests with - - python rtp_analyzer_test.py -or - python3 rtp_analyzer_test.py -""" - -from __future__ import absolute_import -from __future__ import print_function -import collections -import unittest - -MISSING_NUMPY = False # pylint: disable=invalid-name -try: - import numpy - import rtp_analyzer -except ImportError: - MISSING_NUMPY = True - -FakePoint = collections.namedtuple("FakePoint", - ["real_send_time_ms", "absdelay"]) - - -class TestDelay(unittest.TestCase): - def AssertMaskEqual(self, masked_array, data, mask): - self.assertEqual(list(masked_array.data), data) - - if isinstance(masked_array.mask, numpy.bool_): - array_mask = masked_array.mask - else: - array_mask = list(masked_array.mask) - self.assertEqual(array_mask, mask) - - def testCalculateDelaySimple(self): - points = [FakePoint(0, 0), FakePoint(1, 0)] - mask = rtp_analyzer.CalculateDelay(0, 1, 1, points) - self.AssertMaskEqual(mask, [0, 0], False) - - def testCalculateDelayMissing(self): - points = [FakePoint(0, 0), FakePoint(2, 0)] - mask = rtp_analyzer.CalculateDelay(0, 2, 1, points) - self.AssertMaskEqual(mask, [0, -1, 0], [False, True, False]) - - def testCalculateDelayBorders(self): - points = [FakePoint(0, 0), FakePoint(2, 0)] - mask = rtp_analyzer.CalculateDelay(0, 3, 2, points) - self.AssertMaskEqual(mask, [0, 0, -1], [False, False, True]) - - -if __name__ == "__main__": - if MISSING_NUMPY: - print("Missing numpy, skipping test.") - else: - unittest.main() diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc b/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc index ed3629a7876..3fa1f53dd77 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc @@ -25,12 +25,16 @@ #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_format.h" #include "api/environment/environment.h" +#include "api/field_trials_view.h" #include "api/function_view.h" #include "api/make_ref_counted.h" #include "api/neteq/neteq.h" #include "api/scoped_refptr.h" #include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" #include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h" +#include "logging/rtc_event_log/events/rtc_event_audio_playout.h" +#include "logging/rtc_event_log/events/rtc_event_neteq_set_minimum_delay.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" #include "modules/audio_coding/neteq/tools/audio_sink.h" #include "modules/audio_coding/neteq/tools/fake_decode_from_file.h" @@ -243,7 +247,7 @@ std::unique_ptr CreateNetEqTestAndRun( uint32_t ssrc, absl::string_view replacement_file_name, int file_sample_rate_hz, - absl::string_view field_trials) { + const FieldTrialsView* field_trials) { std::unique_ptr input = test::CreateNetEqEventLogInput(parsed_log, ssrc); if (!input) { @@ -277,8 +281,8 @@ std::unique_ptr CreateNetEqTestAndRun( NetEq::Config config; test::NetEqTest test(config, decoder_factory, codecs, /*text_log=*/nullptr, - /*factory=*/nullptr, std::move(input), std::move(output), - callbacks, field_trials); + /*neteq_factory=*/nullptr, std::move(input), + std::move(output), callbacks, field_trials); test.Run(); return neteq_stats_getter; } @@ -288,7 +292,7 @@ NetEqStatsGetterMap SimulateNetEq(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config, absl::string_view replacement_file_name, int file_sample_rate_hz, - absl::string_view field_trials) { + const FieldTrialsView* field_trials) { NetEqStatsGetterMap neteq_stats; for (uint32_t ssrc : parsed_log.incoming_audio_ssrcs()) { std::unique_ptr stats = @@ -426,4 +430,107 @@ void CreateNetEqLifetimeStatsGraph( stats_extractor, plot_name, plot); } +// For each SSRC, plot the time between the consecutive playouts. +void CreatePlayoutGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& playout_stream : parsed_log.audio_playout_events()) { + uint32_t ssrc = playout_stream.first; + if (!MatchingSsrc(ssrc, config.desired_ssrc_)) + continue; + std::optional last_playout_ms; + TimeSeries time_series(SsrcToString(ssrc), LineStyle::kBar); + for (const auto& playout_event : playout_stream.second) { + float x = config.GetCallTimeSec(playout_event.log_time()); + int64_t playout_time_ms = playout_event.log_time_ms(); + // If there were no previous playouts, place the point on the x-axis. + float y = playout_time_ms - last_playout_ms.value_or(playout_time_ms); + time_series.points.push_back(TimeSeriesPoint(x, y)); + last_playout_ms.emplace(playout_time_ms); + } + plot->AppendTimeSeries(std::move(time_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Time since last playout (ms)", kBottomMargin, + kTopMargin); + plot->SetTitle("Audio playout"); +} + +void CreateNetEqSetMinimumDelay(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& playout_stream : + parsed_log.neteq_set_minimum_delay_events()) { + uint32_t ssrc = playout_stream.first; + if (!MatchingSsrc(ssrc, config.desired_ssrc_)) + continue; + + TimeSeries time_series(SsrcToString(ssrc), LineStyle::kStep, + PointStyle::kHighlight); + for (const auto& event : playout_stream.second) { + float x = config.GetCallTimeSec(event.log_time()); + float y = event.minimum_delay_ms; + time_series.points.push_back(TimeSeriesPoint(x, y)); + } + plot->AppendTimeSeries(std::move(time_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1000, "Minimum Delay (ms)", kBottomMargin, + kTopMargin); + plot->SetTitle("Set Minimum Delay"); +} + +// For audio SSRCs, plot the audio level. +void CreateAudioLevelGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + PacketDirection direction, + Plot* plot) { + for (const auto& stream : parsed_log.rtp_packets_by_ssrc(direction)) { + if (!IsAudioSsrc(parsed_log, direction, stream.ssrc)) + continue; + TimeSeries time_series(GetStreamName(parsed_log, direction, stream.ssrc), + LineStyle::kLine); + for (auto& packet : stream.packet_view) { + if (packet.header.extension.audio_level()) { + float x = config.GetCallTimeSec(packet.log_time()); + // The audio level is stored in -dBov (so e.g. -10 dBov is stored as 10) + // Here we convert it to dBov. + float y = + static_cast(-packet.header.extension.audio_level()->level()); + time_series.points.emplace_back(TimeSeriesPoint(x, y)); + } + } + plot->AppendTimeSeries(std::move(time_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetYAxis(-127, 0, "Audio level (dBov)", kBottomMargin, kTopMargin); + plot->SetTitle(GetDirectionAsString(direction) + " audio level"); +} + +LazyNetEqSimulator::LazyNetEqSimulator(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config) + : parsed_log_(parsed_log), config_(config) {} + +void LazyNetEqSimulator::SetReplacementAudioFile( + absl::string_view replacement_file_name, + int file_sample_rate_hz) { + replacement_file_name_ = std::string(replacement_file_name); + file_sample_rate_hz_ = file_sample_rate_hz; +} + +const NetEqStatsGetterMap& LazyNetEqSimulator::GetStats() const { + if (!neteq_stats_) { + neteq_stats_ = + SimulateNetEq(parsed_log_, config_, replacement_file_name_, + file_sample_rate_hz_, &config_.env_.field_trials()); + } + return *neteq_stats_; +} + } // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_audio.h b/rtc_tools/rtc_event_log_visualizer/analyze_audio.h index 094f9d91b60..db306f813f0 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyze_audio.h +++ b/rtc_tools/rtc_event_log_visualizer/analyze_audio.h @@ -14,9 +14,11 @@ #include #include #include +#include #include #include "absl/strings/string_view.h" +#include "api/field_trials_view.h" #include "api/function_view.h" #include "api/neteq/neteq.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" @@ -45,13 +47,45 @@ void CreateAudioEncoderNumChannelsGraph(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config, Plot* plot); +void CreatePlayoutGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateNetEqSetMinimumDelay(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateAudioLevelGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + PacketDirection direction, + Plot* plot); + using NetEqStatsGetterMap = std::map>; + +class LazyNetEqSimulator { + public: + LazyNetEqSimulator(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config); + + void SetReplacementAudioFile(absl::string_view replacement_file_name, + int file_sample_rate_hz); + + const NetEqStatsGetterMap& GetStats() const; + + private: + const ParsedRtcEventLog& parsed_log_; + const AnalyzerConfig config_; + std::string replacement_file_name_; + int file_sample_rate_hz_ = 48000; + mutable std::optional neteq_stats_; +}; + NetEqStatsGetterMap SimulateNetEq(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config, absl::string_view replacement_file_name, int file_sample_rate_hz, - absl::string_view field_trials); + const FieldTrialsView* field_trials); void CreateAudioJitterBufferGraph(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config, diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc new file mode 100644 index 00000000000..30682e4fddb --- /dev/null +++ b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.cc @@ -0,0 +1,1276 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_tools/rtc_event_log_visualizer/analyze_bwe.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/functional/bind_front.h" +#include "absl/strings/string_view.h" +#include "api/environment/environment.h" +#include "api/environment/environment_factory.h" +#include "api/field_trials.h" +#include "api/function_view.h" +#include "api/media_types.h" +#include "api/rtp_headers.h" +#include "api/transport/bandwidth_usage.h" +#include "api/transport/goog_cc_factory.h" +#include "api/transport/network_control.h" +#include "api/transport/network_types.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h" +#include "modules/congestion_controller/include/receive_side_congestion_controller.h" +#include "modules/congestion_controller/rtp/transport_feedback_adapter.h" +#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/network/sent_packet.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" +#include "rtc_base/rate_statistics.h" +#include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" +#include "rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h" +#include "rtc_tools/rtc_event_log_visualizer/log_simulation.h" +#include "rtc_tools/rtc_event_log_visualizer/plot_base.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { + +namespace { + +double AbsSendTimeToMicroseconds(int64_t abs_send_time) { + // The timestamp is a fixed point representation with 6 bits for seconds + // and 18 bits for fractions of a second. Thus, we divide by 2^18 to get the + // time in seconds and then multiply by kNumMicrosecsPerSec to convert to + // microseconds. + static constexpr double kTimestampToMicroSec = + static_cast(kNumMicrosecsPerSec) / static_cast(1ul << 18); + return abs_send_time * kTimestampToMicroSec; +} + +// This is much more reliable for outgoing streams than for incoming streams. +template +std::optional EstimateRtpClockFrequency( + const RtpPacketContainer& packets, + int64_t end_time_us) { + RTC_CHECK(packets.size() >= 2); + SeqNumUnwrapper unwrapper; + int64_t first_rtp_timestamp = + unwrapper.Unwrap(packets[0].rtp.header.timestamp); + int64_t first_log_timestamp = packets[0].log_time_us(); + int64_t last_rtp_timestamp = first_rtp_timestamp; + int64_t last_log_timestamp = first_log_timestamp; + for (size_t i = 1; i < packets.size(); i++) { + if (packets[i].log_time_us() > end_time_us) + break; + last_rtp_timestamp = unwrapper.Unwrap(packets[i].rtp.header.timestamp); + last_log_timestamp = packets[i].log_time_us(); + } + if (last_log_timestamp - first_log_timestamp < kNumMicrosecsPerSec) { + RTC_LOG(LS_WARNING) + << "Failed to estimate RTP clock frequency: Stream too short. (" + << packets.size() << " packets, " + << last_log_timestamp - first_log_timestamp << " us)"; + return std::nullopt; + } + double duration = + static_cast(last_log_timestamp - first_log_timestamp) / + kNumMicrosecsPerSec; + double estimated_frequency = + (last_rtp_timestamp - first_rtp_timestamp) / duration; + for (uint32_t f : {8000, 16000, 32000, 48000, 90000}) { + if (std::fabs(estimated_frequency - f) < 0.15 * f) { + return f; + } + } + RTC_LOG(LS_WARNING) << "Failed to estimate RTP clock frequency: Estimate " + << estimated_frequency + << " not close to any standard RTP frequency." + << " Last timestamp " << last_rtp_timestamp + << " first timestamp " << first_rtp_timestamp; + return std::nullopt; +} + +std::optional NetworkDelayDiff_AbsSendTime( + const LoggedRtpPacketIncoming& old_packet, + const LoggedRtpPacketIncoming& new_packet) { + if (old_packet.rtp.header.extension.hasAbsoluteSendTime && + new_packet.rtp.header.extension.hasAbsoluteSendTime) { + int64_t send_time_diff = WrappingDifference( + new_packet.rtp.header.extension.absoluteSendTime, + old_packet.rtp.header.extension.absoluteSendTime, 1ul << 24); + int64_t recv_time_diff = + new_packet.log_time_us() - old_packet.log_time_us(); + double delay_change_us = + recv_time_diff - AbsSendTimeToMicroseconds(send_time_diff); + return delay_change_us / 1000; + } else { + return std::nullopt; + } +} + +std::optional NetworkDelayDiff_CaptureTime( + const LoggedRtpPacketIncoming& old_packet, + const LoggedRtpPacketIncoming& new_packet, + const double sample_rate) { + int64_t send_time_diff = + WrappingDifference(new_packet.rtp.header.timestamp, + old_packet.rtp.header.timestamp, 1ull << 32); + int64_t recv_time_diff = new_packet.log_time_us() - old_packet.log_time_us(); + + double delay_change = + static_cast(recv_time_diff) / 1000 - + static_cast(send_time_diff) / sample_rate * 1000; + if (delay_change < -10000 || 10000 < delay_change) { + RTC_LOG(LS_WARNING) << "Very large delay change. Timestamps correct?"; + RTC_LOG(LS_WARNING) << "Old capture time " + << old_packet.rtp.header.timestamp << ", received time " + << old_packet.log_time_us(); + RTC_LOG(LS_WARNING) << "New capture time " + << new_packet.rtp.header.timestamp << ", received time " + << new_packet.log_time_us(); + RTC_LOG(LS_WARNING) << "Receive time difference " << recv_time_diff << " = " + << static_cast(recv_time_diff) / + kNumMicrosecsPerSec + << "s"; + RTC_LOG(LS_WARNING) << "Send time difference " << send_time_diff << " = " + << static_cast(send_time_diff) / sample_rate + << "s"; + } + return delay_change; +} + +struct FakeExtensionSmall { + static constexpr RTPExtensionType kId = kRtpExtensionMid; + static constexpr absl::string_view Uri() { return "fake-extension-small"; } +}; +struct FakeExtensionLarge { + static constexpr RTPExtensionType kId = kRtpExtensionRtpStreamId; + static constexpr absl::string_view Uri() { return "fake-extension-large"; } +}; + +RtpPacketReceived RtpPacketForBWEFromHeader(const RTPHeader& header) { + RtpHeaderExtensionMap rtp_header_extensions(/*extmap_allow_mixed=*/true); + // ReceiveSideCongestionController doesn't need to know extensions ids as + // long as it able to get extensions by type. So any ids would work here. + rtp_header_extensions.Register(1); + rtp_header_extensions.Register(2); + rtp_header_extensions.Register(3); + rtp_header_extensions.Register(4); + // Use id > 14 to force two byte header per rtp header when this one is used. + rtp_header_extensions.Register(16); + + RtpPacketReceived rtp_packet(&rtp_header_extensions); + // Set only fields that might be relevant for the bandwidth estimatior. + rtp_packet.SetSsrc(header.ssrc); + rtp_packet.SetTimestamp(header.timestamp); + size_t num_bwe_extensions = 0; + if (header.extension.hasTransmissionTimeOffset) { + rtp_packet.SetExtension( + header.extension.transmissionTimeOffset); + ++num_bwe_extensions; + } + if (header.extension.hasAbsoluteSendTime) { + rtp_packet.SetExtension( + header.extension.absoluteSendTime); + ++num_bwe_extensions; + } + if (header.extension.hasTransportSequenceNumber) { + rtp_packet.SetExtension( + header.extension.transportSequenceNumber); + ++num_bwe_extensions; + } + + // All parts of the RTP header are 32bit aligned. + RTC_CHECK_EQ(header.headerLength % 4, 0); + + // Original packet could have more extensions, there could be csrcs that are + // not propagated by the rtc event log, i.e. logged header size might be + // larger that rtp_packet.header_size(). Increase it by setting an extra fake + // extension. + RTC_CHECK_GE(header.headerLength, rtp_packet.headers_size()); + size_t bytes_to_add = header.headerLength - rtp_packet.headers_size(); + if (bytes_to_add > 0) { + if (bytes_to_add <= 16) { + // one-byte header rtp header extension allows to add up to 16 bytes. + rtp_packet.AllocateExtension(FakeExtensionSmall::kId, bytes_to_add - 1); + } else { + // two-byte header rtp header extension would also add one byte per + // already set extension. + rtp_packet.AllocateExtension(FakeExtensionLarge::kId, + bytes_to_add - 2 - num_bwe_extensions); + } + } + RTC_CHECK_EQ(rtp_packet.headers_size(), header.headerLength); + + return rtp_packet; +} + +} // namespace + +class BitrateObserver : public RemoteBitrateObserver { + public: + BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {} + + void Update(NetworkControlUpdate update) { + if (update.target_rate) { + last_bitrate_bps_ = update.target_rate->target_rate.bps(); + bitrate_updated_ = true; + } + } + + void OnReceiveBitrateChanged(const std::vector& ssrcs, + uint32_t bitrate) override {} + + uint32_t last_bitrate_bps() const { return last_bitrate_bps_; } + bool GetAndResetBitrateUpdated() { + bool bitrate_updated = bitrate_updated_; + bitrate_updated_ = false; + return bitrate_updated; + } + + private: + uint32_t last_bitrate_bps_; + bool bitrate_updated_; +}; + +void CreateIncomingDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.incoming_rtp_packets_by_ssrc()) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, config.desired_ssrc_) || + IsRtxSsrc(parsed_log, kIncomingPacket, stream.ssrc)) { + continue; + } + + const std::vector& packets = + stream.incoming_packets; + if (packets.size() < 100) { + RTC_LOG(LS_WARNING) << "Can't estimate the RTP clock frequency with " + << packets.size() << " packets in the stream."; + continue; + } + int64_t segment_end_us = parsed_log.first_log_segment().stop_time_us(); + std::optional estimated_frequency = + EstimateRtpClockFrequency(packets, segment_end_us); + if (!estimated_frequency) + continue; + const double frequency_hz = *estimated_frequency; + if (IsVideoSsrc(parsed_log, kIncomingPacket, stream.ssrc) && + frequency_hz != 90000) { + RTC_LOG(LS_WARNING) + << "Video stream should use a 90 kHz clock but appears to use " + << frequency_hz / 1000 << ". Discarding."; + continue; + } + + auto ToCallTime = [&config](const LoggedRtpPacketIncoming& packet) { + return config.GetCallTimeSec(packet.log_time()); + }; + auto ToNetworkDelay = [frequency_hz]( + const LoggedRtpPacketIncoming& old_packet, + const LoggedRtpPacketIncoming& new_packet) { + return NetworkDelayDiff_CaptureTime(old_packet, new_packet, frequency_hz); + }; + + TimeSeries capture_time_data( + GetStreamName(parsed_log, kIncomingPacket, stream.ssrc) + + " capture-time", + LineStyle::kLine); + AccumulatePairs( + ToCallTime, ToNetworkDelay, packets, &capture_time_data); + plot->AppendTimeSeries(std::move(capture_time_data)); + + TimeSeries send_time_data( + GetStreamName(parsed_log, kIncomingPacket, stream.ssrc) + + " abs-send-time", + LineStyle::kLine); + AccumulatePairs( + ToCallTime, NetworkDelayDiff_AbsSendTime, packets, &send_time_data); + plot->AppendTimeSeriesIfNotEmpty(std::move(send_time_data)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Delay (ms)", kBottomMargin, kTopMargin); + plot->SetTitle("Incoming network delay (relative to first packet)"); +} + +// Plot the fraction of packets lost (as perceived by the loss-based BWE). +void CreateFractionLossGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries time_series("Fraction lost", LineStyle::kLine, + PointStyle::kHighlight); + for (auto& bwe_update : parsed_log.bwe_loss_updates()) { + float x = config.GetCallTimeSec(bwe_update.log_time()); + float y = static_cast(bwe_update.fraction_lost) / 255 * 100; + time_series.points.emplace_back(x, y); + } + + plot->AppendTimeSeries(std::move(time_series)); + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Loss rate (in %)", kBottomMargin, kTopMargin); + plot->SetTitle("Outgoing packet loss (as reported by BWE)"); +} + +// Plot the total bandwidth used by all RTP streams. +void CreateTotalIncomingBitrateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + // TODO(terelius): This could be provided by the parser. + std::multimap packets_in_order; + for (const auto& stream : parsed_log.incoming_rtp_packets_by_ssrc()) { + for (const LoggedRtpPacketIncoming& packet : stream.incoming_packets) + packets_in_order.insert( + std::make_pair(packet.rtp.log_time(), packet.rtp.total_length)); + } + + auto window_begin = packets_in_order.begin(); + auto window_end = packets_in_order.begin(); + size_t bytes_in_window = 0; + + if (!packets_in_order.empty()) { + // Calculate a moving average of the bitrate and store in a TimeSeries. + TimeSeries bitrate_series("Bitrate", LineStyle::kLine); + for (Timestamp time = config.begin_time_; + time < config.end_time_ + config.step_; time += config.step_) { + while (window_end != packets_in_order.end() && window_end->first < time) { + bytes_in_window += window_end->second; + ++window_end; + } + while (window_begin != packets_in_order.end() && + window_begin->first < time - config.window_duration_) { + RTC_DCHECK_LE(window_begin->second, bytes_in_window); + bytes_in_window -= window_begin->second; + ++window_begin; + } + float window_duration_in_seconds = + static_cast(config.window_duration_.us()) / + kNumMicrosecsPerSec; + float x = config.GetCallTimeSec(time); + float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; + bitrate_series.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(bitrate_series)); + } + + // Overlay the outgoing REMB over incoming bitrate. + TimeSeries remb_series("Remb", LineStyle::kStep); + for (const auto& rtcp : parsed_log.rembs(kOutgoingPacket)) { + float x = config.GetCallTimeSec(rtcp.log_time()); + float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; + remb_series.points.emplace_back(x, y); + } + plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle("Incoming RTP bitrate"); +} + +// Plot the total bandwidth used by all RTP streams. +void CreateTotalOutgoingBitrateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot, + bool show_detector_state, + bool show_alr_state, + bool show_link_capacity) { + // TODO(terelius): This could be provided by the parser. + std::multimap packets_in_order; + for (const auto& stream : parsed_log.outgoing_rtp_packets_by_ssrc()) { + for (const LoggedRtpPacketOutgoing& packet : stream.outgoing_packets) + packets_in_order.insert( + std::make_pair(packet.rtp.log_time(), packet.rtp.total_length)); + } + + auto window_begin = packets_in_order.begin(); + auto window_end = packets_in_order.begin(); + size_t bytes_in_window = 0; + + if (!packets_in_order.empty()) { + // Calculate a moving average of the bitrate and store in a TimeSeries. + TimeSeries bitrate_series("Bitrate", LineStyle::kLine); + for (Timestamp time = config.begin_time_; + time < config.end_time_ + config.step_; time += config.step_) { + while (window_end != packets_in_order.end() && window_end->first < time) { + bytes_in_window += window_end->second; + ++window_end; + } + while (window_begin != packets_in_order.end() && + window_begin->first < time - config.window_duration_) { + RTC_DCHECK_LE(window_begin->second, bytes_in_window); + bytes_in_window -= window_begin->second; + ++window_begin; + } + float window_duration_in_seconds = + static_cast(config.window_duration_.us()) / + kNumMicrosecsPerSec; + float x = config.GetCallTimeSec(time); + float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; + bitrate_series.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(bitrate_series)); + } + + // Overlay the send-side bandwidth estimate over the outgoing bitrate. + TimeSeries loss_series("Loss-based estimate", LineStyle::kStep); + for (auto& loss_update : parsed_log.bwe_loss_updates()) { + float x = config.GetCallTimeSec(loss_update.log_time()); + float y = static_cast(loss_update.bitrate_bps) / 1000; + loss_series.points.emplace_back(x, y); + } + + TimeSeries link_capacity_lower_series("Link-capacity-lower", + LineStyle::kStep); + TimeSeries link_capacity_upper_series("Link-capacity-upper", + LineStyle::kStep); + for (auto& remote_estimate_event : parsed_log.remote_estimate_events()) { + float x = config.GetCallTimeSec(remote_estimate_event.log_time()); + if (remote_estimate_event.link_capacity_lower.has_value()) { + float link_capacity_lower = static_cast( + remote_estimate_event.link_capacity_lower.value().kbps()); + link_capacity_lower_series.points.emplace_back(x, link_capacity_lower); + } + if (remote_estimate_event.link_capacity_upper.has_value()) { + float link_capacity_upper = static_cast( + remote_estimate_event.link_capacity_upper.value().kbps()); + link_capacity_upper_series.points.emplace_back(x, link_capacity_upper); + } + } + + TimeSeries delay_series("Delay-based estimate", LineStyle::kStep); + IntervalSeries overusing_series("Overusing", "#ff8e82", + IntervalSeries::kHorizontal); + IntervalSeries underusing_series("Underusing", "#5092fc", + IntervalSeries::kHorizontal); + IntervalSeries normal_series("Normal", "#c4ffc4", + IntervalSeries::kHorizontal); + IntervalSeries* last_series = &normal_series; + float last_detector_switch = 0.0; + + BandwidthUsage last_detector_state = BandwidthUsage::kBwNormal; + + for (auto& delay_update : parsed_log.bwe_delay_updates()) { + float x = config.GetCallTimeSec(delay_update.log_time()); + float y = static_cast(delay_update.bitrate_bps) / 1000; + + if (last_detector_state != delay_update.detector_state) { + last_series->intervals.emplace_back(last_detector_switch, x); + last_detector_state = delay_update.detector_state; + last_detector_switch = x; + + switch (delay_update.detector_state) { + case BandwidthUsage::kBwNormal: + last_series = &normal_series; + break; + case BandwidthUsage::kBwUnderusing: + last_series = &underusing_series; + break; + case BandwidthUsage::kBwOverusing: + last_series = &overusing_series; + break; + case BandwidthUsage::kLast: + RTC_DCHECK_NOTREACHED(); + } + } + + delay_series.points.emplace_back(x, y); + } + + RTC_CHECK(last_series); + last_series->intervals.emplace_back(last_detector_switch, + config.CallEndTimeSec()); + + TimeSeries scream_series("Scream target rate", LineStyle::kStep); + for (auto& scream_update : parsed_log.bwe_scream_updates()) { + float x = config.GetCallTimeSec(scream_update.log_time()); + float y = static_cast(scream_update.target_rate.kbps()); + scream_series.points.emplace_back(x, y); + } + + TimeSeries created_series("Probe cluster created.", LineStyle::kNone, + PointStyle::kHighlight); + for (auto& cluster : parsed_log.bwe_probe_cluster_created_events()) { + float x = config.GetCallTimeSec(cluster.log_time()); + float y = static_cast(cluster.bitrate_bps) / 1000; + created_series.points.emplace_back(x, y); + } + + TimeSeries result_series("Probing results.", LineStyle::kNone, + PointStyle::kHighlight); + for (auto& result : parsed_log.bwe_probe_success_events()) { + float x = config.GetCallTimeSec(result.log_time()); + float y = static_cast(result.bitrate_bps) / 1000; + result_series.points.emplace_back(x, y); + } + + TimeSeries probe_failures_series("Probe failed", LineStyle::kNone, + PointStyle::kHighlight); + for (auto& failure : parsed_log.bwe_probe_failure_events()) { + float x = config.GetCallTimeSec(failure.log_time()); + probe_failures_series.points.emplace_back(x, 0); + } + + IntervalSeries alr_state("ALR", "#555555", IntervalSeries::kHorizontal); + bool previously_in_alr = false; + Timestamp alr_start = Timestamp::Zero(); + for (auto& alr : parsed_log.alr_state_events()) { + float y = config.GetCallTimeSec(alr.log_time()); + if (!previously_in_alr && alr.in_alr) { + alr_start = alr.log_time(); + previously_in_alr = true; + } else if (previously_in_alr && !alr.in_alr) { + float x = config.GetCallTimeSec(alr_start); + alr_state.intervals.emplace_back(x, y); + previously_in_alr = false; + } + } + + if (previously_in_alr) { + float x = config.GetCallTimeSec(alr_start); + float y = config.GetCallTimeSec(config.end_time_); + alr_state.intervals.emplace_back(x, y); + } + + if (show_detector_state) { + plot->AppendIntervalSeries(std::move(overusing_series)); + plot->AppendIntervalSeries(std::move(underusing_series)); + plot->AppendIntervalSeries(std::move(normal_series)); + } + + if (show_alr_state) { + plot->AppendIntervalSeries(std::move(alr_state)); + } + + if (show_link_capacity) { + plot->AppendTimeSeriesIfNotEmpty(std::move(link_capacity_lower_series)); + plot->AppendTimeSeriesIfNotEmpty(std::move(link_capacity_upper_series)); + } + + plot->AppendTimeSeries(std::move(loss_series)); + plot->AppendTimeSeriesIfNotEmpty(std::move(probe_failures_series)); + plot->AppendTimeSeries(std::move(delay_series)); + plot->AppendTimeSeriesIfNotEmpty(std::move(scream_series)); + plot->AppendTimeSeries(std::move(created_series)); + plot->AppendTimeSeries(std::move(result_series)); + + // Overlay the incoming REMB over the outgoing bitrate. + TimeSeries remb_series("Remb", LineStyle::kStep); + for (const auto& rtcp : parsed_log.rembs(kIncomingPacket)) { + float x = config.GetCallTimeSec(rtcp.log_time()); + float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; + remb_series.points.emplace_back(x, y); + } + plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle("Outgoing RTP bitrate"); +} + +void CreateGoogCcSimulationGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries target_rates("Simulated target rate", LineStyle::kStep, + PointStyle::kHighlight); + TimeSeries delay_based("Logged delay-based estimate", LineStyle::kStep, + PointStyle::kHighlight); + TimeSeries loss_based("Logged loss-based estimate", LineStyle::kStep, + PointStyle::kHighlight); + TimeSeries probe_results("Logged probe success", LineStyle::kNone, + PointStyle::kHighlight); + + LogBasedNetworkControllerSimulation simulation( + config.env_, std::make_unique(), + [&](const NetworkControlUpdate& update, Timestamp at_time) { + if (update.target_rate) { + target_rates.points.emplace_back( + config.GetCallTimeSec(at_time), + update.target_rate->target_rate.kbps()); + } + }); + + simulation.ProcessEventsInLog(parsed_log); + for (const auto& logged : parsed_log.bwe_delay_updates()) + delay_based.points.emplace_back(config.GetCallTimeSec(logged.log_time()), + logged.bitrate_bps / 1000); + for (const auto& logged : parsed_log.bwe_probe_success_events()) + probe_results.points.emplace_back(config.GetCallTimeSec(logged.log_time()), + logged.bitrate_bps / 1000); + for (const auto& logged : parsed_log.bwe_loss_updates()) + loss_based.points.emplace_back(config.GetCallTimeSec(logged.log_time()), + logged.bitrate_bps / 1000); + + plot->AppendTimeSeries(std::move(delay_based)); + plot->AppendTimeSeries(std::move(loss_based)); + plot->AppendTimeSeries(std::move(probe_results)); + plot->AppendTimeSeries(std::move(target_rates)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated BWE behavior"); +} + +void CreateScreamSimulationDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries smoothed_rtt_series("Smoothed RTT", LineStyle::kStep); + TimeSeries queue_delay_series("Queue delay", LineStyle::kStep); + TimeSeries queue_delay_min_avg_series("Queue delay min avg", + LineStyle::kStep); + TimeSeries latency_difference_avg_series("Latency difference avg", + LineStyle::kStep); + + LogScreamSimulation simulation({.rate_window = config.window_duration_}, + config.env_); + simulation.ProcessEventsInLog(parsed_log); + + for (const LogScreamSimulation::State& state : simulation.updates()) { + smoothed_rtt_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.smoothed_rtt.ms()); + queue_delay_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.queue_delay.ms()); + queue_delay_min_avg_series.points.emplace_back( + config.GetCallTimeSec(state.time), state.queue_delay_min_avg.ms()); + latency_difference_avg_series.points.emplace_back( + config.GetCallTimeSec(state.time), state.latency_difference_avg.ms()); + } + plot->AppendTimeSeries(std::move(smoothed_rtt_series)); + plot->AppendTimeSeries(std::move(queue_delay_series)); + plot->AppendTimeSeries(std::move(queue_delay_min_avg_series)); + plot->AppendTimeSeries(std::move(latency_difference_avg_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 50, "Delay (ms)", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated Scream delays"); +} + +void CreateScreamSimulationBitrateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries target_rate_series("Target rate", LineStyle::kStep); + TimeSeries pacing_rate_series("Pacing rate", LineStyle::kStep); + TimeSeries send_rate_series("Send rate", LineStyle::kStep); + + LogScreamSimulation simulation({.rate_window = config.window_duration_}, + config.env_); + simulation.ProcessEventsInLog(parsed_log); + + for (const LogScreamSimulation::State& state : simulation.updates()) { + target_rate_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.target_rate.bps() / 1000); + pacing_rate_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.pacing_rate.bps() / 1000); + send_rate_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.send_rate.bps() / 1000); + } + plot->AppendTimeSeries(std::move(target_rate_series)); + plot->AppendTimeSeries(std::move(pacing_rate_series)); + plot->AppendTimeSeries(std::move(send_rate_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 100, "Kbps", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated Scream rates"); +} + +void CreateScreamSimulationRefWindowGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + using SendWindowUsage = LogScreamSimulation::State::SendWindowUsage; + TimeSeries ref_window_series("RefWindow", LineStyle::kStep); + TimeSeries ref_window_i_series("RefWindowI", LineStyle::kStep); + TimeSeries max_data_in_flight("Max allowed data in flight", LineStyle::kStep); + TimeSeries max_allowed_ref_window_series("Max allowed ref window", + LineStyle::kStep); + TimeSeries data_in_flight("Data in flight", LineStyle::kStep); + IntervalSeries send_window_above_max_series( + "Data in flight > Max allowed", "#ff8e82", IntervalSeries::kHorizontal); + IntervalSeries send_window_below_ref_window_series( + "Data in flight < RefWindow", "#c5dff2", IntervalSeries::kHorizontal); + IntervalSeries send_window_above_ref_window_series( + "Data in flight >= RefWindow", "#b9fad8", IntervalSeries::kHorizontal); + IntervalSeries* last_series = &send_window_below_ref_window_series; + + LogScreamSimulation simulation({.rate_window = config.window_duration_}, + config.env_); + simulation.ProcessEventsInLog(parsed_log); + if (simulation.updates().empty()) { + RTC_LOG(LS_ERROR) << "Empty simulation."; + return; + } + + float send_window_state_switch = + config.GetCallTimeSec(simulation.updates().front().time); + SendWindowUsage send_window_usage = SendWindowUsage::kBelowRefWindow; + float last_time = config.CallBeginTimeSec(); + for (const LogScreamSimulation::State& state : simulation.updates()) { + ref_window_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.ref_window.bytes()); + ref_window_i_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.ref_window_i.bytes()); + max_data_in_flight.points.emplace_back(config.GetCallTimeSec(state.time), + state.max_data_in_flight.bytes()); + max_allowed_ref_window_series.points.emplace_back( + config.GetCallTimeSec(state.time), + state.max_allowed_ref_window.bytes()); + // Plot the max data in flight before the feedback. + data_in_flight.points.emplace_back(last_time, state.data_in_flight.bytes()); + if (state.send_window_usage != send_window_usage) { + last_series->intervals.emplace_back(send_window_state_switch, + config.GetCallTimeSec(state.time)); + send_window_usage = state.send_window_usage; + send_window_state_switch = config.GetCallTimeSec(state.time); + switch (send_window_usage) { + case SendWindowUsage::kAboveRefWindow: + last_series = &send_window_above_ref_window_series; + break; + case SendWindowUsage::kBelowRefWindow: + last_series = &send_window_below_ref_window_series; + break; + case SendWindowUsage::kAboveScreamMax: + last_series = &send_window_above_max_series; + break; + } + } + last_time = config.GetCallTimeSec(state.time); + } + last_series->intervals.emplace_back(send_window_state_switch, + config.CallEndTimeSec()); + plot->AppendTimeSeries(std::move(ref_window_series)); + plot->AppendTimeSeries(std::move(ref_window_i_series)); + plot->AppendTimeSeries(std::move(max_data_in_flight)); + plot->AppendTimeSeries(std::move(max_allowed_ref_window_series)); + plot->AppendTimeSeries(std::move(data_in_flight)); + plot->AppendIntervalSeries(std::move(send_window_above_max_series)); + plot->AppendIntervalSeries(std::move(send_window_below_ref_window_series)); + plot->AppendIntervalSeries(std::move(send_window_above_ref_window_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Bytes", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated Scream RefWindow"); +} + +void CreateScreamSimulationRatiosGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries queue_delay_dev_norm_series("QueueDelayDevNorm", LineStyle::kStep); + TimeSeries l4s_alpha_series("L4sAlpha", LineStyle::kStep); + TimeSeries l4s_alpha_v_series("L4sAlphaV", LineStyle::kStep); + TimeSeries ref_window_scale_factor_due_to_min_delay_variation( + "RefWindowScaleFactorDueToAvgMinQueueDelay", LineStyle::kStep); + TimeSeries ref_window_scale_factor_due_to_latency_difference( + "RefWindowScaleFactorDueToAvgLatencyDifference", LineStyle::kStep); + TimeSeries ref_window_scale_factor_close_to_ref_window_i( + "RefWindowScaleFactorCloseToRefWindowI", LineStyle::kStep); + TimeSeries ref_window_combined_increase_scale_factor( + "RefWindowCombinedIncreaseScaleFactor", LineStyle::kStep); + + LogScreamSimulation simulation({.rate_window = config.window_duration_}, + config.env_); + simulation.ProcessEventsInLog(parsed_log); + + for (const LogScreamSimulation::State& state : simulation.updates()) { + queue_delay_dev_norm_series.points.emplace_back( + config.GetCallTimeSec(state.time), state.queue_delay_dev_norm); + l4s_alpha_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.l4s_alpha); + l4s_alpha_v_series.points.emplace_back(config.GetCallTimeSec(state.time), + state.l4s_alpha_v); + ref_window_scale_factor_due_to_min_delay_variation.points.emplace_back( + config.GetCallTimeSec(state.time), + state.ref_window_scale_factor_due_to_avg_min_delay); + ref_window_scale_factor_due_to_latency_difference.points.emplace_back( + config.GetCallTimeSec(state.time), + state.ref_window_scale_factor_due_to_latency_difference); + ref_window_scale_factor_close_to_ref_window_i.points.emplace_back( + config.GetCallTimeSec(state.time), + state.ref_window_scale_factor_close_to_ref_window_i); + ref_window_combined_increase_scale_factor.points.emplace_back( + config.GetCallTimeSec(state.time), + state.ref_window_combined_increase_scale_factor); + } + plot->AppendTimeSeries(std::move(queue_delay_dev_norm_series)); + plot->AppendTimeSeries(std::move(l4s_alpha_series)); + plot->AppendTimeSeries(std::move(l4s_alpha_v_series)); + plot->AppendTimeSeries( + std::move(ref_window_scale_factor_due_to_min_delay_variation)); + plot->AppendTimeSeries( + std::move(ref_window_scale_factor_due_to_latency_difference)); + plot->AppendTimeSeries( + std::move(ref_window_scale_factor_close_to_ref_window_i)); + plot->AppendTimeSeries(std::move(ref_window_combined_increase_scale_factor)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Ratios", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated Scream Ratios"); +} + +void CreateScreamRefWindowGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries ref_window_series("RefWindow", LineStyle::kStep); + for (auto& scream_update : parsed_log.bwe_scream_updates()) { + float x = config.GetCallTimeSec(scream_update.log_time()); + float y = static_cast(scream_update.ref_window.bytes()); + ref_window_series.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(ref_window_series)); + + TimeSeries data_in_flight_series("Data in flight", LineStyle::kLine); + for (auto& scream_update : parsed_log.bwe_scream_updates()) { + float x = config.GetCallTimeSec(scream_update.log_time()); + float y = static_cast(scream_update.data_in_flight.bytes()); + data_in_flight_series.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(data_in_flight_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 3000, "Bytes", kBottomMargin, kTopMargin); + plot->SetTitle("Scream Ref Window"); +} + +void CreateScreamDelayEstimateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries smoothed_rtt_series("Smoothed RTT", LineStyle::kStep); + TimeSeries avg_queue_delay_series("Avg queue delay", LineStyle::kStep); + + for (auto& scream_update : parsed_log.bwe_scream_updates()) { + float x = config.GetCallTimeSec(scream_update.log_time()); + float smoothed_rtt_ms = static_cast(scream_update.smoothed_rtt.ms()); + smoothed_rtt_series.points.emplace_back(x, smoothed_rtt_ms); + float avg_queue_delay_ms = + static_cast(scream_update.avg_queue_delay.ms()); + avg_queue_delay_series.points.emplace_back(x, avg_queue_delay_ms); + } + + plot->AppendTimeSeries(std::move(smoothed_rtt_series)); + plot->AppendTimeSeries(std::move(avg_queue_delay_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 50, "Delay (ms)", kBottomMargin, kTopMargin); + plot->SetTitle("Scream delay estimates"); +} + +void CreateSendSideBweSimulationGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + using RtpPacketType = LoggedRtpPacketOutgoing; + using TransportFeedbackType = LoggedRtcpPacketTransportFeedback; + + // TODO(terelius): The parser could provide a clearer view of the + // streams, so that we don't have to recalculate it. + std::multimap outgoing_rtp; + for (const auto& stream : parsed_log.outgoing_rtp_packets_by_ssrc()) { + for (const RtpPacketType& rtp_packet : stream.outgoing_packets) + outgoing_rtp.insert( + std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet)); + } + + const std::vector& incoming_rtcp = + parsed_log.transport_feedbacks(kIncomingPacket); + + SimulatedClock clock(0); + BitrateObserver observer; + TransportFeedbackAdapter transport_feedback; + auto factory = GoogCcNetworkControllerFactory(); + TimeDelta process_interval = factory.GetProcessInterval(); + // TODO(holmer): Log the call config and use that here instead. + static const uint32_t kDefaultStartBitrateBps = 300000; + NetworkControllerConfig cc_config(config.env_); + cc_config.constraints.at_time = clock.CurrentTime(); + cc_config.constraints.starting_rate = + DataRate::BitsPerSec(kDefaultStartBitrateBps); + auto goog_cc = factory.Create(cc_config); + + TimeSeries time_series("Delay-based estimate", LineStyle::kStep, + PointStyle::kHighlight); + TimeSeries acked_time_series("Raw acked bitrate", LineStyle::kLine, + PointStyle::kHighlight); + TimeSeries robust_time_series("Robust throughput estimate", LineStyle::kLine, + PointStyle::kHighlight); + TimeSeries acked_estimate_time_series("Acknowledged bitrate estimate", + LineStyle::kLine, + PointStyle::kHighlight); + + auto rtp_iterator = outgoing_rtp.begin(); + auto rtcp_iterator = incoming_rtcp.begin(); + + auto NextRtpTime = [&]() { + if (rtp_iterator != outgoing_rtp.end()) + return static_cast(rtp_iterator->first); + return std::numeric_limits::max(); + }; + + auto NextRtcpTime = [&]() { + if (rtcp_iterator != incoming_rtcp.end()) + return static_cast(rtcp_iterator->log_time_us()); + return std::numeric_limits::max(); + }; + int64_t next_process_time_us_ = std::min({NextRtpTime(), NextRtcpTime()}); + + auto NextProcessTime = [&]() { + if (rtcp_iterator != incoming_rtcp.end() || + rtp_iterator != outgoing_rtp.end()) { + return next_process_time_us_; + } + return std::numeric_limits::max(); + }; + + RateStatistics raw_acked_bitrate(750, 8000); + FieldTrials throughput_config( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/enabled:true/"); + std::unique_ptr + robust_throughput_estimator( + AcknowledgedBitrateEstimatorInterface::Create(&throughput_config)); + FieldTrials acked_bitrate_config( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/enabled:false/"); + std::unique_ptr + acknowledged_bitrate_estimator( + AcknowledgedBitrateEstimatorInterface::Create(&acked_bitrate_config)); + int64_t time_us = + std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()}); + int64_t last_update_us = 0; + while (time_us != std::numeric_limits::max()) { + clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds()); + if (clock.TimeInMicroseconds() >= NextRtpTime()) { + RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime()); + const RtpPacketType& rtp_packet = *rtp_iterator->second; + if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) { + RtpPacketToSend send_packet(/*extensions=*/nullptr); + send_packet.set_transport_sequence_number( + rtp_packet.rtp.header.extension.transportSequenceNumber); + send_packet.SetSsrc(rtp_packet.rtp.header.ssrc); + send_packet.SetSequenceNumber(rtp_packet.rtp.header.sequenceNumber); + send_packet.SetPayloadSize(rtp_packet.rtp.total_length - + send_packet.headers_size()); + RTC_DCHECK_EQ(send_packet.size(), rtp_packet.rtp.total_length); + if (IsRtxSsrc(parsed_log, PacketDirection::kOutgoingPacket, + rtp_packet.rtp.header.ssrc)) { + // Don't set the optional media type as we don't know if it is + // a retransmission, FEC or padding. + } else if (IsVideoSsrc(parsed_log, PacketDirection::kOutgoingPacket, + rtp_packet.rtp.header.ssrc)) { + send_packet.set_packet_type(RtpPacketMediaType::kVideo); + } else if (IsAudioSsrc(parsed_log, PacketDirection::kOutgoingPacket, + rtp_packet.rtp.header.ssrc)) { + send_packet.set_packet_type(RtpPacketMediaType::kAudio); + } + transport_feedback.AddPacket( + send_packet, PacedPacketInfo(), + 0u, // Per packet overhead bytes., + Timestamp::Micros(rtp_packet.rtp.log_time_us())); + } + SentPacketInfo sent_packet; + sent_packet.send_time_ms = rtp_packet.rtp.log_time_ms(); + sent_packet.info.included_in_allocation = true; + sent_packet.info.packet_size_bytes = rtp_packet.rtp.total_length; + if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) { + sent_packet.packet_id = + rtp_packet.rtp.header.extension.transportSequenceNumber; + sent_packet.info.included_in_feedback = true; + } + auto sent_msg = transport_feedback.ProcessSentPacket(sent_packet); + if (sent_msg) + observer.Update(goog_cc->OnSentPacket(*sent_msg)); + ++rtp_iterator; + } + if (clock.TimeInMicroseconds() >= NextRtcpTime()) { + RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime()); + + auto feedback_msg = transport_feedback.ProcessTransportFeedback( + rtcp_iterator->transport_feedback, clock.CurrentTime()); + if (feedback_msg) { + observer.Update(goog_cc->OnTransportPacketsFeedback(*feedback_msg)); + std::vector feedback = + feedback_msg->SortedByReceiveTime(); + if (!feedback.empty()) { + acknowledged_bitrate_estimator->IncomingPacketFeedbackVector( + feedback); + robust_throughput_estimator->IncomingPacketFeedbackVector(feedback); + for (const PacketResult& packet : feedback) { + raw_acked_bitrate.Update(packet.sent_packet.size.bytes(), + packet.receive_time.ms()); + } + std::optional raw_bitrate_bps = + raw_acked_bitrate.Rate(feedback.back().receive_time.ms()); + float x = config.GetCallTimeSec(clock.CurrentTime()); + if (raw_bitrate_bps) { + float y = raw_bitrate_bps.value() / 1000; + acked_time_series.points.emplace_back(x, y); + } + std::optional robust_estimate = + robust_throughput_estimator->bitrate(); + if (robust_estimate) { + float y = robust_estimate.value().kbps(); + robust_time_series.points.emplace_back(x, y); + } + std::optional acked_estimate = + acknowledged_bitrate_estimator->bitrate(); + if (acked_estimate) { + float y = acked_estimate.value().kbps(); + acked_estimate_time_series.points.emplace_back(x, y); + } + } + } + ++rtcp_iterator; + } + if (clock.TimeInMicroseconds() >= NextProcessTime()) { + RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextProcessTime()); + ProcessInterval msg; + msg.at_time = clock.CurrentTime(); + observer.Update(goog_cc->OnProcessInterval(msg)); + next_process_time_us_ += process_interval.us(); + } + if (observer.GetAndResetBitrateUpdated() || + time_us - last_update_us >= 1e6) { + uint32_t y = observer.last_bitrate_bps() / 1000; + float x = config.GetCallTimeSec(clock.CurrentTime()); + time_series.points.emplace_back(x, y); + last_update_us = time_us; + } + time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()}); + } + // Add the data set to the plot. + plot->AppendTimeSeries(std::move(time_series)); + plot->AppendTimeSeries(std::move(robust_time_series)); + plot->AppendTimeSeries(std::move(acked_time_series)); + plot->AppendTimeSeriesIfNotEmpty(std::move(acked_estimate_time_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated send-side BWE behavior"); +} + +void CreateReceiveSideBweSimulationGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + using RtpPacketType = LoggedRtpPacketIncoming; + class RembInterceptor { + public: + void SendRemb(uint32_t bitrate_bps, std::vector ssrcs) { + last_bitrate_bps_ = bitrate_bps; + bitrate_updated_ = true; + } + uint32_t last_bitrate_bps() const { return last_bitrate_bps_; } + bool GetAndResetBitrateUpdated() { + bool bitrate_updated = bitrate_updated_; + bitrate_updated_ = false; + return bitrate_updated; + } + + private: + // We don't know the start bitrate, but assume that it is the default 300 + // kbps. + uint32_t last_bitrate_bps_ = 300000; + bool bitrate_updated_ = false; + }; + + std::multimap incoming_rtp; + + for (const auto& stream : parsed_log.incoming_rtp_packets_by_ssrc()) { + if (IsVideoSsrc(parsed_log, kIncomingPacket, stream.ssrc)) { + for (const auto& rtp_packet : stream.incoming_packets) + incoming_rtp.insert( + std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet)); + } + } + + SimulatedClock clock(0); + EnvironmentFactory env_factory(config.env_); + env_factory.Set(&clock); + RembInterceptor remb_interceptor; + ReceiveSideCongestionController rscc( + env_factory.Create(), [](auto...) {}, + absl::bind_front(&RembInterceptor::SendRemb, &remb_interceptor)); + // TODO(holmer): Log the call config and use that here instead. + // static const uint32_t kDefaultStartBitrateBps = 300000; + // rscc.SetBweBitrates(0, kDefaultStartBitrateBps, -1); + + TimeSeries time_series("Receive side estimate", LineStyle::kLine, + PointStyle::kHighlight); + TimeSeries acked_time_series("Received bitrate", LineStyle::kLine); + + RateStatistics acked_bitrate(250, 8000); + int64_t last_update_us = 0; + for (const auto& kv : incoming_rtp) { + const RtpPacketType& packet = *kv.second; + + RtpPacketReceived rtp_packet = RtpPacketForBWEFromHeader(packet.rtp.header); + rtp_packet.set_arrival_time(packet.rtp.log_time()); + rtp_packet.SetPayloadSize(packet.rtp.total_length - + rtp_packet.headers_size()); + + clock.AdvanceTime(rtp_packet.arrival_time() - clock.CurrentTime()); + rscc.OnReceivedPacket(rtp_packet, MediaType::VIDEO); + int64_t arrival_time_ms = packet.rtp.log_time().ms(); + acked_bitrate.Update(packet.rtp.total_length, arrival_time_ms); + std::optional bitrate_bps = acked_bitrate.Rate(arrival_time_ms); + if (bitrate_bps) { + uint32_t y = *bitrate_bps / 1000; + float x = config.GetCallTimeSec(clock.CurrentTime()); + acked_time_series.points.emplace_back(x, y); + } + if (remb_interceptor.GetAndResetBitrateUpdated() || + clock.TimeInMicroseconds() - last_update_us >= 1e6) { + uint32_t y = remb_interceptor.last_bitrate_bps() / 1000; + float x = config.GetCallTimeSec(clock.CurrentTime()); + time_series.points.emplace_back(x, y); + last_update_us = clock.TimeInMicroseconds(); + } + } + // Add the data set to the plot. + plot->AppendTimeSeries(std::move(time_series)); + plot->AppendTimeSeries(std::move(acked_time_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated receive-side BWE behavior"); +} + +void CreateNetworkDelayFeedbackGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries time_series("Network delay", LineStyle::kLine, + PointStyle::kHighlight); + int64_t min_send_receive_diff_ms = std::numeric_limits::max(); + int64_t min_rtt_ms = std::numeric_limits::max(); + + std::vector matched_rtp_rtcp = + GetNetworkTrace(parsed_log); + absl::c_stable_sort(matched_rtp_rtcp, [](const MatchedSendArrivalTimes& a, + const MatchedSendArrivalTimes& b) { + return a.feedback_arrival_time_ms < b.feedback_arrival_time_ms || + (a.feedback_arrival_time_ms == b.feedback_arrival_time_ms && + a.arrival_time_ms < b.arrival_time_ms); + }); + for (const auto& packet : matched_rtp_rtcp) { + if (packet.arrival_time_ms == MatchedSendArrivalTimes::kNotReceived) + continue; + float x = config.GetCallTimeSecFromMs(packet.feedback_arrival_time_ms); + int64_t y = packet.arrival_time_ms - packet.send_time_ms; + int64_t rtt_ms = packet.feedback_arrival_time_ms - packet.send_time_ms; + min_rtt_ms = std::min(rtt_ms, min_rtt_ms); + min_send_receive_diff_ms = std::min(y, min_send_receive_diff_ms); + time_series.points.emplace_back(x, y); + } + + // We assume that the base network delay (w/o queues) is equal to half + // the minimum RTT. Therefore rescale the delays by subtracting the minimum + // observed 1-ways delay and add half the minimum RTT. + const int64_t estimated_clock_offset_ms = + min_send_receive_diff_ms - min_rtt_ms / 2; + for (TimeSeriesPoint& point : time_series.points) + point.y -= estimated_clock_offset_ms; + + // Add the data set to the plot. + plot->AppendTimeSeriesIfNotEmpty(std::move(time_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Delay (ms)", kBottomMargin, kTopMargin); + plot->SetTitle("Outgoing network delay (based on per-packet feedback)"); +} + +void CreatePacerDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.outgoing_rtp_packets_by_ssrc()) { + const std::vector& packets = + stream.outgoing_packets; + + if (IsRtxSsrc(parsed_log, kOutgoingPacket, stream.ssrc)) { + continue; + } + + if (packets.size() < 2) { + RTC_LOG(LS_WARNING) + << "Can't estimate the RTP clock frequency or the " + "pacer delay with less than 2 packets in the stream"; + continue; + } + int64_t segment_end_us = parsed_log.first_log_segment().stop_time_us(); + std::optional estimated_frequency = + EstimateRtpClockFrequency(packets, segment_end_us); + if (!estimated_frequency) + continue; + if (IsVideoSsrc(parsed_log, kOutgoingPacket, stream.ssrc) && + *estimated_frequency != 90000) { + RTC_LOG(LS_WARNING) + << "Video stream should use a 90 kHz clock but appears to use " + << *estimated_frequency / 1000 << ". Discarding."; + continue; + } + + TimeSeries pacer_delay_series( + GetStreamName(parsed_log, kOutgoingPacket, stream.ssrc) + "(" + + std::to_string(*estimated_frequency / 1000) + " kHz)", + LineStyle::kLine, PointStyle::kHighlight); + SeqNumUnwrapper timestamp_unwrapper; + uint64_t first_capture_timestamp = + timestamp_unwrapper.Unwrap(packets.front().rtp.header.timestamp); + uint64_t first_send_timestamp = packets.front().rtp.log_time_us(); + for (const auto& packet : packets) { + double capture_time_ms = (static_cast(timestamp_unwrapper.Unwrap( + packet.rtp.header.timestamp)) - + first_capture_timestamp) / + *estimated_frequency * 1000; + double send_time_ms = + static_cast(packet.rtp.log_time_us() - first_send_timestamp) / + 1000; + float x = config.GetCallTimeSec(packet.rtp.log_time()); + float y = send_time_ms - capture_time_ms; + pacer_delay_series.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(pacer_delay_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Pacer delay (ms)", kBottomMargin, kTopMargin); + plot->SetTitle( + "Delay from capture to send time. (First packet normalized to 0.)"); +} + +} // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_bwe.h b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.h new file mode 100644 index 00000000000..5de8598abdf --- /dev/null +++ b/rtc_tools/rtc_event_log_visualizer/analyze_bwe.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_BWE_H_ +#define RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_BWE_H_ + +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" +#include "rtc_tools/rtc_event_log_visualizer/plot_base.h" + +namespace webrtc { + +void CreateIncomingDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateFractionLossGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateTotalIncomingBitrateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateTotalOutgoingBitrateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot, + bool show_detector_state = false, + bool show_alr_state = false, + bool show_link_capacity = false); + +void CreateGoogCcSimulationGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateScreamSimulationDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateScreamSimulationBitrateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); +void CreateScreamSimulationRefWindowGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateScreamSimulationRatiosGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateScreamRefWindowGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateScreamDelayEstimateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateSendSideBweSimulationGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateReceiveSideBweSimulationGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateNetworkDelayFeedbackGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreatePacerDelayGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +} // namespace webrtc + +#endif // RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_BWE_H_ diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_connectivity.cc b/rtc_tools/rtc_event_log_visualizer/analyze_connectivity.cc new file mode 100644 index 00000000000..b87a108b961 --- /dev/null +++ b/rtc_tools/rtc_event_log_visualizer/analyze_connectivity.cc @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_tools/rtc_event_log_visualizer/analyze_connectivity.h" + +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/candidate.h" +#include "api/dtls_transport_interface.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" +#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" +#include "rtc_tools/rtc_event_log_visualizer/plot_base.h" + +namespace webrtc { + +namespace { + +const char kUnknownEnumValue[] = "unknown"; + +// TODO(tommi): This should be "host". +const char kIceCandidateTypeLocal[] = "local"; +// TODO(tommi): This should be "srflx". +const char kIceCandidateTypeStun[] = "stun"; +const char kIceCandidateTypePrflx[] = "prflx"; +const char kIceCandidateTypeRelay[] = "relay"; + +const char kProtocolUdp[] = "udp"; +const char kProtocolTcp[] = "tcp"; +const char kProtocolSsltcp[] = "ssltcp"; +const char kProtocolTls[] = "tls"; + +const char kAddressFamilyIpv4[] = "ipv4"; +const char kAddressFamilyIpv6[] = "ipv6"; + +const char kNetworkTypeEthernet[] = "ethernet"; +const char kNetworkTypeLoopback[] = "loopback"; +const char kNetworkTypeWifi[] = "wifi"; +const char kNetworkTypeVpn[] = "vpn"; +const char kNetworkTypeCellular[] = "cellular"; + +absl::string_view GetIceCandidateTypeAsString(IceCandidateType type) { + switch (type) { + case IceCandidateType::kHost: + return kIceCandidateTypeLocal; + case IceCandidateType::kSrflx: + return kIceCandidateTypeStun; + case IceCandidateType::kPrflx: + return kIceCandidateTypePrflx; + case IceCandidateType::kRelay: + return kIceCandidateTypeRelay; + default: + RTC_DCHECK_NOTREACHED(); + return kUnknownEnumValue; + } +} + +std::string GetProtocolAsString(IceCandidatePairProtocol protocol) { + switch (protocol) { + case IceCandidatePairProtocol::kUdp: + return kProtocolUdp; + case IceCandidatePairProtocol::kTcp: + return kProtocolTcp; + case IceCandidatePairProtocol::kSsltcp: + return kProtocolSsltcp; + case IceCandidatePairProtocol::kTls: + return kProtocolTls; + default: + return kUnknownEnumValue; + } +} + +std::string GetAddressFamilyAsString(IceCandidatePairAddressFamily family) { + switch (family) { + case IceCandidatePairAddressFamily::kIpv4: + return kAddressFamilyIpv4; + case IceCandidatePairAddressFamily::kIpv6: + return kAddressFamilyIpv6; + default: + return kUnknownEnumValue; + } +} + +std::string GetNetworkTypeAsString(IceCandidateNetworkType type) { + switch (type) { + case IceCandidateNetworkType::kEthernet: + return kNetworkTypeEthernet; + case IceCandidateNetworkType::kLoopback: + return kNetworkTypeLoopback; + case IceCandidateNetworkType::kWifi: + return kNetworkTypeWifi; + case IceCandidateNetworkType::kVpn: + return kNetworkTypeVpn; + case IceCandidateNetworkType::kCellular: + return kNetworkTypeCellular; + default: + return kUnknownEnumValue; + } +} + +std::string GetCandidatePairLogDescriptionAsString( + const LoggedIceCandidatePairConfig& config) { + // Example: stun:wifi->relay(tcp):cellular@udp:ipv4 + // represents a pair of a local server-reflexive candidate on a WiFi network + // and a remote relay candidate using TCP as the relay protocol on a cell + // network, when the candidate pair communicates over UDP using IPv4. + StringBuilder ss; + ss << GetIceCandidateTypeAsString(config.local_candidate_type); + + if (config.local_candidate_type == IceCandidateType::kRelay) { + ss << "(" << GetProtocolAsString(config.local_relay_protocol) << ")"; + } + + ss << ":" << GetNetworkTypeAsString(config.local_network_type) << ":" + << GetAddressFamilyAsString(config.local_address_family) << "->" + << GetIceCandidateTypeAsString(config.remote_candidate_type) << ":" + << GetAddressFamilyAsString(config.remote_address_family) << "@" + << GetProtocolAsString(config.candidate_pair_protocol); + return ss.Release(); +} + +std::map BuildCandidateIdLogDescriptionMap( + const std::vector& + ice_candidate_pair_configs) { + std::map candidate_pair_desc_by_id; + for (const auto& config : ice_candidate_pair_configs) { + // TODO(qingsi): Add the handling of the "Updated" config event after the + // visualization of property change for candidate pairs is introduced. + if (candidate_pair_desc_by_id.find(config.candidate_pair_id) == + candidate_pair_desc_by_id.end()) { + const std::string candidate_pair_desc = + GetCandidatePairLogDescriptionAsString(config); + candidate_pair_desc_by_id[config.candidate_pair_id] = candidate_pair_desc; + } + } + return candidate_pair_desc_by_id; +} + +} // namespace + +void CreateIceCandidatePairConfigGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + std::map configs_by_cp_id; + for (const auto& config_item : parsed_log.ice_candidate_pair_configs()) { + if (configs_by_cp_id.find(config_item.candidate_pair_id) == + configs_by_cp_id.end()) { + const std::string candidate_pair_desc = + GetCandidatePairLogDescriptionAsString(config_item); + configs_by_cp_id[config_item.candidate_pair_id] = + TimeSeries("[" + std::to_string(config_item.candidate_pair_id) + "]" + + candidate_pair_desc, + LineStyle::kNone, PointStyle::kHighlight); + } + float x = config.GetCallTimeSec(config_item.log_time()); + float y = static_cast(config_item.type); + configs_by_cp_id[config_item.candidate_pair_id].points.emplace_back(x, y); + } + + // TODO(qingsi): There can be a large number of candidate pairs generated by + // certain calls and the frontend cannot render the chart in this case due + // to the failure of generating a palette with the same number of colors. + for (auto& kv : configs_by_cp_id) { + plot->AppendTimeSeries(std::move(kv.second)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 3, "Config Type", kBottomMargin, kTopMargin); + plot->SetTitle("[IceEventLog] ICE candidate pair configs"); + plot->SetYAxisTickLabels( + {{static_cast(IceCandidatePairConfigType::kAdded), "ADDED"}, + {static_cast(IceCandidatePairConfigType::kUpdated), "UPDATED"}, + {static_cast(IceCandidatePairConfigType::kDestroyed), + "DESTROYED"}, + {static_cast(IceCandidatePairConfigType::kSelected), + "SELECTED"}}); +} + +void CreateIceConnectivityCheckGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + constexpr int kEventTypeOffset = + static_cast(IceCandidatePairConfigType::kNumValues); + std::map checks_by_cp_id; + std::map candidate_pair_desc_by_id = + BuildCandidateIdLogDescriptionMap( + parsed_log.ice_candidate_pair_configs()); + for (const auto& event : parsed_log.ice_candidate_pair_events()) { + if (checks_by_cp_id.find(event.candidate_pair_id) == + checks_by_cp_id.end()) { + checks_by_cp_id[event.candidate_pair_id] = + TimeSeries("[" + std::to_string(event.candidate_pair_id) + "]" + + candidate_pair_desc_by_id[event.candidate_pair_id], + LineStyle::kNone, PointStyle::kHighlight); + } + float x = config.GetCallTimeSec(event.log_time()); + float y = static_cast(event.type) + kEventTypeOffset; + checks_by_cp_id[event.candidate_pair_id].points.emplace_back(x, y); + } + + // TODO(qingsi): The same issue as in CreateIceCandidatePairConfigGraph. + for (auto& kv : checks_by_cp_id) { + plot->AppendTimeSeries(std::move(kv.second)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 4, "Connectivity State", kBottomMargin, + kTopMargin); + plot->SetTitle("[IceEventLog] ICE connectivity checks"); + + plot->SetYAxisTickLabels( + {{static_cast(IceCandidatePairEventType::kCheckSent) + + kEventTypeOffset, + "CHECK SENT"}, + {static_cast(IceCandidatePairEventType::kCheckReceived) + + kEventTypeOffset, + "CHECK RECEIVED"}, + {static_cast(IceCandidatePairEventType::kCheckResponseSent) + + kEventTypeOffset, + "RESPONSE SENT"}, + {static_cast(IceCandidatePairEventType::kCheckResponseReceived) + + kEventTypeOffset, + "RESPONSE RECEIVED"}}); +} + +void CreateDtlsTransportStateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries states("DTLS Transport State", LineStyle::kNone, + PointStyle::kHighlight); + for (const auto& event : parsed_log.dtls_transport_states()) { + float x = config.GetCallTimeSec(event.log_time()); + float y = static_cast(event.dtls_transport_state); + states.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(states)); + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, static_cast(DtlsTransportState::kNumValues), + "Transport State", kBottomMargin, kTopMargin); + plot->SetTitle("DTLS Transport State"); + plot->SetYAxisTickLabels( + {{static_cast(DtlsTransportState::kNew), "NEW"}, + {static_cast(DtlsTransportState::kConnecting), "CONNECTING"}, + {static_cast(DtlsTransportState::kConnected), "CONNECTED"}, + {static_cast(DtlsTransportState::kClosed), "CLOSED"}, + {static_cast(DtlsTransportState::kFailed), "FAILED"}}); +} + +void CreateDtlsWritableStateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + TimeSeries writable("DTLS Writable", LineStyle::kNone, + PointStyle::kHighlight); + for (const auto& event : parsed_log.dtls_writable_states()) { + float x = config.GetCallTimeSec(event.log_time()); + float y = static_cast(event.writable); + writable.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(writable)); + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Writable", kBottomMargin, kTopMargin); + plot->SetTitle("DTLS Writable State"); +} + +} // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_connectivity.h b/rtc_tools/rtc_event_log_visualizer/analyze_connectivity.h new file mode 100644 index 00000000000..1eac2f0c521 --- /dev/null +++ b/rtc_tools/rtc_event_log_visualizer/analyze_connectivity.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_CONNECTIVITY_H_ +#define RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_CONNECTIVITY_H_ + +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" +#include "rtc_tools/rtc_event_log_visualizer/plot_base.h" + +namespace webrtc { + +void CreateIceCandidatePairConfigGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateIceConnectivityCheckGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateDtlsTransportStateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateDtlsWritableStateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +} // namespace webrtc + +#endif // RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_CONNECTIVITY_H_ diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.cc b/rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.cc new file mode 100644 index 00000000000..9d6c0668e83 --- /dev/null +++ b/rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.cc @@ -0,0 +1,809 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api/function_view.h" +#include "api/rtp_headers.h" +#include "api/transport/ecn_marking.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "logging/rtc_event_log/rtc_event_processor.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h" +#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" +#include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" +#include "rtc_tools/rtc_event_log_visualizer/plot_base.h" + +namespace webrtc { + +namespace { + +template +TimeSeries CreateRtcpTypeTimeSeries(const std::vector& rtcp_list, + AnalyzerConfig config, + std::string rtcp_name, + int category_id) { + TimeSeries time_series(rtcp_name, LineStyle::kNone, PointStyle::kHighlight); + for (const auto& rtcp : rtcp_list) { + float x = config.GetCallTimeSec(rtcp.timestamp); + float y = category_id; + time_series.points.emplace_back(x, y); + } + return time_series; +} + +struct PacketLossSummary { + size_t num_packets = 0; + size_t num_lost_packets = 0; + Timestamp base_time = Timestamp::MinusInfinity(); +}; + +} // namespace + +float GetHighestSeqNumber(const rtcp::ReportBlock& block) { + return block.extended_high_seq_num(); +} + +float GetFractionLost(const rtcp::ReportBlock& block) { + return static_cast(block.fraction_lost()) / 256 * 100; +} + +float GetCumulativeLost(const rtcp::ReportBlock& block) { + return block.cumulative_lost(); +} + +float DelaySinceLastSr(const rtcp::ReportBlock& block) { + return static_cast(block.delay_since_last_sr()) / 65536; +} + +void CreatePacketGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.rtp_packets_by_ssrc(direction)) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, config.desired_ssrc_)) { + continue; + } + + TimeSeries time_series(GetStreamName(parsed_log, direction, stream.ssrc), + LineStyle::kBar); + auto GetPacketSize = [](const LoggedRtpPacket& packet) { + return std::optional(packet.total_length); + }; + auto ToCallTime = [&config](const LoggedRtpPacket& packet) { + return config.GetCallTimeSec(packet.timestamp); + }; + ProcessPoints(ToCallTime, GetPacketSize, + stream.packet_view, &time_series); + plot->AppendTimeSeries(std::move(time_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Packet size (bytes)", kBottomMargin, + kTopMargin); + plot->SetTitle(GetDirectionAsString(direction) + " RTP packets"); +} + +void CreateRtcpTypeGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( + parsed_log.transport_feedbacks(direction), config, "TWCC", 1)); + plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( + parsed_log.congestion_feedback(direction), config, "CCFB", 2)); + plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( + parsed_log.receiver_reports(direction), config, "RR", 3)); + plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( + parsed_log.sender_reports(direction), config, "SR", 4)); + plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( + parsed_log.extended_reports(direction), config, "XR", 5)); + plot->AppendTimeSeries( + CreateRtcpTypeTimeSeries(parsed_log.nacks(direction), config, "NACK", 6)); + plot->AppendTimeSeries( + CreateRtcpTypeTimeSeries(parsed_log.rembs(direction), config, "REMB", 7)); + plot->AppendTimeSeries( + CreateRtcpTypeTimeSeries(parsed_log.firs(direction), config, "FIR", 8)); + plot->AppendTimeSeries( + CreateRtcpTypeTimeSeries(parsed_log.plis(direction), config, "PLI", 9)); + plot->AppendTimeSeries( + CreateRtcpTypeTimeSeries(parsed_log.byes(direction), config, "BYE", 10)); + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "RTCP type", kBottomMargin, kTopMargin); + plot->SetTitle(GetDirectionAsString(direction) + " RTCP packets"); + plot->SetYAxisTickLabels({{1, "TWCC"}, + {2, "CCFB"}, + {3, "RR"}, + {4, "SR"}, + {5, "XR"}, + {6, "NACK"}, + {7, "REMB"}, + {8, "FIR"}, + {9, "PLI"}, + {10, "BYE"}}); +} + +template +void CreateAccumulatedPacketsTimeSeries(Plot* plot, + const AnalyzerConfig& config, + const IterableType& packets, + const std::string& label) { + TimeSeries time_series(label, LineStyle::kStep); + for (size_t i = 0; i < packets.size(); i++) { + float x = config.GetCallTimeSec(packets[i].log_time()); + time_series.points.emplace_back(x, i + 1); + } + plot->AppendTimeSeries(std::move(time_series)); +} + +void CreateAccumulatedPacketsGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.rtp_packets_by_ssrc(direction)) { + if (!MatchingSsrc(stream.ssrc, config.desired_ssrc_)) + continue; + std::string label = + std::string("RTP ") + GetStreamName(parsed_log, direction, stream.ssrc); + CreateAccumulatedPacketsTimeSeries(plot, config, stream.packet_view, label); + } + std::string label = + std::string("RTCP ") + "(" + GetDirectionAsShortString(direction) + ")"; + if (direction == kIncomingPacket) { + CreateAccumulatedPacketsTimeSeries( + plot, config, parsed_log.incoming_rtcp_packets(), label); + } else { + CreateAccumulatedPacketsTimeSeries( + plot, config, parsed_log.outgoing_rtcp_packets(), label); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Received Packets", kBottomMargin, kTopMargin); + plot->SetTitle(std::string("Accumulated ") + GetDirectionAsString(direction) + + " RTP/RTCP packets"); +} + +void CreatePacketRateGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + auto CountPackets = [](auto packet) { return 1.0; }; + for (const auto& stream : parsed_log.rtp_packets_by_ssrc(direction)) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, config.desired_ssrc_)) { + continue; + } + TimeSeries time_series( + std::string("RTP ") + GetStreamName(parsed_log, direction, stream.ssrc), + LineStyle::kLine); + MovingAverage(CountPackets, stream.packet_view, + config, &time_series); + plot->AppendTimeSeries(std::move(time_series)); + } + TimeSeries time_series( + std::string("RTCP ") + "(" + GetDirectionAsShortString(direction) + ")", + LineStyle::kLine); + if (direction == kIncomingPacket) { + MovingAverage( + CountPackets, parsed_log.incoming_rtcp_packets(), config, &time_series); + } else { + MovingAverage( + CountPackets, parsed_log.outgoing_rtcp_packets(), config, &time_series); + } + plot->AppendTimeSeries(std::move(time_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Packet Rate (packets/s)", kBottomMargin, + kTopMargin); + plot->SetTitle("Rate of " + GetDirectionAsString(direction) + + " RTP/RTCP packets"); +} + +void CreateTotalPacketRateGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + // Contains a log timestamp to enable counting logged events of different + // types using MovingAverage(). + class LogTime { + public: + explicit LogTime(Timestamp log_time) : log_time_(log_time) {} + Timestamp log_time() const { return log_time_; } + + private: + Timestamp log_time_; + }; + std::vector packet_times; + auto handle_rtp = [&packet_times](const LoggedRtpPacket& packet) { + packet_times.emplace_back(packet.log_time()); + }; + RtcEventProcessor process; + for (const auto& stream : parsed_log.rtp_packets_by_ssrc(direction)) { + process.AddEvents(stream.packet_view, handle_rtp, direction); + } + if (direction == kIncomingPacket) { + auto handle_incoming_rtcp = + [&packet_times](const LoggedRtcpPacketIncoming& packet) { + packet_times.emplace_back(packet.log_time()); + }; + process.AddEvents(parsed_log.incoming_rtcp_packets(), handle_incoming_rtcp); + } else { + auto handle_outgoing_rtcp = + [&packet_times](const LoggedRtcpPacketOutgoing& packet) { + packet_times.emplace_back(packet.log_time()); + }; + process.AddEvents(parsed_log.outgoing_rtcp_packets(), handle_outgoing_rtcp); + } + process.ProcessEventsInOrder(); + TimeSeries time_series(std::string("Total ") + "(" + + GetDirectionAsShortString(direction) + ") packets", + LineStyle::kLine); + MovingAverage([](auto packet) { return 1; }, packet_times, + config, &time_series); + plot->AppendTimeSeries(std::move(time_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Packet Rate (packets/s)", kBottomMargin, + kTopMargin); + plot->SetTitle("Rate of all " + GetDirectionAsString(direction) + + " RTP/RTCP packets"); +} + +// For each SSRC, plot the sequence number difference between consecutive +// incoming packets. +void CreateSequenceNumberGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.incoming_rtp_packets_by_ssrc()) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, config.desired_ssrc_)) { + continue; + } + + TimeSeries time_series( + GetStreamName(parsed_log, kIncomingPacket, stream.ssrc), + LineStyle::kBar); + auto GetSequenceNumberDiff = [](const LoggedRtpPacketIncoming& old_packet, + const LoggedRtpPacketIncoming& new_packet) { + int64_t diff = + WrappingDifference(new_packet.rtp.header.sequenceNumber, + old_packet.rtp.header.sequenceNumber, 1ul << 16); + return diff; + }; + auto ToCallTime = [&config](const LoggedRtpPacketIncoming& packet) { + return config.GetCallTimeSec(packet.log_time()); + }; + ProcessPairs( + ToCallTime, GetSequenceNumberDiff, stream.incoming_packets, + &time_series); + plot->AppendTimeSeries(std::move(time_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Difference since last packet", kBottomMargin, + kTopMargin); + plot->SetTitle("Incoming sequence number delta"); +} + +void CreateIncomingPacketLossGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.incoming_rtp_packets_by_ssrc()) { + const std::vector& packets = + stream.incoming_packets; + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, config.desired_ssrc_) || packets.empty()) { + continue; + } + + TimeSeries time_series( + GetStreamName(parsed_log, kIncomingPacket, stream.ssrc), + LineStyle::kLine, PointStyle::kHighlight); + // TODO(terelius): Should the window and step size be read from the class + // instead? + const TimeDelta kWindow = TimeDelta::Millis(1000); + const TimeDelta kStep = TimeDelta::Millis(1000); + SeqNumUnwrapper unwrapper_; + SeqNumUnwrapper prior_unwrapper_; + size_t window_index_begin = 0; + size_t window_index_end = 0; + uint64_t highest_seq_number = + unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1; + uint64_t highest_prior_seq_number = + prior_unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1; + + for (Timestamp t = config.begin_time_; t < config.end_time_ + kStep; + t += kStep) { + while (window_index_end < packets.size() && + packets[window_index_end].rtp.log_time() < t) { + uint64_t sequence_number = unwrapper_.Unwrap( + packets[window_index_end].rtp.header.sequenceNumber); + highest_seq_number = std::max(highest_seq_number, sequence_number); + ++window_index_end; + } + while (window_index_begin < packets.size() && + packets[window_index_begin].rtp.log_time() < t - kWindow) { + uint64_t sequence_number = prior_unwrapper_.Unwrap( + packets[window_index_begin].rtp.header.sequenceNumber); + highest_prior_seq_number = + std::max(highest_prior_seq_number, sequence_number); + ++window_index_begin; + } + float x = config.GetCallTimeSec(t); + uint64_t expected_packets = highest_seq_number - highest_prior_seq_number; + if (expected_packets > 0) { + int64_t received_packets = window_index_end - window_index_begin; + int64_t lost_packets = expected_packets - received_packets; + float y = static_cast(lost_packets) / expected_packets * 100; + time_series.points.emplace_back(x, y); + } + } + plot->AppendTimeSeries(std::move(time_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Loss rate (in %)", kBottomMargin, kTopMargin); + plot->SetTitle("Incoming packet loss (derived from incoming packets)"); +} + +// For each SSRC, plot the bandwidth used by that stream. +void CreateStreamBitrateGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.rtp_packets_by_ssrc(direction)) { + // Filter on SSRC. + if (!MatchingSsrc(stream.ssrc, config.desired_ssrc_)) { + continue; + } + + TimeSeries time_series(GetStreamName(parsed_log, direction, stream.ssrc), + LineStyle::kLine); + auto GetPacketSizeKilobits = [](const LoggedRtpPacket& packet) { + return packet.total_length * 8.0 / 1000.0; + }; + MovingAverage( + GetPacketSizeKilobits, stream.packet_view, config, &time_series); + plot->AppendTimeSeries(std::move(time_series)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle(GetDirectionAsString(direction) + " bitrate per stream"); +} + +// Plot the bitrate allocation for each temporal and spatial layer. +// Computed from RTCP XR target bitrate block, so the graph is only populated if +// those are sent. +void CreateBitrateAllocationGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + std::map time_series; + const auto& xr_list = parsed_log.extended_reports(direction); + for (const auto& rtcp : xr_list) { + const std::optional& target_bitrate = + rtcp.xr.target_bitrate(); + if (!target_bitrate.has_value()) + continue; + for (const auto& bitrate_item : target_bitrate->GetTargetBitrates()) { + LayerDescription layer(rtcp.xr.sender_ssrc(), bitrate_item.spatial_layer, + bitrate_item.temporal_layer); + auto time_series_it = time_series.find(layer); + if (time_series_it == time_series.end()) { + std::string layer_name = GetLayerName(layer); + bool inserted; + std::tie(time_series_it, inserted) = time_series.insert( + std::make_pair(layer, TimeSeries(layer_name, LineStyle::kStep))); + RTC_DCHECK(inserted); + } + float x = config.GetCallTimeSec(rtcp.log_time()); + float y = bitrate_item.target_bitrate_kbps; + time_series_it->second.points.emplace_back(x, y); + } + } + for (auto& layer : time_series) { + plot->AppendTimeSeries(std::move(layer.second)); + } + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); + if (direction == kIncomingPacket) + plot->SetTitle("Target bitrate per incoming layer"); + else + plot->SetTitle("Target bitrate per outgoing layer"); +} + +void CreateEcnFeedbackGraph(Plot* plot, + PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config) { + TimeSeries not_ect("Not ECN capable", LineStyle::kBar, + PointStyle::kHighlight); + TimeSeries ect_1("ECN capable", LineStyle::kBar, PointStyle::kHighlight); + TimeSeries ce("Congestion experienced", LineStyle::kBar, + PointStyle::kHighlight); + + for (const LoggedRtcpCongestionControlFeedback& feedback : + parsed_log.congestion_feedback(direction)) { + int ect_1_count = 0; + int not_ect_count = 0; + int ce_count = 0; + + for (const rtcp::CongestionControlFeedback::PacketInfo& info : + feedback.congestion_feedback.packets()) { + switch (info.ecn) { + case EcnMarking::kNotEct: + ++not_ect_count; + break; + case EcnMarking::kEct1: + ++ect_1_count; + break; + case EcnMarking::kEct0: + RTC_LOG(LS_ERROR) << "unexpected ect(0)"; + break; + case EcnMarking::kCe: + ++ce_count; + break; + } + } + ect_1.points.emplace_back(config.GetCallTimeSec(feedback.timestamp), + ect_1_count); + not_ect.points.emplace_back(config.GetCallTimeSec(feedback.timestamp), + not_ect_count); + ce.points.emplace_back(config.GetCallTimeSec(feedback.timestamp), ce_count); + } + + plot->AppendTimeSeriesIfNotEmpty(std::move(ect_1)); + plot->AppendTimeSeriesIfNotEmpty(std::move(not_ect)); + plot->AppendTimeSeriesIfNotEmpty(std::move(ce)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Count per feedback", kBottomMargin, + kTopMargin); +} + +void CreateOutgoingEcnFeedbackGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + CreateEcnFeedbackGraph(plot, kOutgoingPacket, parsed_log, config); + plot->SetTitle("Outgoing ECN count per feedback"); +} + +void CreateIncomingEcnFeedbackGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + CreateEcnFeedbackGraph(plot, kIncomingPacket, parsed_log, config); + plot->SetTitle("Incoming ECN count per feedback"); +} + +void CreateOutgoingLossRateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + struct PacketLossPerFeedback { + Timestamp timestamp; // Time when this feedback was received. + int num_packets_in_feedback = 0; // Includes lost packets. + int num_lost_packets = 0; // In this specific feedback. + int num_reordered_packets = 0; // Packets received in this feedback, but + // was previously reported as lost. + int num_missing_feedback = + 0; // Packets missing feedback between this report and the previous. + }; + + class LossFeedbackBuilder { + public: + void AddPacket(uint16_t sequence_number, TimeDelta arrival_time_delta) { + last_unwrapped_sequence_number_ = + sequence_number_unwrapper_.Unwrap(sequence_number); + if (!first_sequence_number_.has_value()) { + first_sequence_number_ = last_unwrapped_sequence_number_; + } + ++num_packets_; + if (arrival_time_delta.IsInfinite()) { + lost_sequence_numbers_.insert(last_unwrapped_sequence_number_); + } else { + num_reordered_packets_ += previous_lost_sequence_numbers_.count( + last_unwrapped_sequence_number_); + } + } + + void Update(PacketLossPerFeedback& feedback) { + feedback.num_packets_in_feedback += num_packets_; + feedback.num_lost_packets += lost_sequence_numbers_.size(); + feedback.num_reordered_packets += num_reordered_packets_; + if (first_sequence_number_.has_value() && + previous_feedback_highest_seq_number_.has_value()) { + feedback.num_missing_feedback += + *first_sequence_number_ - *previous_feedback_highest_seq_number_ - + 1; + } + + // Prepare for next feedback. + first_sequence_number_ = std::nullopt; + previous_lost_sequence_numbers_.insert(lost_sequence_numbers_.begin(), + lost_sequence_numbers_.end()); + previous_feedback_highest_seq_number_ = last_unwrapped_sequence_number_; + lost_sequence_numbers_.clear(); + num_reordered_packets_ = 0; + num_packets_ = 0; + } + + private: + int64_t last_unwrapped_sequence_number_ = 0; + int num_reordered_packets_ = 0; + int num_packets_ = 0; + std::optional first_sequence_number_; + + std::unordered_set lost_sequence_numbers_; + std::unordered_set previous_lost_sequence_numbers_; + std::optional previous_feedback_highest_seq_number_; + + RtpSequenceNumberUnwrapper sequence_number_unwrapper_; + }; + + TimeSeries loss_rate_series("Loss rate (from packet feedback)", + LineStyle::kLine, PointStyle::kHighlight); + TimeSeries reordered_packets_between_feedback( + "Ratio of reordered packets from last feedback", LineStyle::kLine, + PointStyle::kHighlight); + TimeSeries average_loss_rate_series("Average loss rate last 5s", + LineStyle::kLine, PointStyle::kHighlight); + TimeSeries missing_feedback_series("Missing feedback", LineStyle::kNone, + PointStyle::kHighlight); + + std::vector loss_per_feedback; + + if (!parsed_log.congestion_feedback(kIncomingPacket).empty()) { + plot->SetTitle("Outgoing loss rate (from CCFB)"); + + std::map per_ssrc_builder; + for (const LoggedRtcpCongestionControlFeedback& feedback : + parsed_log.congestion_feedback(kIncomingPacket)) { + const rtcp::CongestionControlFeedback& transport_feedback = + feedback.congestion_feedback; + + PacketLossPerFeedback packet_loss_per_feedback = { + .timestamp = feedback.log_time()}; + for (const rtcp::CongestionControlFeedback::PacketInfo& packet : + transport_feedback.packets()) { + per_ssrc_builder[packet.ssrc].AddPacket(packet.sequence_number, + packet.arrival_time_offset); + } + for (auto& [ssrc, builder] : per_ssrc_builder) { + builder.Update(packet_loss_per_feedback); + } + loss_per_feedback.push_back(packet_loss_per_feedback); + } + } else if (!parsed_log.transport_feedbacks(kIncomingPacket).empty()) { + plot->SetTitle("Outgoing loss rate (from TWCC)"); + + LossFeedbackBuilder builder; + for (const LoggedRtcpPacketTransportFeedback& feedback : + parsed_log.transport_feedbacks(kIncomingPacket)) { + feedback.transport_feedback.ForAllPackets( + [&](uint16_t sequence_number, TimeDelta receive_time_delta) { + builder.AddPacket(sequence_number, receive_time_delta); + }); + PacketLossPerFeedback packet_loss_per_feedback = { + .timestamp = feedback.log_time()}; + builder.Update(packet_loss_per_feedback); + loss_per_feedback.push_back(packet_loss_per_feedback); + } + } + + PacketLossSummary window_summary; + Timestamp last_observation_receive_time = Timestamp::Zero(); + + // Use loss based bwe 2 observation duration and observation window size. + constexpr TimeDelta kObservationDuration = TimeDelta::Millis(250); + constexpr uint32_t kObservationWindowSize = 20; + std::deque observations; + int previous_feedback_size = 0; + for (const PacketLossPerFeedback& feedback : loss_per_feedback) { + for (int64_t num = 0; num < feedback.num_missing_feedback; ++num) { + missing_feedback_series.points.emplace_back( + config.GetCallTimeSec(feedback.timestamp), 100 + num); + } + + // Compute loss rate from the transport feedback. + float loss_rate = static_cast(feedback.num_lost_packets * 100.0 / + feedback.num_packets_in_feedback); + + loss_rate_series.points.emplace_back( + config.GetCallTimeSec(feedback.timestamp), loss_rate); + float reordered_rate = + previous_feedback_size == 0 + ? 0 + : static_cast(feedback.num_reordered_packets * 100.0 / + previous_feedback_size); + previous_feedback_size = feedback.num_packets_in_feedback; + reordered_packets_between_feedback.points.emplace_back( + config.GetCallTimeSec(feedback.timestamp), reordered_rate); + + // Compute loss rate in a window of kObservationWindowSize. + if (window_summary.num_packets == 0) { + window_summary.base_time = feedback.timestamp; + } + window_summary.num_packets += feedback.num_packets_in_feedback; + window_summary.num_lost_packets += + feedback.num_lost_packets - feedback.num_reordered_packets; + + const Timestamp last_received_time = feedback.timestamp; + const TimeDelta observation_duration = + window_summary.base_time == Timestamp::Zero() + ? TimeDelta::Zero() + : last_received_time - window_summary.base_time; + if (observation_duration > kObservationDuration) { + last_observation_receive_time = last_received_time; + observations.push_back(window_summary); + if (observations.size() > kObservationWindowSize) { + observations.pop_front(); + } + + // Compute average loss rate in a number of windows. + int total_packets = 0; + int total_loss = 0; + for (const auto& observation : observations) { + total_loss += observation.num_lost_packets; + total_packets += observation.num_packets; + } + if (total_packets > 0) { + float average_loss_rate = total_loss * 100.0 / total_packets; + average_loss_rate_series.points.emplace_back( + config.GetCallTimeSec(feedback.timestamp), average_loss_rate); + } else { + average_loss_rate_series.points.emplace_back( + config.GetCallTimeSec(feedback.timestamp), 0); + } + window_summary = PacketLossSummary(); + } + } + // Add the data set to the plot. + plot->AppendTimeSeriesIfNotEmpty(std::move(loss_rate_series)); + plot->AppendTimeSeriesIfNotEmpty( + std::move(reordered_packets_between_feedback)); + plot->AppendTimeSeriesIfNotEmpty(std::move(average_loss_rate_series)); + plot->AppendTimeSeriesIfNotEmpty(std::move(missing_feedback_series)); + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 100, "Loss rate (percent)", kBottomMargin, + kTopMargin); +} + +void CreateTimestampGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + for (const auto& stream : parsed_log.rtp_packets_by_ssrc(direction)) { + TimeSeries rtp_timestamps( + GetStreamName(parsed_log, direction, stream.ssrc) + " capture-time", + LineStyle::kLine, PointStyle::kHighlight); + for (const auto& packet : stream.packet_view) { + float x = config.GetCallTimeSec(packet.log_time()); + float y = packet.header.timestamp; + rtp_timestamps.points.emplace_back(x, y); + } + plot->AppendTimeSeries(std::move(rtp_timestamps)); + + TimeSeries rtcp_timestamps( + GetStreamName(parsed_log, direction, stream.ssrc) + + " rtcp capture-time", + LineStyle::kLine, PointStyle::kHighlight); + // TODO(terelius): Why only sender reports? + const auto& sender_reports = parsed_log.sender_reports(direction); + for (const auto& rtcp : sender_reports) { + if (rtcp.sr.sender_ssrc() != stream.ssrc) + continue; + float x = config.GetCallTimeSec(rtcp.log_time()); + float y = rtcp.sr.rtp_timestamp(); + rtcp_timestamps.points.emplace_back(x, y); + } + plot->AppendTimeSeriesIfNotEmpty(std::move(rtcp_timestamps)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "RTP timestamp", kBottomMargin, kTopMargin); + plot->SetTitle(GetDirectionAsString(direction) + " timestamps"); +} + +void CreateSenderAndReceiverReportPlot( + PacketDirection direction, + FunctionView fy, + std::string title, + std::string yaxis_label, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot) { + std::map sr_reports_by_ssrc; + const auto& sender_reports = parsed_log.sender_reports(direction); + for (const auto& rtcp : sender_reports) { + float x = config.GetCallTimeSec(rtcp.log_time()); + uint32_t ssrc = rtcp.sr.sender_ssrc(); + for (const auto& block : rtcp.sr.report_blocks()) { + float y = fy(block); + auto sr_report_it = sr_reports_by_ssrc.find(ssrc); + bool inserted; + if (sr_report_it == sr_reports_by_ssrc.end()) { + std::tie(sr_report_it, inserted) = sr_reports_by_ssrc.emplace( + ssrc, TimeSeries(GetStreamName(parsed_log, direction, ssrc) + + " Sender Reports", + LineStyle::kLine, PointStyle::kHighlight)); + } + sr_report_it->second.points.emplace_back(x, y); + } + } + for (auto& kv : sr_reports_by_ssrc) { + plot->AppendTimeSeries(std::move(kv.second)); + } + + std::map rr_reports_by_ssrc; + const auto& receiver_reports = parsed_log.receiver_reports(direction); + for (const auto& rtcp : receiver_reports) { + float x = config.GetCallTimeSec(rtcp.log_time()); + uint32_t ssrc = rtcp.rr.sender_ssrc(); + for (const auto& block : rtcp.rr.report_blocks()) { + float y = fy(block); + auto rr_report_it = rr_reports_by_ssrc.find(ssrc); + bool inserted; + if (rr_report_it == rr_reports_by_ssrc.end()) { + std::tie(rr_report_it, inserted) = rr_reports_by_ssrc.emplace( + ssrc, TimeSeries(GetStreamName(parsed_log, direction, ssrc) + + " Receiver Reports", + LineStyle::kLine, PointStyle::kHighlight)); + } + rr_report_it->second.points.emplace_back(x, y); + } + } + for (auto& kv : rr_reports_by_ssrc) { + plot->AppendTimeSeries(std::move(kv.second)); + } + + plot->SetXAxis(config.CallBeginTimeSec(), config.CallEndTimeSec(), "Time (s)", + kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, yaxis_label, kBottomMargin, kTopMargin); + plot->SetTitle(title); +} + +} // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.h b/rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.h new file mode 100644 index 00000000000..2865023e609 --- /dev/null +++ b/rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_RTP_RTCP_H_ +#define RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_RTP_RTCP_H_ + +#include + +#include "api/function_view.h" +#include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" +#include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" +#include "rtc_tools/rtc_event_log_visualizer/plot_base.h" + +namespace webrtc { + +float GetHighestSeqNumber(const rtcp::ReportBlock& block); +float GetFractionLost(const rtcp::ReportBlock& block); +float GetCumulativeLost(const rtcp::ReportBlock& block); +float DelaySinceLastSr(const rtcp::ReportBlock& block); + +void CreatePacketGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateRtcpTypeGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateAccumulatedPacketsGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreatePacketRateGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateTotalPacketRateGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateSequenceNumberGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateIncomingPacketLossGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateStreamBitrateGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateBitrateAllocationGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateOutgoingEcnFeedbackGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateIncomingEcnFeedbackGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateOutgoingLossRateGraph(const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateTimestampGraph(PacketDirection direction, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +void CreateSenderAndReceiverReportPlot( + PacketDirection direction, + FunctionView fy, + std::string title, + std::string yaxis_label, + const ParsedRtcEventLog& parsed_log, + const AnalyzerConfig& config, + Plot* plot); + +} // namespace webrtc + +#endif // RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZE_RTP_RTCP_H_ diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.cc b/rtc_tools/rtc_event_log_visualizer/analyzer.cc index 55eb5614063..5821ad7bd4f 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.cc @@ -10,524 +10,87 @@ #include "rtc_tools/rtc_event_log_visualizer/analyzer.h" -#include -#include #include -#include -#include -#include -#include #include -#include #include -#include -#include -#include #include #include "absl/algorithm/container.h" -#include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" -#include "api/candidate.h" -#include "api/dtls_transport_interface.h" -#include "api/environment/environment.h" #include "api/environment/environment_factory.h" -#include "api/field_trials.h" #include "api/function_view.h" -#include "api/media_types.h" -#include "api/rtp_headers.h" -#include "api/transport/bandwidth_usage.h" -#include "api/transport/ecn_marking.h" -#include "api/transport/goog_cc_factory.h" -#include "api/transport/network_control.h" -#include "api/transport/network_types.h" +#include "api/neteq/neteq.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" -#include "logging/rtc_event_log/events/logged_rtp_rtcp.h" -#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" -#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" -#include "logging/rtc_event_log/rtc_event_processor.h" -#include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h" -#include "modules/congestion_controller/include/receive_side_congestion_controller.h" -#include "modules/congestion_controller/rtp/transport_feedback_adapter.h" -#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" -#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" -#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" -#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h" -#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" -#include "modules/rtp_rtcp/source/rtcp_packet/remb.h" #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" -#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" -#include "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h" -#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" -#include "modules/rtp_rtcp/source/rtp_header_extensions.h" -#include "modules/rtp_rtcp/source/rtp_packet_received.h" -#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" -#include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "rtc_base/network/sent_packet.h" -#include "rtc_base/numerics/sequence_number_unwrapper.h" -#include "rtc_base/rate_statistics.h" -#include "rtc_base/strings/string_builder.h" #include "rtc_tools/rtc_event_log_visualizer/analyze_audio.h" +#include "rtc_tools/rtc_event_log_visualizer/analyze_bwe.h" +#include "rtc_tools/rtc_event_log_visualizer/analyze_connectivity.h" +#include "rtc_tools/rtc_event_log_visualizer/analyze_rtp_rtcp.h" #include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" -#include "rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h" -#include "rtc_tools/rtc_event_log_visualizer/log_simulation.h" #include "rtc_tools/rtc_event_log_visualizer/plot_base.h" -#include "system_wrappers/include/clock.h" namespace webrtc { -namespace { - -std::string SsrcToString(uint32_t ssrc) { - StringBuilder ss; - ss << "SSRC " << ssrc; - return ss.Release(); -} - -// Checks whether an SSRC is contained in the list of desired SSRCs. -// Note that an empty SSRC list matches every SSRC. -bool MatchingSsrc(uint32_t ssrc, const std::vector& desired_ssrc) { - if (desired_ssrc.empty()) - return true; - return std::find(desired_ssrc.begin(), desired_ssrc.end(), ssrc) != - desired_ssrc.end(); -} - -double AbsSendTimeToMicroseconds(int64_t abs_send_time) { - // The timestamp is a fixed point representation with 6 bits for seconds - // and 18 bits for fractions of a second. Thus, we divide by 2^18 to get the - // time in seconds and then multiply by kNumMicrosecsPerSec to convert to - // microseconds. - static constexpr double kTimestampToMicroSec = - static_cast(kNumMicrosecsPerSec) / static_cast(1ul << 18); - return abs_send_time * kTimestampToMicroSec; -} - -// Computes the difference `later` - `earlier` where `later` and `earlier` -// are counters that wrap at `modulus`. The difference is chosen to have the -// least absolute value. For example if `modulus` is 8, then the difference will -// be chosen in the range [-3, 4]. If `modulus` is 9, then the difference will -// be in [-4, 4]. -int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) { - RTC_DCHECK_LE(1, modulus); - RTC_DCHECK_LT(later, modulus); - RTC_DCHECK_LT(earlier, modulus); - int64_t difference = - static_cast(later) - static_cast(earlier); - int64_t max_difference = modulus / 2; - int64_t min_difference = max_difference - modulus + 1; - if (difference > max_difference) { - difference -= modulus; - } - if (difference < min_difference) { - difference += modulus; - } - if (difference > max_difference / 2 || difference < min_difference / 2) { - RTC_LOG(LS_WARNING) << "Difference between" << later << " and " << earlier - << " expected to be in the range (" - << min_difference / 2 << "," << max_difference / 2 - << ") but is " << difference - << ". Correct unwrapping is uncertain."; - } - return difference; -} - -// This is much more reliable for outgoing streams than for incoming streams. -template -std::optional EstimateRtpClockFrequency( - const RtpPacketContainer& packets, - int64_t end_time_us) { - RTC_CHECK(packets.size() >= 2); - SeqNumUnwrapper unwrapper; - int64_t first_rtp_timestamp = - unwrapper.Unwrap(packets[0].rtp.header.timestamp); - int64_t first_log_timestamp = packets[0].log_time_us(); - int64_t last_rtp_timestamp = first_rtp_timestamp; - int64_t last_log_timestamp = first_log_timestamp; - for (size_t i = 1; i < packets.size(); i++) { - if (packets[i].log_time_us() > end_time_us) - break; - last_rtp_timestamp = unwrapper.Unwrap(packets[i].rtp.header.timestamp); - last_log_timestamp = packets[i].log_time_us(); - } - if (last_log_timestamp - first_log_timestamp < kNumMicrosecsPerSec) { - RTC_LOG(LS_WARNING) - << "Failed to estimate RTP clock frequency: Stream too short. (" - << packets.size() << " packets, " - << last_log_timestamp - first_log_timestamp << " us)"; - return std::nullopt; - } - double duration = - static_cast(last_log_timestamp - first_log_timestamp) / - kNumMicrosecsPerSec; - double estimated_frequency = - (last_rtp_timestamp - first_rtp_timestamp) / duration; - for (uint32_t f : {8000, 16000, 32000, 48000, 90000}) { - if (std::fabs(estimated_frequency - f) < 0.15 * f) { - return f; - } - } - RTC_LOG(LS_WARNING) << "Failed to estimate RTP clock frequency: Estimate " - << estimated_frequency - << " not close to any standard RTP frequency." - << " Last timestamp " << last_rtp_timestamp - << " first timestamp " << first_rtp_timestamp; - return std::nullopt; -} - -std::optional NetworkDelayDiff_AbsSendTime( - const LoggedRtpPacketIncoming& old_packet, - const LoggedRtpPacketIncoming& new_packet) { - if (old_packet.rtp.header.extension.hasAbsoluteSendTime && - new_packet.rtp.header.extension.hasAbsoluteSendTime) { - int64_t send_time_diff = WrappingDifference( - new_packet.rtp.header.extension.absoluteSendTime, - old_packet.rtp.header.extension.absoluteSendTime, 1ul << 24); - int64_t recv_time_diff = - new_packet.log_time_us() - old_packet.log_time_us(); - double delay_change_us = - recv_time_diff - AbsSendTimeToMicroseconds(send_time_diff); - return delay_change_us / 1000; - } else { - return std::nullopt; - } -} - -std::optional NetworkDelayDiff_CaptureTime( - const LoggedRtpPacketIncoming& old_packet, - const LoggedRtpPacketIncoming& new_packet, - const double sample_rate) { - int64_t send_time_diff = - WrappingDifference(new_packet.rtp.header.timestamp, - old_packet.rtp.header.timestamp, 1ull << 32); - int64_t recv_time_diff = new_packet.log_time_us() - old_packet.log_time_us(); - - double delay_change = - static_cast(recv_time_diff) / 1000 - - static_cast(send_time_diff) / sample_rate * 1000; - if (delay_change < -10000 || 10000 < delay_change) { - RTC_LOG(LS_WARNING) << "Very large delay change. Timestamps correct?"; - RTC_LOG(LS_WARNING) << "Old capture time " - << old_packet.rtp.header.timestamp << ", received time " - << old_packet.log_time_us(); - RTC_LOG(LS_WARNING) << "New capture time " - << new_packet.rtp.header.timestamp << ", received time " - << new_packet.log_time_us(); - RTC_LOG(LS_WARNING) << "Receive time difference " << recv_time_diff << " = " - << static_cast(recv_time_diff) / - kNumMicrosecsPerSec - << "s"; - RTC_LOG(LS_WARNING) << "Send time difference " << send_time_diff << " = " - << static_cast(send_time_diff) / sample_rate - << "s"; - } - return delay_change; -} - -template -TimeSeries CreateRtcpTypeTimeSeries(const std::vector& rtcp_list, - AnalyzerConfig config, - std::string rtcp_name, - int category_id) { - TimeSeries time_series(rtcp_name, LineStyle::kNone, PointStyle::kHighlight); - for (const auto& rtcp : rtcp_list) { - float x = config.GetCallTimeSec(rtcp.timestamp); - float y = category_id; - time_series.points.emplace_back(x, y); - } - return time_series; -} - -const char kUnknownEnumValue[] = "unknown"; - -// TODO(tommi): This should be "host". -const char kIceCandidateTypeLocal[] = "local"; -// TODO(tommi): This should be "srflx". -const char kIceCandidateTypeStun[] = "stun"; -const char kIceCandidateTypePrflx[] = "prflx"; -const char kIceCandidateTypeRelay[] = "relay"; - -const char kProtocolUdp[] = "udp"; -const char kProtocolTcp[] = "tcp"; -const char kProtocolSsltcp[] = "ssltcp"; -const char kProtocolTls[] = "tls"; - -const char kAddressFamilyIpv4[] = "ipv4"; -const char kAddressFamilyIpv6[] = "ipv6"; - -const char kNetworkTypeEthernet[] = "ethernet"; -const char kNetworkTypeLoopback[] = "loopback"; -const char kNetworkTypeWifi[] = "wifi"; -const char kNetworkTypeVpn[] = "vpn"; -const char kNetworkTypeCellular[] = "cellular"; - -absl::string_view GetIceCandidateTypeAsString(IceCandidateType type) { - switch (type) { - case IceCandidateType::kHost: - return kIceCandidateTypeLocal; - case IceCandidateType::kSrflx: - return kIceCandidateTypeStun; - case IceCandidateType::kPrflx: - return kIceCandidateTypePrflx; - case IceCandidateType::kRelay: - return kIceCandidateTypeRelay; - default: - RTC_DCHECK_NOTREACHED(); - return kUnknownEnumValue; - } -} - -std::string GetProtocolAsString(IceCandidatePairProtocol protocol) { - switch (protocol) { - case IceCandidatePairProtocol::kUdp: - return kProtocolUdp; - case IceCandidatePairProtocol::kTcp: - return kProtocolTcp; - case IceCandidatePairProtocol::kSsltcp: - return kProtocolSsltcp; - case IceCandidatePairProtocol::kTls: - return kProtocolTls; - default: - return kUnknownEnumValue; - } -} - -std::string GetAddressFamilyAsString(IceCandidatePairAddressFamily family) { - switch (family) { - case IceCandidatePairAddressFamily::kIpv4: - return kAddressFamilyIpv4; - case IceCandidatePairAddressFamily::kIpv6: - return kAddressFamilyIpv6; - default: - return kUnknownEnumValue; - } -} - -std::string GetNetworkTypeAsString(IceCandidateNetworkType type) { - switch (type) { - case IceCandidateNetworkType::kEthernet: - return kNetworkTypeEthernet; - case IceCandidateNetworkType::kLoopback: - return kNetworkTypeLoopback; - case IceCandidateNetworkType::kWifi: - return kNetworkTypeWifi; - case IceCandidateNetworkType::kVpn: - return kNetworkTypeVpn; - case IceCandidateNetworkType::kCellular: - return kNetworkTypeCellular; - default: - return kUnknownEnumValue; - } -} - -std::string GetCandidatePairLogDescriptionAsString( - const LoggedIceCandidatePairConfig& config) { - // Example: stun:wifi->relay(tcp):cellular@udp:ipv4 - // represents a pair of a local server-reflexive candidate on a WiFi network - // and a remote relay candidate using TCP as the relay protocol on a cell - // network, when the candidate pair communicates over UDP using IPv4. - StringBuilder ss; - ss << GetIceCandidateTypeAsString(config.local_candidate_type); - - if (config.local_candidate_type == IceCandidateType::kRelay) { - ss << "(" << GetProtocolAsString(config.local_relay_protocol) << ")"; - } - - ss << ":" << GetNetworkTypeAsString(config.local_network_type) << ":" - << GetAddressFamilyAsString(config.local_address_family) << "->" - << GetIceCandidateTypeAsString(config.remote_candidate_type) << ":" - << GetAddressFamilyAsString(config.remote_address_family) << "@" - << GetProtocolAsString(config.candidate_pair_protocol); - return ss.Release(); -} - -std::string GetDirectionAsString(PacketDirection direction) { - if (direction == kIncomingPacket) { - return "Incoming"; - } else { - return "Outgoing"; - } -} - -std::string GetDirectionAsShortString(PacketDirection direction) { - if (direction == kIncomingPacket) { - return "In"; - } else { - return "Out"; - } -} - -struct FakeExtensionSmall { - static constexpr RTPExtensionType kId = kRtpExtensionMid; - static constexpr absl::string_view Uri() { return "fake-extension-small"; } -}; -struct FakeExtensionLarge { - static constexpr RTPExtensionType kId = kRtpExtensionRtpStreamId; - static constexpr absl::string_view Uri() { return "fake-extension-large"; } -}; - -RtpPacketReceived RtpPacketForBWEFromHeader(const RTPHeader& header) { - RtpHeaderExtensionMap rtp_header_extensions(/*extmap_allow_mixed=*/true); - // ReceiveSideCongestionController doesn't need to know extensions ids as - // long as it able to get extensions by type. So any ids would work here. - rtp_header_extensions.Register(1); - rtp_header_extensions.Register(2); - rtp_header_extensions.Register(3); - rtp_header_extensions.Register(4); - // Use id > 14 to force two byte header per rtp header when this one is used. - rtp_header_extensions.Register(16); - - RtpPacketReceived rtp_packet(&rtp_header_extensions); - // Set only fields that might be relevant for the bandwidth estimatior. - rtp_packet.SetSsrc(header.ssrc); - rtp_packet.SetTimestamp(header.timestamp); - size_t num_bwe_extensions = 0; - if (header.extension.hasTransmissionTimeOffset) { - rtp_packet.SetExtension( - header.extension.transmissionTimeOffset); - ++num_bwe_extensions; - } - if (header.extension.hasAbsoluteSendTime) { - rtp_packet.SetExtension( - header.extension.absoluteSendTime); - ++num_bwe_extensions; - } - if (header.extension.hasTransportSequenceNumber) { - rtp_packet.SetExtension( - header.extension.transportSequenceNumber); - ++num_bwe_extensions; - } - - // All parts of the RTP header are 32bit aligned. - RTC_CHECK_EQ(header.headerLength % 4, 0); - - // Original packet could have more extensions, there could be csrcs that are - // not propagated by the rtc event log, i.e. logged header size might be - // larger that rtp_packet.header_size(). Increase it by setting an extra fake - // extension. - RTC_CHECK_GE(header.headerLength, rtp_packet.headers_size()); - size_t bytes_to_add = header.headerLength - rtp_packet.headers_size(); - if (bytes_to_add > 0) { - if (bytes_to_add <= 16) { - // one-byte header rtp header extension allows to add up to 16 bytes. - rtp_packet.AllocateExtension(FakeExtensionSmall::kId, bytes_to_add - 1); - } else { - // two-byte header rtp header extension would also add one byte per - // already set extension. - rtp_packet.AllocateExtension(FakeExtensionLarge::kId, - bytes_to_add - 2 - num_bwe_extensions); - } - } - RTC_CHECK_EQ(rtp_packet.headers_size(), header.headerLength); - - return rtp_packet; -} - -struct PacketLossSummary { - size_t num_packets = 0; - size_t num_lost_packets = 0; - Timestamp base_time = Timestamp::MinusInfinity(); -}; - -float GetHighestSeqNumber(const rtcp::ReportBlock& block) { - return block.extended_high_seq_num(); -} - -float GetFractionLost(const rtcp::ReportBlock& block) { - return static_cast(block.fraction_lost()) / 256 * 100; -} - -float GetCumulativeLost(const rtcp::ReportBlock& block) { - return block.cumulative_lost(); -} - -float DelaySinceLastSr(const rtcp::ReportBlock& block) { - return static_cast(block.delay_since_last_sr()) / 65536; -} - -std::map BuildCandidateIdLogDescriptionMap( - const std::vector& - ice_candidate_pair_configs) { - std::map candidate_pair_desc_by_id; - for (const auto& config : ice_candidate_pair_configs) { - // TODO(qingsi): Add the handling of the "Updated" config event after the - // visualization of property change for candidate pairs is introduced. - if (candidate_pair_desc_by_id.find(config.candidate_pair_id) == - candidate_pair_desc_by_id.end()) { - const std::string candidate_pair_desc = - GetCandidatePairLogDescriptionAsString(config); - candidate_pair_desc_by_id[config.candidate_pair_id] = candidate_pair_desc; - } - } - return candidate_pair_desc_by_id; -} - -} // namespace - -EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log, +EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& parsed_log, bool normalize_time) - : env_(CreateEnvironment()), parsed_log_(log) { + : parsed_log_(parsed_log), + config_(CreateEnvironment(), parsed_log, normalize_time) { config_.window_duration_ = TimeDelta::Millis(250); config_.step_ = TimeDelta::Millis(10); - if (!log.start_log_events().empty()) { - config_.rtc_to_utc_offset_ = log.start_log_events()[0].utc_time() - - log.start_log_events()[0].log_time(); - } - config_.normalize_time_ = normalize_time; - config_.begin_time_ = parsed_log_.first_timestamp(); - config_.end_time_ = parsed_log_.last_timestamp(); if (config_.end_time_ < config_.begin_time_) { RTC_LOG(LS_WARNING) << "No useful events in the log."; config_.begin_time_ = config_.end_time_ = Timestamp::Zero(); } + neteq_simulator_ = std::make_unique(parsed_log_, config_); - RTC_LOG(LS_INFO) << "Log is " - << (parsed_log_.last_timestamp().ms() - - parsed_log_.first_timestamp().ms()) / - 1000 - << " seconds long."; + if (parsed_log_.first_timestamp().IsFinite() && + parsed_log_.last_timestamp().IsFinite()) { + RTC_LOG(LS_INFO) << "Log is " + << (parsed_log_.last_timestamp().ms() - + parsed_log_.first_timestamp().ms()) / + 1000 + << " seconds long."; + } } -EventLogAnalyzer::EventLogAnalyzer(const Environment& env, - const ParsedRtcEventLog& log, +EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& parsed_log, const AnalyzerConfig& config) - : env_(env), parsed_log_(log), config_(config) { - RTC_LOG(LS_INFO) << "Log is " - << (parsed_log_.last_timestamp().ms() - - parsed_log_.first_timestamp().ms()) / - 1000 - << " seconds long."; + : parsed_log_(parsed_log), config_(config) { + neteq_simulator_ = std::make_unique(parsed_log_, config_); + if (parsed_log_.first_timestamp().IsFinite() && + parsed_log_.last_timestamp().IsFinite()) { + RTC_LOG(LS_INFO) << "Log is " + << (parsed_log_.last_timestamp().ms() - + parsed_log_.first_timestamp().ms()) / + 1000 + << " seconds long."; + } } -class BitrateObserver : public RemoteBitrateObserver { - public: - BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {} +EventLogAnalyzer::~EventLogAnalyzer() = default; - void Update(NetworkControlUpdate update) { - if (update.target_rate) { - last_bitrate_bps_ = update.target_rate->target_rate.bps(); - bitrate_updated_ = true; - } - } - - void OnReceiveBitrateChanged(const std::vector& ssrcs, - uint32_t bitrate) override {} +void EventLogAnalyzer::SetNetEqReplacementFile( + absl::string_view replacement_file_name, + int file_sample_rate_hz) { + neteq_simulator_->SetReplacementAudioFile(replacement_file_name, + file_sample_rate_hz); +} - uint32_t last_bitrate_bps() const { return last_bitrate_bps_; } - bool GetAndResetBitrateUpdated() { - bool bitrate_updated = bitrate_updated_; - bitrate_updated_ = false; - return bitrate_updated; +void EventLogAnalyzer::CreateGraphsByName(const std::vector& names, + PlotCollection* collection) const { + for (absl::string_view name : names) { + auto plot = absl::c_find_if(plots_, [name](const PlotDeclaration& plot) { + return plot.label == name; + }); + if (plot != plots_.end()) { + plot->plot_func(collection); + } } - - private: - uint32_t last_bitrate_bps_; - bool bitrate_updated_; -}; +} void EventLogAnalyzer::InitializeMapOfNamedGraphs(bool show_detector_state, bool show_alr_state, @@ -615,6 +178,9 @@ void EventLogAnalyzer::InitializeMapOfNamedGraphs(bool show_detector_state, plots_.RegisterPlot("simulated_goog_cc", [this](Plot* plot) { this->CreateGoogCcSimulationGraph(plot); }); + plots_.RegisterPlot("simulated_scream_delay", [this](Plot* plot) { + this->CreateScreamSimulationDelayGraph(plot); + }); plots_.RegisterPlot("simulated_scream_bitrates", [this](Plot* plot) { this->CreateScreamSimulationBitrateGraph(plot); }); @@ -736,1619 +302,230 @@ void EventLogAnalyzer::InitializeMapOfNamedGraphs(bool show_detector_state, plots_.RegisterPlot("dtls_writable_state", [this](Plot* plot) { this->CreateDtlsWritableStateGraph(plot); }); + plots_.RegisterPlot( + "simulated_neteq_expand_rate", [this](PlotCollection* collection) { + CreateNetEqNetworkStatsGraph( + parsed_log_, config_, neteq_simulator_->GetStats(), + [](const NetEqNetworkStatistics& stats) { + return stats.expand_rate / 16384.f; + }, + "Expand rate", + collection->AppendNewPlot("simulated_neteq_expand_rate")); + }); + plots_.RegisterPlot( + "simulated_neteq_speech_expand_rate", [this](PlotCollection* collection) { + CreateNetEqNetworkStatsGraph( + parsed_log_, config_, neteq_simulator_->GetStats(), + [](const NetEqNetworkStatistics& stats) { + return stats.speech_expand_rate / 16384.f; + }, + "Speech expand rate", + collection->AppendNewPlot("simulated_neteq_speech_expand_rate")); + }); + plots_.RegisterPlot( + "simulated_neteq_accelerate_rate", [this](PlotCollection* collection) { + CreateNetEqNetworkStatsGraph( + parsed_log_, config_, neteq_simulator_->GetStats(), + [](const NetEqNetworkStatistics& stats) { + return stats.accelerate_rate / 16384.f; + }, + "Accelerate rate", + collection->AppendNewPlot("simulated_neteq_accelerate_rate")); + }); + plots_.RegisterPlot( + "simulated_neteq_preemptive_rate", [this](PlotCollection* collection) { + CreateNetEqNetworkStatsGraph( + parsed_log_, config_, neteq_simulator_->GetStats(), + [](const NetEqNetworkStatistics& stats) { + return stats.preemptive_rate / 16384.f; + }, + "Preemptive rate", + collection->AppendNewPlot("simulated_neteq_preemptive_rate")); + }); + plots_.RegisterPlot( + "simulated_neteq_concealment_events", [this](PlotCollection* collection) { + CreateNetEqLifetimeStatsGraph( + parsed_log_, config_, neteq_simulator_->GetStats(), + [](const NetEqLifetimeStatistics& stats) { + return static_cast(stats.concealment_events); + }, + "Concealment events", + collection->AppendNewPlot("simulated_neteq_concealment_events")); + }); + plots_.RegisterPlot( + "simulated_neteq_preferred_buffer_size", + [this](PlotCollection* collection) { + CreateNetEqNetworkStatsGraph( + parsed_log_, config_, neteq_simulator_->GetStats(), + [](const NetEqNetworkStatistics& stats) { + return stats.preferred_buffer_size_ms; + }, + "Preferred buffer size (ms)", + collection->AppendNewPlot("simulated_neteq_preferred_buffer_size")); + }); + plots_.RegisterPlot( + "simulated_neteq_jitter_buffer_delay", + [this](PlotCollection* collection) { + for (const auto& st : neteq_simulator_->GetStats()) { + CreateAudioJitterBufferGraph( + parsed_log_, config_, st.first, st.second.get(), + collection->AppendNewPlot("simulated_neteq_jitter_buffer_delay")); + } + }); } - -void EventLogAnalyzer::CreateGraphsByName(const std::vector& names, - PlotCollection* collection) const { - for (absl::string_view name : names) { - auto plot = absl::c_find_if(plots_, [name](const PlotDeclaration& plot) { - return plot.label == name; - }); - if (plot != plots_.end()) { - Plot* output = collection->AppendNewPlot(plot->label); - plot->plot_func(output); - } - } -} - -void EventLogAnalyzer::CreatePacketGraph(PacketDirection direction, - Plot* plot) const { - for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { - // Filter on SSRC. - if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) { - continue; - } - - TimeSeries time_series(GetStreamName(parsed_log_, direction, stream.ssrc), - LineStyle::kBar); - auto GetPacketSize = [](const LoggedRtpPacket& packet) { - return std::optional(packet.total_length); - }; - auto ToCallTime = [this](const LoggedRtpPacket& packet) { - return this->config_.GetCallTimeSec(packet.timestamp); - }; - ProcessPoints(ToCallTime, GetPacketSize, - stream.packet_view, &time_series); - plot->AppendTimeSeries(std::move(time_series)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Packet size (bytes)", kBottomMargin, - kTopMargin); - plot->SetTitle(GetDirectionAsString(direction) + " RTP packets"); -} - -void EventLogAnalyzer::CreateRtcpTypeGraph(PacketDirection direction, - Plot* plot) const { - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( - parsed_log_.transport_feedbacks(direction), config_, "TWCC", 1)); - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( - parsed_log_.congestion_feedback(direction), config_, "CCFB", 2)); - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( - parsed_log_.receiver_reports(direction), config_, "RR", 3)); - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( - parsed_log_.sender_reports(direction), config_, "SR", 4)); - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries( - parsed_log_.extended_reports(direction), config_, "XR", 5)); - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries(parsed_log_.nacks(direction), - config_, "NACK", 6)); - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries(parsed_log_.rembs(direction), - config_, "REMB", 7)); - plot->AppendTimeSeries( - CreateRtcpTypeTimeSeries(parsed_log_.firs(direction), config_, "FIR", 8)); - plot->AppendTimeSeries( - CreateRtcpTypeTimeSeries(parsed_log_.plis(direction), config_, "PLI", 9)); - plot->AppendTimeSeries(CreateRtcpTypeTimeSeries(parsed_log_.byes(direction), - config_, "BYE", 10)); - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "RTCP type", kBottomMargin, kTopMargin); - plot->SetTitle(GetDirectionAsString(direction) + " RTCP packets"); - plot->SetYAxisTickLabels({{1, "TWCC"}, - {2, "CCFB"}, - {3, "RR"}, - {4, "SR"}, - {5, "XR"}, - {6, "NACK"}, - {7, "REMB"}, - {8, "FIR"}, - {9, "PLI"}, - {10, "BYE"}}); -} - -template -void EventLogAnalyzer::CreateAccumulatedPacketsTimeSeries( - Plot* plot, - const IterableType& packets, - const std::string& label) const { - TimeSeries time_series(label, LineStyle::kStep); - for (size_t i = 0; i < packets.size(); i++) { - float x = config_.GetCallTimeSec(packets[i].log_time()); - time_series.points.emplace_back(x, i + 1); - } - plot->AppendTimeSeries(std::move(time_series)); -} - -void EventLogAnalyzer::CreateAccumulatedPacketsGraph(PacketDirection direction, - Plot* plot) const { - for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { - if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) - continue; - std::string label = std::string("RTP ") + - GetStreamName(parsed_log_, direction, stream.ssrc); - CreateAccumulatedPacketsTimeSeries(plot, stream.packet_view, label); - } - std::string label = - std::string("RTCP ") + "(" + GetDirectionAsShortString(direction) + ")"; - if (direction == kIncomingPacket) { - CreateAccumulatedPacketsTimeSeries( - plot, parsed_log_.incoming_rtcp_packets(), label); - } else { - CreateAccumulatedPacketsTimeSeries( - plot, parsed_log_.outgoing_rtcp_packets(), label); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Received Packets", kBottomMargin, kTopMargin); - plot->SetTitle(std::string("Accumulated ") + GetDirectionAsString(direction) + - " RTP/RTCP packets"); -} - -void EventLogAnalyzer::CreatePacketRateGraph(PacketDirection direction, - Plot* plot) const { - auto CountPackets = [](auto packet) { return 1.0; }; - for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { - // Filter on SSRC. - if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) { - continue; - } - TimeSeries time_series( - std::string("RTP ") + - GetStreamName(parsed_log_, direction, stream.ssrc), - LineStyle::kLine); - MovingAverage(CountPackets, stream.packet_view, - config_, &time_series); - plot->AppendTimeSeries(std::move(time_series)); - } - TimeSeries time_series( - std::string("RTCP ") + "(" + GetDirectionAsShortString(direction) + ")", - LineStyle::kLine); - if (direction == kIncomingPacket) { - MovingAverage( - CountPackets, parsed_log_.incoming_rtcp_packets(), config_, - &time_series); - } else { - MovingAverage( - CountPackets, parsed_log_.outgoing_rtcp_packets(), config_, - &time_series); - } - plot->AppendTimeSeries(std::move(time_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Packet Rate (packets/s)", kBottomMargin, - kTopMargin); - plot->SetTitle("Rate of " + GetDirectionAsString(direction) + - " RTP/RTCP packets"); -} - -void EventLogAnalyzer::CreateTotalPacketRateGraph(PacketDirection direction, - Plot* plot) const { - // Contains a log timestamp to enable counting logged events of different - // types using MovingAverage(). - class LogTime { - public: - explicit LogTime(Timestamp log_time) : log_time_(log_time) {} - Timestamp log_time() const { return log_time_; } - - private: - Timestamp log_time_; - }; - std::vector packet_times; - auto handle_rtp = [&packet_times](const LoggedRtpPacket& packet) { - packet_times.emplace_back(packet.log_time()); - }; - RtcEventProcessor process; - for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { - process.AddEvents(stream.packet_view, handle_rtp, direction); - } - if (direction == kIncomingPacket) { - auto handle_incoming_rtcp = - [&packet_times](const LoggedRtcpPacketIncoming& packet) { - packet_times.emplace_back(packet.log_time()); - }; - process.AddEvents(parsed_log_.incoming_rtcp_packets(), - handle_incoming_rtcp); - } else { - auto handle_outgoing_rtcp = - [&packet_times](const LoggedRtcpPacketOutgoing& packet) { - packet_times.emplace_back(packet.log_time()); - }; - process.AddEvents(parsed_log_.outgoing_rtcp_packets(), - handle_outgoing_rtcp); - } - process.ProcessEventsInOrder(); - TimeSeries time_series(std::string("Total ") + "(" + - GetDirectionAsShortString(direction) + ") packets", - LineStyle::kLine); - MovingAverage([](auto packet) { return 1; }, packet_times, - config_, &time_series); - plot->AppendTimeSeries(std::move(time_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Packet Rate (packets/s)", kBottomMargin, - kTopMargin); - plot->SetTitle("Rate of all " + GetDirectionAsString(direction) + - " RTP/RTCP packets"); -} - -// For each SSRC, plot the time between the consecutive playouts. void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) const { - for (const auto& playout_stream : parsed_log_.audio_playout_events()) { - uint32_t ssrc = playout_stream.first; - if (!MatchingSsrc(ssrc, desired_ssrc_)) - continue; - std::optional last_playout_ms; - TimeSeries time_series(SsrcToString(ssrc), LineStyle::kBar); - for (const auto& playout_event : playout_stream.second) { - float x = config_.GetCallTimeSec(playout_event.log_time()); - int64_t playout_time_ms = playout_event.log_time_ms(); - // If there were no previous playouts, place the point on the x-axis. - float y = playout_time_ms - last_playout_ms.value_or(playout_time_ms); - time_series.points.push_back(TimeSeriesPoint(x, y)); - last_playout_ms.emplace(playout_time_ms); - } - plot->AppendTimeSeries(std::move(time_series)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Time since last playout (ms)", kBottomMargin, - kTopMargin); - plot->SetTitle("Audio playout"); + webrtc::CreatePlayoutGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateNetEqSetMinimumDelay(Plot* plot) const { - for (const auto& playout_stream : - parsed_log_.neteq_set_minimum_delay_events()) { - uint32_t ssrc = playout_stream.first; - if (!MatchingSsrc(ssrc, desired_ssrc_)) - continue; - - TimeSeries time_series(SsrcToString(ssrc), LineStyle::kStep, - PointStyle::kHighlight); - for (const auto& event : playout_stream.second) { - float x = config_.GetCallTimeSec(event.log_time()); - float y = event.minimum_delay_ms; - time_series.points.push_back(TimeSeriesPoint(x, y)); - } - plot->AppendTimeSeries(std::move(time_series)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1000, "Minimum Delay (ms)", kBottomMargin, - kTopMargin); - plot->SetTitle("Set Minimum Delay"); + webrtc::CreateNetEqSetMinimumDelay(parsed_log_, config_, plot); } -// For audio SSRCs, plot the audio level. void EventLogAnalyzer::CreateAudioLevelGraph(PacketDirection direction, Plot* plot) const { - for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { - if (!IsAudioSsrc(parsed_log_, direction, stream.ssrc)) - continue; - TimeSeries time_series(GetStreamName(parsed_log_, direction, stream.ssrc), - LineStyle::kLine); - for (auto& packet : stream.packet_view) { - if (packet.header.extension.audio_level()) { - float x = config_.GetCallTimeSec(packet.log_time()); - // The audio level is stored in -dBov (so e.g. -10 dBov is stored as 10) - // Here we convert it to dBov. - float y = - static_cast(-packet.header.extension.audio_level()->level()); - time_series.points.emplace_back(TimeSeriesPoint(x, y)); - } - } - plot->AppendTimeSeries(std::move(time_series)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetYAxis(-127, 0, "Audio level (dBov)", kBottomMargin, kTopMargin); - plot->SetTitle(GetDirectionAsString(direction) + " audio level"); -} - -// For each SSRC, plot the sequence number difference between consecutive -// incoming packets. -void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) const { - for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { - // Filter on SSRC. - if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) { - continue; - } - - TimeSeries time_series( - GetStreamName(parsed_log_, kIncomingPacket, stream.ssrc), - LineStyle::kBar); - auto GetSequenceNumberDiff = [](const LoggedRtpPacketIncoming& old_packet, - const LoggedRtpPacketIncoming& new_packet) { - int64_t diff = - WrappingDifference(new_packet.rtp.header.sequenceNumber, - old_packet.rtp.header.sequenceNumber, 1ul << 16); - return diff; - }; - auto ToCallTime = [this](const LoggedRtpPacketIncoming& packet) { - return this->config_.GetCallTimeSec(packet.log_time()); - }; - ProcessPairs( - ToCallTime, GetSequenceNumberDiff, stream.incoming_packets, - &time_series); - plot->AppendTimeSeries(std::move(time_series)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Difference since last packet", kBottomMargin, - kTopMargin); - plot->SetTitle("Incoming sequence number delta"); -} - -void EventLogAnalyzer::CreateIncomingPacketLossGraph(Plot* plot) const { - for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { - const std::vector& packets = - stream.incoming_packets; - // Filter on SSRC. - if (!MatchingSsrc(stream.ssrc, desired_ssrc_) || packets.empty()) { - continue; - } - - TimeSeries time_series( - GetStreamName(parsed_log_, kIncomingPacket, stream.ssrc), - LineStyle::kLine, PointStyle::kHighlight); - // TODO(terelius): Should the window and step size be read from the class - // instead? - const TimeDelta kWindow = TimeDelta::Millis(1000); - const TimeDelta kStep = TimeDelta::Millis(1000); - SeqNumUnwrapper unwrapper_; - SeqNumUnwrapper prior_unwrapper_; - size_t window_index_begin = 0; - size_t window_index_end = 0; - uint64_t highest_seq_number = - unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1; - uint64_t highest_prior_seq_number = - prior_unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1; - - for (Timestamp t = config_.begin_time_; t < config_.end_time_ + kStep; - t += kStep) { - while (window_index_end < packets.size() && - packets[window_index_end].rtp.log_time() < t) { - uint64_t sequence_number = unwrapper_.Unwrap( - packets[window_index_end].rtp.header.sequenceNumber); - highest_seq_number = std::max(highest_seq_number, sequence_number); - ++window_index_end; - } - while (window_index_begin < packets.size() && - packets[window_index_begin].rtp.log_time() < t - kWindow) { - uint64_t sequence_number = prior_unwrapper_.Unwrap( - packets[window_index_begin].rtp.header.sequenceNumber); - highest_prior_seq_number = - std::max(highest_prior_seq_number, sequence_number); - ++window_index_begin; - } - float x = config_.GetCallTimeSec(t); - uint64_t expected_packets = highest_seq_number - highest_prior_seq_number; - if (expected_packets > 0) { - int64_t received_packets = window_index_end - window_index_begin; - int64_t lost_packets = expected_packets - received_packets; - float y = static_cast(lost_packets) / expected_packets * 100; - time_series.points.emplace_back(x, y); - } - } - plot->AppendTimeSeries(std::move(time_series)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Loss rate (in %)", kBottomMargin, kTopMargin); - plot->SetTitle("Incoming packet loss (derived from incoming packets)"); + webrtc::CreateAudioLevelGraph(parsed_log_, config_, direction, plot); } void EventLogAnalyzer::CreateIncomingDelayGraph(Plot* plot) const { - for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { - // Filter on SSRC. - if (!MatchingSsrc(stream.ssrc, desired_ssrc_) || - IsRtxSsrc(parsed_log_, kIncomingPacket, stream.ssrc)) { - continue; - } - - const std::vector& packets = - stream.incoming_packets; - if (packets.size() < 100) { - RTC_LOG(LS_WARNING) << "Can't estimate the RTP clock frequency with " - << packets.size() << " packets in the stream."; - continue; - } - int64_t segment_end_us = parsed_log_.first_log_segment().stop_time_us(); - std::optional estimated_frequency = - EstimateRtpClockFrequency(packets, segment_end_us); - if (!estimated_frequency) - continue; - const double frequency_hz = *estimated_frequency; - if (IsVideoSsrc(parsed_log_, kIncomingPacket, stream.ssrc) && - frequency_hz != 90000) { - RTC_LOG(LS_WARNING) - << "Video stream should use a 90 kHz clock but appears to use " - << frequency_hz / 1000 << ". Discarding."; - continue; - } - - auto ToCallTime = [this](const LoggedRtpPacketIncoming& packet) { - return this->config_.GetCallTimeSec(packet.log_time()); - }; - auto ToNetworkDelay = [frequency_hz]( - const LoggedRtpPacketIncoming& old_packet, - const LoggedRtpPacketIncoming& new_packet) { - return NetworkDelayDiff_CaptureTime(old_packet, new_packet, frequency_hz); - }; - - TimeSeries capture_time_data( - GetStreamName(parsed_log_, kIncomingPacket, stream.ssrc) + - " capture-time", - LineStyle::kLine); - AccumulatePairs( - ToCallTime, ToNetworkDelay, packets, &capture_time_data); - plot->AppendTimeSeries(std::move(capture_time_data)); - - TimeSeries send_time_data( - GetStreamName(parsed_log_, kIncomingPacket, stream.ssrc) + - " abs-send-time", - LineStyle::kLine); - AccumulatePairs( - ToCallTime, NetworkDelayDiff_AbsSendTime, packets, &send_time_data); - plot->AppendTimeSeriesIfNotEmpty(std::move(send_time_data)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Delay (ms)", kBottomMargin, kTopMargin); - plot->SetTitle("Incoming network delay (relative to first packet)"); + webrtc::CreateIncomingDelayGraph(parsed_log_, config_, plot); } -// Plot the fraction of packets lost (as perceived by the loss-based BWE). void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) const { - TimeSeries time_series("Fraction lost", LineStyle::kLine, - PointStyle::kHighlight); - for (auto& bwe_update : parsed_log_.bwe_loss_updates()) { - float x = config_.GetCallTimeSec(bwe_update.log_time()); - float y = static_cast(bwe_update.fraction_lost) / 255 * 100; - time_series.points.emplace_back(x, y); - } - - plot->AppendTimeSeries(std::move(time_series)); - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Loss rate (in %)", kBottomMargin, kTopMargin); - plot->SetTitle("Outgoing packet loss (as reported by BWE)"); + webrtc::CreateFractionLossGraph(parsed_log_, config_, plot); } -// Plot the total bandwidth used by all RTP streams. void EventLogAnalyzer::CreateTotalIncomingBitrateGraph(Plot* plot) const { - // TODO(terelius): This could be provided by the parser. - std::multimap packets_in_order; - for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { - for (const LoggedRtpPacketIncoming& packet : stream.incoming_packets) - packets_in_order.insert( - std::make_pair(packet.rtp.log_time(), packet.rtp.total_length)); - } - - auto window_begin = packets_in_order.begin(); - auto window_end = packets_in_order.begin(); - size_t bytes_in_window = 0; - - if (!packets_in_order.empty()) { - // Calculate a moving average of the bitrate and store in a TimeSeries. - TimeSeries bitrate_series("Bitrate", LineStyle::kLine); - for (Timestamp time = config_.begin_time_; - time < config_.end_time_ + config_.step_; time += config_.step_) { - while (window_end != packets_in_order.end() && window_end->first < time) { - bytes_in_window += window_end->second; - ++window_end; - } - while (window_begin != packets_in_order.end() && - window_begin->first < time - config_.window_duration_) { - RTC_DCHECK_LE(window_begin->second, bytes_in_window); - bytes_in_window -= window_begin->second; - ++window_begin; - } - float window_duration_in_seconds = - static_cast(config_.window_duration_.us()) / - kNumMicrosecsPerSec; - float x = config_.GetCallTimeSec(time); - float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; - bitrate_series.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(bitrate_series)); - } - - // Overlay the outgoing REMB over incoming bitrate. - TimeSeries remb_series("Remb", LineStyle::kStep); - for (const auto& rtcp : parsed_log_.rembs(kOutgoingPacket)) { - float x = config_.GetCallTimeSec(rtcp.log_time()); - float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; - remb_series.points.emplace_back(x, y); - } - plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); - plot->SetTitle("Incoming RTP bitrate"); + webrtc::CreateTotalIncomingBitrateGraph(parsed_log_, config_, plot); } -// Plot the total bandwidth used by all RTP streams. void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph( Plot* plot, bool show_detector_state, bool show_alr_state, bool show_link_capacity) const { - // TODO(terelius): This could be provided by the parser. - std::multimap packets_in_order; - for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { - for (const LoggedRtpPacketOutgoing& packet : stream.outgoing_packets) - packets_in_order.insert( - std::make_pair(packet.rtp.log_time(), packet.rtp.total_length)); - } - - auto window_begin = packets_in_order.begin(); - auto window_end = packets_in_order.begin(); - size_t bytes_in_window = 0; - - if (!packets_in_order.empty()) { - // Calculate a moving average of the bitrate and store in a TimeSeries. - TimeSeries bitrate_series("Bitrate", LineStyle::kLine); - for (Timestamp time = config_.begin_time_; - time < config_.end_time_ + config_.step_; time += config_.step_) { - while (window_end != packets_in_order.end() && window_end->first < time) { - bytes_in_window += window_end->second; - ++window_end; - } - while (window_begin != packets_in_order.end() && - window_begin->first < time - config_.window_duration_) { - RTC_DCHECK_LE(window_begin->second, bytes_in_window); - bytes_in_window -= window_begin->second; - ++window_begin; - } - float window_duration_in_seconds = - static_cast(config_.window_duration_.us()) / - kNumMicrosecsPerSec; - float x = config_.GetCallTimeSec(time); - float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; - bitrate_series.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(bitrate_series)); - } - - // Overlay the send-side bandwidth estimate over the outgoing bitrate. - TimeSeries loss_series("Loss-based estimate", LineStyle::kStep); - for (auto& loss_update : parsed_log_.bwe_loss_updates()) { - float x = config_.GetCallTimeSec(loss_update.log_time()); - float y = static_cast(loss_update.bitrate_bps) / 1000; - loss_series.points.emplace_back(x, y); - } - - TimeSeries link_capacity_lower_series("Link-capacity-lower", - LineStyle::kStep); - TimeSeries link_capacity_upper_series("Link-capacity-upper", - LineStyle::kStep); - for (auto& remote_estimate_event : parsed_log_.remote_estimate_events()) { - float x = config_.GetCallTimeSec(remote_estimate_event.log_time()); - if (remote_estimate_event.link_capacity_lower.has_value()) { - float link_capacity_lower = static_cast( - remote_estimate_event.link_capacity_lower.value().kbps()); - link_capacity_lower_series.points.emplace_back(x, link_capacity_lower); - } - if (remote_estimate_event.link_capacity_upper.has_value()) { - float link_capacity_upper = static_cast( - remote_estimate_event.link_capacity_upper.value().kbps()); - link_capacity_upper_series.points.emplace_back(x, link_capacity_upper); - } - } - - TimeSeries delay_series("Delay-based estimate", LineStyle::kStep); - IntervalSeries overusing_series("Overusing", "#ff8e82", - IntervalSeries::kHorizontal); - IntervalSeries underusing_series("Underusing", "#5092fc", - IntervalSeries::kHorizontal); - IntervalSeries normal_series("Normal", "#c4ffc4", - IntervalSeries::kHorizontal); - IntervalSeries* last_series = &normal_series; - float last_detector_switch = 0.0; - - BandwidthUsage last_detector_state = BandwidthUsage::kBwNormal; - - for (auto& delay_update : parsed_log_.bwe_delay_updates()) { - float x = config_.GetCallTimeSec(delay_update.log_time()); - float y = static_cast(delay_update.bitrate_bps) / 1000; - - if (last_detector_state != delay_update.detector_state) { - last_series->intervals.emplace_back(last_detector_switch, x); - last_detector_state = delay_update.detector_state; - last_detector_switch = x; - - switch (delay_update.detector_state) { - case BandwidthUsage::kBwNormal: - last_series = &normal_series; - break; - case BandwidthUsage::kBwUnderusing: - last_series = &underusing_series; - break; - case BandwidthUsage::kBwOverusing: - last_series = &overusing_series; - break; - case BandwidthUsage::kLast: - RTC_DCHECK_NOTREACHED(); - } - } - - delay_series.points.emplace_back(x, y); - } - - RTC_CHECK(last_series); - last_series->intervals.emplace_back(last_detector_switch, - config_.CallEndTimeSec()); - - TimeSeries scream_series("Scream target rate", LineStyle::kStep); - for (auto& scream_update : parsed_log_.bwe_scream_updates()) { - float x = config_.GetCallTimeSec(scream_update.log_time()); - float y = static_cast(scream_update.target_rate.kbps()); - scream_series.points.emplace_back(x, y); - } - - TimeSeries created_series("Probe cluster created.", LineStyle::kNone, - PointStyle::kHighlight); - for (auto& cluster : parsed_log_.bwe_probe_cluster_created_events()) { - float x = config_.GetCallTimeSec(cluster.log_time()); - float y = static_cast(cluster.bitrate_bps) / 1000; - created_series.points.emplace_back(x, y); - } - - TimeSeries result_series("Probing results.", LineStyle::kNone, - PointStyle::kHighlight); - for (auto& result : parsed_log_.bwe_probe_success_events()) { - float x = config_.GetCallTimeSec(result.log_time()); - float y = static_cast(result.bitrate_bps) / 1000; - result_series.points.emplace_back(x, y); - } - - TimeSeries probe_failures_series("Probe failed", LineStyle::kNone, - PointStyle::kHighlight); - for (auto& failure : parsed_log_.bwe_probe_failure_events()) { - float x = config_.GetCallTimeSec(failure.log_time()); - probe_failures_series.points.emplace_back(x, 0); - } - - IntervalSeries alr_state("ALR", "#555555", IntervalSeries::kHorizontal); - bool previously_in_alr = false; - Timestamp alr_start = Timestamp::Zero(); - for (auto& alr : parsed_log_.alr_state_events()) { - float y = config_.GetCallTimeSec(alr.log_time()); - if (!previously_in_alr && alr.in_alr) { - alr_start = alr.log_time(); - previously_in_alr = true; - } else if (previously_in_alr && !alr.in_alr) { - float x = config_.GetCallTimeSec(alr_start); - alr_state.intervals.emplace_back(x, y); - previously_in_alr = false; - } - } - - if (previously_in_alr) { - float x = config_.GetCallTimeSec(alr_start); - float y = config_.GetCallTimeSec(config_.end_time_); - alr_state.intervals.emplace_back(x, y); - } - - if (show_detector_state) { - plot->AppendIntervalSeries(std::move(overusing_series)); - plot->AppendIntervalSeries(std::move(underusing_series)); - plot->AppendIntervalSeries(std::move(normal_series)); - } - - if (show_alr_state) { - plot->AppendIntervalSeries(std::move(alr_state)); - } - - if (show_link_capacity) { - plot->AppendTimeSeriesIfNotEmpty(std::move(link_capacity_lower_series)); - plot->AppendTimeSeriesIfNotEmpty(std::move(link_capacity_upper_series)); - } - - plot->AppendTimeSeries(std::move(loss_series)); - plot->AppendTimeSeriesIfNotEmpty(std::move(probe_failures_series)); - plot->AppendTimeSeries(std::move(delay_series)); - plot->AppendTimeSeriesIfNotEmpty(std::move(scream_series)); - plot->AppendTimeSeries(std::move(created_series)); - plot->AppendTimeSeries(std::move(result_series)); - - // Overlay the incoming REMB over the outgoing bitrate. - TimeSeries remb_series("Remb", LineStyle::kStep); - for (const auto& rtcp : parsed_log_.rembs(kIncomingPacket)) { - float x = config_.GetCallTimeSec(rtcp.log_time()); - float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; - remb_series.points.emplace_back(x, y); - } - plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); - plot->SetTitle("Outgoing RTP bitrate"); -} - -// For each SSRC, plot the bandwidth used by that stream. -void EventLogAnalyzer::CreateStreamBitrateGraph(PacketDirection direction, - Plot* plot) const { - for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { - // Filter on SSRC. - if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) { - continue; - } - - TimeSeries time_series(GetStreamName(parsed_log_, direction, stream.ssrc), - LineStyle::kLine); - auto GetPacketSizeKilobits = [](const LoggedRtpPacket& packet) { - return packet.total_length * 8.0 / 1000.0; - }; - MovingAverage( - GetPacketSizeKilobits, stream.packet_view, config_, &time_series); - plot->AppendTimeSeries(std::move(time_series)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); - plot->SetTitle(GetDirectionAsString(direction) + " bitrate per stream"); -} - -// Plot the bitrate allocation for each temporal and spatial layer. -// Computed from RTCP XR target bitrate block, so the graph is only populated if -// those are sent. -void EventLogAnalyzer::CreateBitrateAllocationGraph(PacketDirection direction, - Plot* plot) const { - std::map time_series; - const auto& xr_list = parsed_log_.extended_reports(direction); - for (const auto& rtcp : xr_list) { - const std::optional& target_bitrate = - rtcp.xr.target_bitrate(); - if (!target_bitrate.has_value()) - continue; - for (const auto& bitrate_item : target_bitrate->GetTargetBitrates()) { - LayerDescription layer(rtcp.xr.sender_ssrc(), bitrate_item.spatial_layer, - bitrate_item.temporal_layer); - auto time_series_it = time_series.find(layer); - if (time_series_it == time_series.end()) { - std::string layer_name = GetLayerName(layer); - bool inserted; - std::tie(time_series_it, inserted) = time_series.insert( - std::make_pair(layer, TimeSeries(layer_name, LineStyle::kStep))); - RTC_DCHECK(inserted); - } - float x = config_.GetCallTimeSec(rtcp.log_time()); - float y = bitrate_item.target_bitrate_kbps; - time_series_it->second.points.emplace_back(x, y); - } - } - for (auto& layer : time_series) { - plot->AppendTimeSeries(std::move(layer.second)); - } - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); - if (direction == kIncomingPacket) - plot->SetTitle("Target bitrate per incoming layer"); - else - plot->SetTitle("Target bitrate per outgoing layer"); + webrtc::CreateTotalOutgoingBitrateGraph(parsed_log_, config_, plot, + show_detector_state, show_alr_state, + show_link_capacity); } void EventLogAnalyzer::CreateGoogCcSimulationGraph(Plot* plot) const { - TimeSeries target_rates("Simulated target rate", LineStyle::kStep, - PointStyle::kHighlight); - TimeSeries delay_based("Logged delay-based estimate", LineStyle::kStep, - PointStyle::kHighlight); - TimeSeries loss_based("Logged loss-based estimate", LineStyle::kStep, - PointStyle::kHighlight); - TimeSeries probe_results("Logged probe success", LineStyle::kNone, - PointStyle::kHighlight); - - LogBasedNetworkControllerSimulation simulation( - env_, std::make_unique(), - [&](const NetworkControlUpdate& update, Timestamp at_time) { - if (update.target_rate) { - target_rates.points.emplace_back( - config_.GetCallTimeSec(at_time), - update.target_rate->target_rate.kbps()); - } - }); - - simulation.ProcessEventsInLog(parsed_log_); - for (const auto& logged : parsed_log_.bwe_delay_updates()) - delay_based.points.emplace_back(config_.GetCallTimeSec(logged.log_time()), - logged.bitrate_bps / 1000); - for (const auto& logged : parsed_log_.bwe_probe_success_events()) - probe_results.points.emplace_back(config_.GetCallTimeSec(logged.log_time()), - logged.bitrate_bps / 1000); - for (const auto& logged : parsed_log_.bwe_loss_updates()) - loss_based.points.emplace_back(config_.GetCallTimeSec(logged.log_time()), - logged.bitrate_bps / 1000); - - plot->AppendTimeSeries(std::move(delay_based)); - plot->AppendTimeSeries(std::move(loss_based)); - plot->AppendTimeSeries(std::move(probe_results)); - plot->AppendTimeSeries(std::move(target_rates)); + webrtc::CreateGoogCcSimulationGraph(parsed_log_, config_, plot); +} - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin); - plot->SetTitle("Simulated BWE behavior"); +void EventLogAnalyzer::CreateScreamSimulationDelayGraph(Plot* plot) const { + webrtc::CreateScreamSimulationDelayGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateScreamSimulationBitrateGraph(Plot* plot) const { - TimeSeries target_rate_series("Target rate", LineStyle::kStep); - TimeSeries pacing_rate_series("Pacing rate", LineStyle::kStep); - TimeSeries send_rate_series("Send rate", LineStyle::kStep); - - LogScreamSimulation simulation({.rate_window = config_.window_duration_}, - env_); - simulation.ProcessEventsInLog(parsed_log_); - - for (const LogScreamSimulation::State& state : simulation.updates()) { - target_rate_series.points.emplace_back(config_.GetCallTimeSec(state.time), - state.target_rate.bps() / 1000); - pacing_rate_series.points.emplace_back(config_.GetCallTimeSec(state.time), - state.pacing_rate.bps() / 1000); - send_rate_series.points.emplace_back(config_.GetCallTimeSec(state.time), - state.send_rate.bps() / 1000); - } - plot->AppendTimeSeries(std::move(target_rate_series)); - plot->AppendTimeSeries(std::move(pacing_rate_series)); - plot->AppendTimeSeries(std::move(send_rate_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 100, "Kbps", kBottomMargin, kTopMargin); - plot->SetTitle("Simulated Scream rates"); + webrtc::CreateScreamSimulationBitrateGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateScreamSimulationRefWindowGraph(Plot* plot) const { - TimeSeries ref_window_series("RefWindow", LineStyle::kStep); - TimeSeries ref_window_i_series("RefWindowI", LineStyle::kStep); - TimeSeries max_data_in_flight("Max allowed data in flight", LineStyle::kStep); - TimeSeries data_in_flight("Data in flight", LineStyle::kStep); - - LogScreamSimulation simulation({.rate_window = config_.window_duration_}, - env_); - simulation.ProcessEventsInLog(parsed_log_); - - for (const LogScreamSimulation::State& state : simulation.updates()) { - ref_window_series.points.emplace_back(config_.GetCallTimeSec(state.time), - state.ref_window.bytes()); - ref_window_i_series.points.emplace_back(config_.GetCallTimeSec(state.time), - state.ref_window_i.bytes()); - max_data_in_flight.points.emplace_back(config_.GetCallTimeSec(state.time), - state.max_data_in_flight.bytes()); - data_in_flight.points.emplace_back(config_.GetCallTimeSec(state.time), - state.data_in_flight.bytes()); - } - plot->AppendTimeSeries(std::move(ref_window_series)); - plot->AppendTimeSeries(std::move(ref_window_i_series)); - plot->AppendTimeSeries(std::move(max_data_in_flight)); - plot->AppendTimeSeries(std::move(data_in_flight)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Bytes", kBottomMargin, kTopMargin); - plot->SetTitle("Simulated Scream RefWindow"); + webrtc::CreateScreamSimulationRefWindowGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateScreamSimulationRatiosGraph(Plot* plot) const { - TimeSeries queue_delay_dev_norm_series("QueueDelayDevNorm", LineStyle::kStep); - TimeSeries l4s_alpha_series("L4sAlpha", LineStyle::kStep); - TimeSeries l4s_alpha_v_series("L4sAlphaV", LineStyle::kStep); - TimeSeries ref_window_delay_increase_scale("RefWindowDelayIncreaseScale", - LineStyle::kStep); - - LogScreamSimulation simulation({.rate_window = config_.window_duration_}, - env_); - simulation.ProcessEventsInLog(parsed_log_); - - for (const LogScreamSimulation::State& state : simulation.updates()) { - queue_delay_dev_norm_series.points.emplace_back( - config_.GetCallTimeSec(state.time), state.queue_delay_dev_norm); - l4s_alpha_series.points.emplace_back(config_.GetCallTimeSec(state.time), - state.l4s_alpha); - l4s_alpha_v_series.points.emplace_back(config_.GetCallTimeSec(state.time), - state.l4s_alpha_v); - ref_window_delay_increase_scale.points.emplace_back( - config_.GetCallTimeSec(state.time), - state.ref_window_delay_increase_scale); - } - plot->AppendTimeSeries(std::move(queue_delay_dev_norm_series)); - plot->AppendTimeSeries(std::move(l4s_alpha_series)); - plot->AppendTimeSeries(std::move(l4s_alpha_v_series)); - plot->AppendTimeSeries(std::move(ref_window_delay_increase_scale)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Ratios", kBottomMargin, kTopMargin); - plot->SetTitle("Simulated Scream Ratios"); -} - -void EventLogAnalyzer::CreateOutgoingEcnFeedbackGraph(Plot* plot) const { - CreateEcnFeedbackGraph(plot, kOutgoingPacket); - plot->SetTitle("Outgoing ECN count per feedback"); -} - -void EventLogAnalyzer::CreateIncomingEcnFeedbackGraph(Plot* plot) const { - CreateEcnFeedbackGraph(plot, kIncomingPacket); - plot->SetTitle("Incoming ECN count per feedback"); + webrtc::CreateScreamSimulationRatiosGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateScreamRefWindowGraph(Plot* plot) const { - TimeSeries ref_window_series("RefWindow", LineStyle::kStep); - for (auto& scream_update : parsed_log_.bwe_scream_updates()) { - float x = config_.GetCallTimeSec(scream_update.log_time()); - float y = static_cast(scream_update.ref_window.bytes()); - ref_window_series.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(ref_window_series)); - - TimeSeries data_in_flight_series("Data in flight", LineStyle::kLine); - for (auto& scream_update : parsed_log_.bwe_scream_updates()) { - float x = config_.GetCallTimeSec(scream_update.log_time()); - float y = static_cast(scream_update.data_in_flight.bytes()); - data_in_flight_series.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(data_in_flight_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 3000, "Bytes", kBottomMargin, kTopMargin); - plot->SetTitle("Scream Ref Window"); + webrtc::CreateScreamRefWindowGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateScreamDelayEstimateGraph(Plot* plot) const { - TimeSeries smoothed_rtt_series("Smoothed RTT", LineStyle::kStep); - TimeSeries avg_queue_delay_series("Avg queue delay", LineStyle::kStep); - - for (auto& scream_update : parsed_log_.bwe_scream_updates()) { - float x = config_.GetCallTimeSec(scream_update.log_time()); - float smoothed_rtt_ms = static_cast(scream_update.smoothed_rtt.ms()); - smoothed_rtt_series.points.emplace_back(x, smoothed_rtt_ms); - float avg_queue_delay_ms = - static_cast(scream_update.avg_queue_delay.ms()); - avg_queue_delay_series.points.emplace_back(x, avg_queue_delay_ms); - } - - plot->AppendTimeSeries(std::move(smoothed_rtt_series)); - plot->AppendTimeSeries(std::move(avg_queue_delay_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 50, "Delay (ms)", kBottomMargin, kTopMargin); - plot->SetTitle("Scream delay estimates"); -} - -void EventLogAnalyzer::CreateEcnFeedbackGraph(Plot* plot, - PacketDirection direction) const { - TimeSeries not_ect("Not ECN capable", LineStyle::kBar, - PointStyle::kHighlight); - TimeSeries ect_1("ECN capable", LineStyle::kBar, PointStyle::kHighlight); - TimeSeries ce("Congestion experienced", LineStyle::kBar, - PointStyle::kHighlight); - - for (const LoggedRtcpCongestionControlFeedback& feedback : - parsed_log_.congestion_feedback(direction)) { - int ect_1_count = 0; - int not_ect_count = 0; - int ce_count = 0; - - for (const rtcp::CongestionControlFeedback::PacketInfo& info : - feedback.congestion_feedback.packets()) { - switch (info.ecn) { - case EcnMarking::kNotEct: - ++not_ect_count; - break; - case EcnMarking::kEct1: - ++ect_1_count; - break; - case EcnMarking::kEct0: - RTC_LOG(LS_ERROR) << "unexpected ect(0)"; - break; - case EcnMarking::kCe: - ++ce_count; - break; - } - } - ect_1.points.emplace_back(config_.GetCallTimeSec(feedback.timestamp), - ect_1_count); - not_ect.points.emplace_back(config_.GetCallTimeSec(feedback.timestamp), - not_ect_count); - ce.points.emplace_back(config_.GetCallTimeSec(feedback.timestamp), - ce_count); - } - - plot->AppendTimeSeriesIfNotEmpty(std::move(ect_1)); - plot->AppendTimeSeriesIfNotEmpty(std::move(not_ect)); - plot->AppendTimeSeriesIfNotEmpty(std::move(ce)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Count per feedback", kBottomMargin, - kTopMargin); -} - -void EventLogAnalyzer::CreateOutgoingLossRateGraph(Plot* plot) const { - struct PacketLossPerFeedback { - Timestamp timestamp; // Time when this feedback was received. - int num_packets_in_feedback = 0; // Includes lost packets. - int num_lost_packets = 0; // In this specific feedback. - int num_reordered_packets = 0; // Packets received in this feedback, but - // was previously reported as lost. - int num_missing_feedback = - 0; // Packets missing feedback between this report and the previous. - }; - - class LossFeedbackBuilder { - public: - void AddPacket(uint16_t sequence_number, TimeDelta arrival_time_delta) { - last_unwrapped_sequence_number_ = - sequence_number_unwrapper_.Unwrap(sequence_number); - if (!first_sequence_number_.has_value()) { - first_sequence_number_ = last_unwrapped_sequence_number_; - } - ++num_packets_; - if (arrival_time_delta.IsInfinite()) { - lost_sequence_numbers_.insert(last_unwrapped_sequence_number_); - } else { - num_reordered_packets_ += previous_lost_sequence_numbers_.count( - last_unwrapped_sequence_number_); - } - } - - void Update(PacketLossPerFeedback& feedback) { - feedback.num_packets_in_feedback += num_packets_; - feedback.num_lost_packets += lost_sequence_numbers_.size(); - feedback.num_reordered_packets += num_reordered_packets_; - if (first_sequence_number_.has_value() && - previous_feedback_highest_seq_number_.has_value()) { - feedback.num_missing_feedback += - *first_sequence_number_ - *previous_feedback_highest_seq_number_ - - 1; - } - - // Prepare for next feedback. - first_sequence_number_ = std::nullopt; - previous_lost_sequence_numbers_.insert(lost_sequence_numbers_.begin(), - lost_sequence_numbers_.end()); - previous_feedback_highest_seq_number_ = last_unwrapped_sequence_number_; - lost_sequence_numbers_.clear(); - num_reordered_packets_ = 0; - num_packets_ = 0; - } - - private: - int64_t last_unwrapped_sequence_number_ = 0; - int num_reordered_packets_ = 0; - int num_packets_ = 0; - std::optional first_sequence_number_; - - std::unordered_set lost_sequence_numbers_; - std::unordered_set previous_lost_sequence_numbers_; - std::optional previous_feedback_highest_seq_number_; - - RtpSequenceNumberUnwrapper sequence_number_unwrapper_; - }; - - TimeSeries loss_rate_series("Loss rate (from packet feedback)", - LineStyle::kLine, PointStyle::kHighlight); - TimeSeries reordered_packets_between_feedback( - "Ratio of reordered packets from last feedback", LineStyle::kLine, - PointStyle::kHighlight); - TimeSeries average_loss_rate_series("Average loss rate last 5s", - LineStyle::kLine, PointStyle::kHighlight); - TimeSeries missing_feedback_series("Missing feedback", LineStyle::kNone, - PointStyle::kHighlight); - - std::vector loss_per_feedback; - - if (!parsed_log_.congestion_feedback(kIncomingPacket).empty()) { - plot->SetTitle("Outgoing loss rate (from CCFB)"); - - std::map per_ssrc_builder; - for (const LoggedRtcpCongestionControlFeedback& feedback : - parsed_log_.congestion_feedback(kIncomingPacket)) { - const rtcp::CongestionControlFeedback& transport_feedback = - feedback.congestion_feedback; - - PacketLossPerFeedback packet_loss_per_feedback = { - .timestamp = feedback.log_time()}; - for (const rtcp::CongestionControlFeedback::PacketInfo& packet : - transport_feedback.packets()) { - per_ssrc_builder[packet.ssrc].AddPacket(packet.sequence_number, - packet.arrival_time_offset); - } - for (auto& [ssrc, builder] : per_ssrc_builder) { - builder.Update(packet_loss_per_feedback); - } - loss_per_feedback.push_back(packet_loss_per_feedback); - } - } else if (!parsed_log_.transport_feedbacks(kIncomingPacket).empty()) { - plot->SetTitle("Outgoing loss rate (from TWCC)"); - - LossFeedbackBuilder builder; - for (const LoggedRtcpPacketTransportFeedback& feedback : - parsed_log_.transport_feedbacks(kIncomingPacket)) { - feedback.transport_feedback.ForAllPackets( - [&](uint16_t sequence_number, TimeDelta receive_time_delta) { - builder.AddPacket(sequence_number, receive_time_delta); - }); - PacketLossPerFeedback packet_loss_per_feedback = { - .timestamp = feedback.log_time()}; - builder.Update(packet_loss_per_feedback); - loss_per_feedback.push_back(packet_loss_per_feedback); - } - } - - PacketLossSummary window_summary; - Timestamp last_observation_receive_time = Timestamp::Zero(); - - // Use loss based bwe 2 observation duration and observation window size. - constexpr TimeDelta kObservationDuration = TimeDelta::Millis(250); - constexpr uint32_t kObservationWindowSize = 20; - std::deque observations; - int previous_feedback_size = 0; - for (const PacketLossPerFeedback& feedback : loss_per_feedback) { - for (int64_t num = 0; num < feedback.num_missing_feedback; ++num) { - missing_feedback_series.points.emplace_back( - config_.GetCallTimeSec(feedback.timestamp), 100 + num); - } - - // Compute loss rate from the transport feedback. - float loss_rate = static_cast(feedback.num_lost_packets * 100.0 / - feedback.num_packets_in_feedback); - - loss_rate_series.points.emplace_back( - config_.GetCallTimeSec(feedback.timestamp), loss_rate); - float reordered_rate = - previous_feedback_size == 0 - ? 0 - : static_cast(feedback.num_reordered_packets * 100.0 / - previous_feedback_size); - previous_feedback_size = feedback.num_packets_in_feedback; - reordered_packets_between_feedback.points.emplace_back( - config_.GetCallTimeSec(feedback.timestamp), reordered_rate); - - // Compute loss rate in a window of kObservationWindowSize. - if (window_summary.num_packets == 0) { - window_summary.base_time = feedback.timestamp; - } - window_summary.num_packets += feedback.num_packets_in_feedback; - window_summary.num_lost_packets += - feedback.num_lost_packets - feedback.num_reordered_packets; - - const Timestamp last_received_time = feedback.timestamp; - const TimeDelta observation_duration = - window_summary.base_time == Timestamp::Zero() - ? TimeDelta::Zero() - : last_received_time - window_summary.base_time; - if (observation_duration > kObservationDuration) { - last_observation_receive_time = last_received_time; - observations.push_back(window_summary); - if (observations.size() > kObservationWindowSize) { - observations.pop_front(); - } - - // Compute average loss rate in a number of windows. - int total_packets = 0; - int total_loss = 0; - for (const auto& observation : observations) { - total_loss += observation.num_lost_packets; - total_packets += observation.num_packets; - } - if (total_packets > 0) { - float average_loss_rate = total_loss * 100.0 / total_packets; - average_loss_rate_series.points.emplace_back( - config_.GetCallTimeSec(feedback.timestamp), average_loss_rate); - } else { - average_loss_rate_series.points.emplace_back( - config_.GetCallTimeSec(feedback.timestamp), 0); - } - window_summary = PacketLossSummary(); - } - } - // Add the data set to the plot. - plot->AppendTimeSeriesIfNotEmpty(std::move(loss_rate_series)); - plot->AppendTimeSeriesIfNotEmpty( - std::move(reordered_packets_between_feedback)); - plot->AppendTimeSeriesIfNotEmpty(std::move(average_loss_rate_series)); - plot->AppendTimeSeriesIfNotEmpty(std::move(missing_feedback_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 100, "Loss rate (percent)", kBottomMargin, - kTopMargin); + webrtc::CreateScreamDelayEstimateGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) const { - using RtpPacketType = LoggedRtpPacketOutgoing; - using TransportFeedbackType = LoggedRtcpPacketTransportFeedback; - - // TODO(terelius): This could be provided by the parser. - std::multimap outgoing_rtp; - for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { - for (const RtpPacketType& rtp_packet : stream.outgoing_packets) - outgoing_rtp.insert( - std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet)); - } - - const std::vector& incoming_rtcp = - parsed_log_.transport_feedbacks(kIncomingPacket); - - SimulatedClock clock(0); - BitrateObserver observer; - TransportFeedbackAdapter transport_feedback; - auto factory = GoogCcNetworkControllerFactory(); - TimeDelta process_interval = factory.GetProcessInterval(); - // TODO(holmer): Log the call config and use that here instead. - static const uint32_t kDefaultStartBitrateBps = 300000; - NetworkControllerConfig cc_config(env_); - cc_config.constraints.at_time = clock.CurrentTime(); - cc_config.constraints.starting_rate = - DataRate::BitsPerSec(kDefaultStartBitrateBps); - auto goog_cc = factory.Create(cc_config); - - TimeSeries time_series("Delay-based estimate", LineStyle::kStep, - PointStyle::kHighlight); - TimeSeries acked_time_series("Raw acked bitrate", LineStyle::kLine, - PointStyle::kHighlight); - TimeSeries robust_time_series("Robust throughput estimate", LineStyle::kLine, - PointStyle::kHighlight); - TimeSeries acked_estimate_time_series("Ackednowledged bitrate estimate", - LineStyle::kLine, - PointStyle::kHighlight); - - auto rtp_iterator = outgoing_rtp.begin(); - auto rtcp_iterator = incoming_rtcp.begin(); - - auto NextRtpTime = [&]() { - if (rtp_iterator != outgoing_rtp.end()) - return static_cast(rtp_iterator->first); - return std::numeric_limits::max(); - }; - - auto NextRtcpTime = [&]() { - if (rtcp_iterator != incoming_rtcp.end()) - return static_cast(rtcp_iterator->log_time_us()); - return std::numeric_limits::max(); - }; - int64_t next_process_time_us_ = std::min({NextRtpTime(), NextRtcpTime()}); - - auto NextProcessTime = [&]() { - if (rtcp_iterator != incoming_rtcp.end() || - rtp_iterator != outgoing_rtp.end()) { - return next_process_time_us_; - } - return std::numeric_limits::max(); - }; - - RateStatistics raw_acked_bitrate(750, 8000); - FieldTrials throughput_config( - "WebRTC-Bwe-RobustThroughputEstimatorSettings/enabled:true/"); - std::unique_ptr - robust_throughput_estimator( - AcknowledgedBitrateEstimatorInterface::Create(&throughput_config)); - FieldTrials acked_bitrate_config( - "WebRTC-Bwe-RobustThroughputEstimatorSettings/enabled:false/"); - std::unique_ptr - acknowledged_bitrate_estimator( - AcknowledgedBitrateEstimatorInterface::Create(&acked_bitrate_config)); - int64_t time_us = - std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()}); - int64_t last_update_us = 0; - while (time_us != std::numeric_limits::max()) { - clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds()); - if (clock.TimeInMicroseconds() >= NextRtpTime()) { - RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime()); - const RtpPacketType& rtp_packet = *rtp_iterator->second; - if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) { - RtpPacketToSend send_packet(/*extensions=*/nullptr); - send_packet.set_transport_sequence_number( - rtp_packet.rtp.header.extension.transportSequenceNumber); - send_packet.SetSsrc(rtp_packet.rtp.header.ssrc); - send_packet.SetSequenceNumber(rtp_packet.rtp.header.sequenceNumber); - send_packet.SetPayloadSize(rtp_packet.rtp.total_length - - send_packet.headers_size()); - RTC_DCHECK_EQ(send_packet.size(), rtp_packet.rtp.total_length); - if (IsRtxSsrc(parsed_log_, PacketDirection::kOutgoingPacket, - rtp_packet.rtp.header.ssrc)) { - // Don't set the optional media type as we don't know if it is - // a retransmission, FEC or padding. - } else if (IsVideoSsrc(parsed_log_, PacketDirection::kOutgoingPacket, - rtp_packet.rtp.header.ssrc)) { - send_packet.set_packet_type(RtpPacketMediaType::kVideo); - } else if (IsAudioSsrc(parsed_log_, PacketDirection::kOutgoingPacket, - rtp_packet.rtp.header.ssrc)) { - send_packet.set_packet_type(RtpPacketMediaType::kAudio); - } - transport_feedback.AddPacket( - send_packet, PacedPacketInfo(), - 0u, // Per packet overhead bytes., - Timestamp::Micros(rtp_packet.rtp.log_time_us())); - } - SentPacketInfo sent_packet; - sent_packet.send_time_ms = rtp_packet.rtp.log_time_ms(); - sent_packet.info.included_in_allocation = true; - sent_packet.info.packet_size_bytes = rtp_packet.rtp.total_length; - if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) { - sent_packet.packet_id = - rtp_packet.rtp.header.extension.transportSequenceNumber; - sent_packet.info.included_in_feedback = true; - } - auto sent_msg = transport_feedback.ProcessSentPacket(sent_packet); - if (sent_msg) - observer.Update(goog_cc->OnSentPacket(*sent_msg)); - ++rtp_iterator; - } - if (clock.TimeInMicroseconds() >= NextRtcpTime()) { - RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime()); - - auto feedback_msg = transport_feedback.ProcessTransportFeedback( - rtcp_iterator->transport_feedback, clock.CurrentTime()); - if (feedback_msg) { - observer.Update(goog_cc->OnTransportPacketsFeedback(*feedback_msg)); - std::vector feedback = - feedback_msg->SortedByReceiveTime(); - if (!feedback.empty()) { - acknowledged_bitrate_estimator->IncomingPacketFeedbackVector( - feedback); - robust_throughput_estimator->IncomingPacketFeedbackVector(feedback); - for (const PacketResult& packet : feedback) { - raw_acked_bitrate.Update(packet.sent_packet.size.bytes(), - packet.receive_time.ms()); - } - std::optional raw_bitrate_bps = - raw_acked_bitrate.Rate(feedback.back().receive_time.ms()); - float x = config_.GetCallTimeSec(clock.CurrentTime()); - if (raw_bitrate_bps) { - float y = raw_bitrate_bps.value() / 1000; - acked_time_series.points.emplace_back(x, y); - } - std::optional robust_estimate = - robust_throughput_estimator->bitrate(); - if (robust_estimate) { - float y = robust_estimate.value().kbps(); - robust_time_series.points.emplace_back(x, y); - } - std::optional acked_estimate = - acknowledged_bitrate_estimator->bitrate(); - if (acked_estimate) { - float y = acked_estimate.value().kbps(); - acked_estimate_time_series.points.emplace_back(x, y); - } - } - } - ++rtcp_iterator; - } - if (clock.TimeInMicroseconds() >= NextProcessTime()) { - RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextProcessTime()); - ProcessInterval msg; - msg.at_time = clock.CurrentTime(); - observer.Update(goog_cc->OnProcessInterval(msg)); - next_process_time_us_ += process_interval.us(); - } - if (observer.GetAndResetBitrateUpdated() || - time_us - last_update_us >= 1e6) { - uint32_t y = observer.last_bitrate_bps() / 1000; - float x = config_.GetCallTimeSec(clock.CurrentTime()); - time_series.points.emplace_back(x, y); - last_update_us = time_us; - } - time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()}); - } - // Add the data set to the plot. - plot->AppendTimeSeries(std::move(time_series)); - plot->AppendTimeSeries(std::move(robust_time_series)); - plot->AppendTimeSeries(std::move(acked_time_series)); - plot->AppendTimeSeriesIfNotEmpty(std::move(acked_estimate_time_series)); - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin); - plot->SetTitle("Simulated send-side BWE behavior"); + webrtc::CreateSendSideBweSimulationGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateReceiveSideBweSimulationGraph(Plot* plot) const { - using RtpPacketType = LoggedRtpPacketIncoming; - class RembInterceptor { - public: - void SendRemb(uint32_t bitrate_bps, std::vector ssrcs) { - last_bitrate_bps_ = bitrate_bps; - bitrate_updated_ = true; - } - uint32_t last_bitrate_bps() const { return last_bitrate_bps_; } - bool GetAndResetBitrateUpdated() { - bool bitrate_updated = bitrate_updated_; - bitrate_updated_ = false; - return bitrate_updated; - } - - private: - // We don't know the start bitrate, but assume that it is the default 300 - // kbps. - uint32_t last_bitrate_bps_ = 300000; - bool bitrate_updated_ = false; - }; + webrtc::CreateReceiveSideBweSimulationGraph(parsed_log_, config_, plot); +} - std::multimap incoming_rtp; +void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) const { + webrtc::CreateNetworkDelayFeedbackGraph(parsed_log_, config_, plot); +} - for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { - if (IsVideoSsrc(parsed_log_, kIncomingPacket, stream.ssrc)) { - for (const auto& rtp_packet : stream.incoming_packets) - incoming_rtp.insert( - std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet)); - } - } +void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) const { + webrtc::CreatePacerDelayGraph(parsed_log_, config_, plot); +} - SimulatedClock clock(0); - EnvironmentFactory env_factory(env_); - env_factory.Set(&clock); - RembInterceptor remb_interceptor; - ReceiveSideCongestionController rscc( - env_factory.Create(), [](auto...) {}, - absl::bind_front(&RembInterceptor::SendRemb, &remb_interceptor)); - // TODO(holmer): Log the call config and use that here instead. - // static const uint32_t kDefaultStartBitrateBps = 300000; - // rscc.SetBweBitrates(0, kDefaultStartBitrateBps, -1); +void EventLogAnalyzer::CreateIceCandidatePairConfigGraph(Plot* plot) const { + webrtc::CreateIceCandidatePairConfigGraph(parsed_log_, config_, plot); +} - TimeSeries time_series("Receive side estimate", LineStyle::kLine, - PointStyle::kHighlight); - TimeSeries acked_time_series("Received bitrate", LineStyle::kLine); +void EventLogAnalyzer::CreateIceConnectivityCheckGraph(Plot* plot) const { + webrtc::CreateIceConnectivityCheckGraph(parsed_log_, config_, plot); +} - RateStatistics acked_bitrate(250, 8000); - int64_t last_update_us = 0; - for (const auto& kv : incoming_rtp) { - const RtpPacketType& packet = *kv.second; +void EventLogAnalyzer::CreateDtlsTransportStateGraph(Plot* plot) const { + webrtc::CreateDtlsTransportStateGraph(parsed_log_, config_, plot); +} - RtpPacketReceived rtp_packet = RtpPacketForBWEFromHeader(packet.rtp.header); - rtp_packet.set_arrival_time(packet.rtp.log_time()); - rtp_packet.SetPayloadSize(packet.rtp.total_length - - rtp_packet.headers_size()); +void EventLogAnalyzer::CreateDtlsWritableStateGraph(Plot* plot) const { + webrtc::CreateDtlsWritableStateGraph(parsed_log_, config_, plot); +} - clock.AdvanceTime(rtp_packet.arrival_time() - clock.CurrentTime()); - rscc.OnReceivedPacket(rtp_packet, MediaType::VIDEO); - int64_t arrival_time_ms = packet.rtp.log_time().ms(); - acked_bitrate.Update(packet.rtp.total_length, arrival_time_ms); - std::optional bitrate_bps = acked_bitrate.Rate(arrival_time_ms); - if (bitrate_bps) { - uint32_t y = *bitrate_bps / 1000; - float x = config_.GetCallTimeSec(clock.CurrentTime()); - acked_time_series.points.emplace_back(x, y); - } - if (remb_interceptor.GetAndResetBitrateUpdated() || - clock.TimeInMicroseconds() - last_update_us >= 1e6) { - uint32_t y = remb_interceptor.last_bitrate_bps() / 1000; - float x = config_.GetCallTimeSec(clock.CurrentTime()); - time_series.points.emplace_back(x, y); - last_update_us = clock.TimeInMicroseconds(); - } - } - // Add the data set to the plot. - plot->AppendTimeSeries(std::move(time_series)); - plot->AppendTimeSeries(std::move(acked_time_series)); +void EventLogAnalyzer::CreatePacketGraph(PacketDirection direction, + Plot* plot) const { + webrtc::CreatePacketGraph(direction, parsed_log_, config_, plot); +} - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin); - plot->SetTitle("Simulated receive-side BWE behavior"); +void EventLogAnalyzer::CreateRtcpTypeGraph(PacketDirection direction, + Plot* plot) const { + webrtc::CreateRtcpTypeGraph(direction, parsed_log_, config_, plot); } -void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) const { - TimeSeries time_series("Network delay", LineStyle::kLine, - PointStyle::kHighlight); - int64_t min_send_receive_diff_ms = std::numeric_limits::max(); - int64_t min_rtt_ms = std::numeric_limits::max(); +void EventLogAnalyzer::CreateAccumulatedPacketsGraph(PacketDirection direction, + Plot* plot) const { + webrtc::CreateAccumulatedPacketsGraph(direction, parsed_log_, config_, plot); +} - std::vector matched_rtp_rtcp = - GetNetworkTrace(parsed_log_); - absl::c_stable_sort(matched_rtp_rtcp, [](const MatchedSendArrivalTimes& a, - const MatchedSendArrivalTimes& b) { - return a.feedback_arrival_time_ms < b.feedback_arrival_time_ms || - (a.feedback_arrival_time_ms == b.feedback_arrival_time_ms && - a.arrival_time_ms < b.arrival_time_ms); - }); - for (const auto& packet : matched_rtp_rtcp) { - if (packet.arrival_time_ms == MatchedSendArrivalTimes::kNotReceived) - continue; - float x = config_.GetCallTimeSecFromMs(packet.feedback_arrival_time_ms); - int64_t y = packet.arrival_time_ms - packet.send_time_ms; - int64_t rtt_ms = packet.feedback_arrival_time_ms - packet.send_time_ms; - min_rtt_ms = std::min(rtt_ms, min_rtt_ms); - min_send_receive_diff_ms = std::min(y, min_send_receive_diff_ms); - time_series.points.emplace_back(x, y); - } +void EventLogAnalyzer::CreatePacketRateGraph(PacketDirection direction, + Plot* plot) const { + webrtc::CreatePacketRateGraph(direction, parsed_log_, config_, plot); +} - // We assume that the base network delay (w/o queues) is equal to half - // the minimum RTT. Therefore rescale the delays by subtracting the minimum - // observed 1-ways delay and add half the minimum RTT. - const int64_t estimated_clock_offset_ms = - min_send_receive_diff_ms - min_rtt_ms / 2; - for (TimeSeriesPoint& point : time_series.points) - point.y -= estimated_clock_offset_ms; +void EventLogAnalyzer::CreateTotalPacketRateGraph(PacketDirection direction, + Plot* plot) const { + webrtc::CreateTotalPacketRateGraph(direction, parsed_log_, config_, plot); +} - // Add the data set to the plot. - plot->AppendTimeSeriesIfNotEmpty(std::move(time_series)); +void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) const { + webrtc::CreateSequenceNumberGraph(parsed_log_, config_, plot); +} - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Delay (ms)", kBottomMargin, kTopMargin); - plot->SetTitle("Outgoing network delay (based on per-packet feedback)"); +void EventLogAnalyzer::CreateIncomingPacketLossGraph(Plot* plot) const { + webrtc::CreateIncomingPacketLossGraph(parsed_log_, config_, plot); } -void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) const { - for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { - const std::vector& packets = - stream.outgoing_packets; +void EventLogAnalyzer::CreateStreamBitrateGraph(PacketDirection direction, + Plot* plot) const { + webrtc::CreateStreamBitrateGraph(direction, parsed_log_, config_, plot); +} - if (IsRtxSsrc(parsed_log_, kOutgoingPacket, stream.ssrc)) { - continue; - } +void EventLogAnalyzer::CreateBitrateAllocationGraph(PacketDirection direction, + Plot* plot) const { + webrtc::CreateBitrateAllocationGraph(direction, parsed_log_, config_, plot); +} - if (packets.size() < 2) { - RTC_LOG(LS_WARNING) - << "Can't estimate a the RTP clock frequency or the " - "pacer delay with less than 2 packets in the stream"; - continue; - } - int64_t segment_end_us = parsed_log_.first_log_segment().stop_time_us(); - std::optional estimated_frequency = - EstimateRtpClockFrequency(packets, segment_end_us); - if (!estimated_frequency) - continue; - if (IsVideoSsrc(parsed_log_, kOutgoingPacket, stream.ssrc) && - *estimated_frequency != 90000) { - RTC_LOG(LS_WARNING) - << "Video stream should use a 90 kHz clock but appears to use " - << *estimated_frequency / 1000 << ". Discarding."; - continue; - } +void EventLogAnalyzer::CreateOutgoingEcnFeedbackGraph(Plot* plot) const { + webrtc::CreateOutgoingEcnFeedbackGraph(parsed_log_, config_, plot); +} - TimeSeries pacer_delay_series( - GetStreamName(parsed_log_, kOutgoingPacket, stream.ssrc) + "(" + - std::to_string(*estimated_frequency / 1000) + " kHz)", - LineStyle::kLine, PointStyle::kHighlight); - SeqNumUnwrapper timestamp_unwrapper; - uint64_t first_capture_timestamp = - timestamp_unwrapper.Unwrap(packets.front().rtp.header.timestamp); - uint64_t first_send_timestamp = packets.front().rtp.log_time_us(); - for (const auto& packet : packets) { - double capture_time_ms = (static_cast(timestamp_unwrapper.Unwrap( - packet.rtp.header.timestamp)) - - first_capture_timestamp) / - *estimated_frequency * 1000; - double send_time_ms = - static_cast(packet.rtp.log_time_us() - first_send_timestamp) / - 1000; - float x = config_.GetCallTimeSec(packet.rtp.log_time()); - float y = send_time_ms - capture_time_ms; - pacer_delay_series.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(pacer_delay_series)); - } +void EventLogAnalyzer::CreateIncomingEcnFeedbackGraph(Plot* plot) const { + webrtc::CreateIncomingEcnFeedbackGraph(parsed_log_, config_, plot); +} - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 10, "Pacer delay (ms)", kBottomMargin, kTopMargin); - plot->SetTitle( - "Delay from capture to send time. (First packet normalized to 0.)"); +void EventLogAnalyzer::CreateOutgoingLossRateGraph(Plot* plot) const { + webrtc::CreateOutgoingLossRateGraph(parsed_log_, config_, plot); } void EventLogAnalyzer::CreateTimestampGraph(PacketDirection direction, Plot* plot) const { - for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { - TimeSeries rtp_timestamps( - GetStreamName(parsed_log_, direction, stream.ssrc) + " capture-time", - LineStyle::kLine, PointStyle::kHighlight); - for (const auto& packet : stream.packet_view) { - float x = config_.GetCallTimeSec(packet.log_time()); - float y = packet.header.timestamp; - rtp_timestamps.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(rtp_timestamps)); - - TimeSeries rtcp_timestamps( - GetStreamName(parsed_log_, direction, stream.ssrc) + - " rtcp capture-time", - LineStyle::kLine, PointStyle::kHighlight); - // TODO(terelius): Why only sender reports? - const auto& sender_reports = parsed_log_.sender_reports(direction); - for (const auto& rtcp : sender_reports) { - if (rtcp.sr.sender_ssrc() != stream.ssrc) - continue; - float x = config_.GetCallTimeSec(rtcp.log_time()); - float y = rtcp.sr.rtp_timestamp(); - rtcp_timestamps.points.emplace_back(x, y); - } - plot->AppendTimeSeriesIfNotEmpty(std::move(rtcp_timestamps)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "RTP timestamp", kBottomMargin, kTopMargin); - plot->SetTitle(GetDirectionAsString(direction) + " timestamps"); + webrtc::CreateTimestampGraph(direction, parsed_log_, config_, plot); } void EventLogAnalyzer::CreateSenderAndReceiverReportPlot( @@ -2357,174 +534,8 @@ void EventLogAnalyzer::CreateSenderAndReceiverReportPlot( std::string title, std::string yaxis_label, Plot* plot) const { - std::map sr_reports_by_ssrc; - const auto& sender_reports = parsed_log_.sender_reports(direction); - for (const auto& rtcp : sender_reports) { - float x = config_.GetCallTimeSec(rtcp.log_time()); - uint32_t ssrc = rtcp.sr.sender_ssrc(); - for (const auto& block : rtcp.sr.report_blocks()) { - float y = fy(block); - auto sr_report_it = sr_reports_by_ssrc.find(ssrc); - bool inserted; - if (sr_report_it == sr_reports_by_ssrc.end()) { - std::tie(sr_report_it, inserted) = sr_reports_by_ssrc.emplace( - ssrc, TimeSeries(GetStreamName(parsed_log_, direction, ssrc) + - " Sender Reports", - LineStyle::kLine, PointStyle::kHighlight)); - } - sr_report_it->second.points.emplace_back(x, y); - } - } - for (auto& kv : sr_reports_by_ssrc) { - plot->AppendTimeSeries(std::move(kv.second)); - } - - std::map rr_reports_by_ssrc; - const auto& receiver_reports = parsed_log_.receiver_reports(direction); - for (const auto& rtcp : receiver_reports) { - float x = config_.GetCallTimeSec(rtcp.log_time()); - uint32_t ssrc = rtcp.rr.sender_ssrc(); - for (const auto& block : rtcp.rr.report_blocks()) { - float y = fy(block); - auto rr_report_it = rr_reports_by_ssrc.find(ssrc); - bool inserted; - if (rr_report_it == rr_reports_by_ssrc.end()) { - std::tie(rr_report_it, inserted) = rr_reports_by_ssrc.emplace( - ssrc, TimeSeries(GetStreamName(parsed_log_, direction, ssrc) + - " Receiver Reports", - LineStyle::kLine, PointStyle::kHighlight)); - } - rr_report_it->second.points.emplace_back(x, y); - } - } - for (auto& kv : rr_reports_by_ssrc) { - plot->AppendTimeSeries(std::move(kv.second)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, yaxis_label, kBottomMargin, kTopMargin); - plot->SetTitle(title); -} - -void EventLogAnalyzer::CreateIceCandidatePairConfigGraph(Plot* plot) const { - std::map configs_by_cp_id; - for (const auto& config : parsed_log_.ice_candidate_pair_configs()) { - if (configs_by_cp_id.find(config.candidate_pair_id) == - configs_by_cp_id.end()) { - const std::string candidate_pair_desc = - GetCandidatePairLogDescriptionAsString(config); - configs_by_cp_id[config.candidate_pair_id] = - TimeSeries("[" + std::to_string(config.candidate_pair_id) + "]" + - candidate_pair_desc, - LineStyle::kNone, PointStyle::kHighlight); - } - float x = config_.GetCallTimeSec(config.log_time()); - float y = static_cast(config.type); - configs_by_cp_id[config.candidate_pair_id].points.emplace_back(x, y); - } - - // TODO(qingsi): There can be a large number of candidate pairs generated by - // certain calls and the frontend cannot render the chart in this case due - // to the failure of generating a palette with the same number of colors. - for (auto& kv : configs_by_cp_id) { - plot->AppendTimeSeries(std::move(kv.second)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 3, "Config Type", kBottomMargin, kTopMargin); - plot->SetTitle("[IceEventLog] ICE candidate pair configs"); - plot->SetYAxisTickLabels( - {{static_cast(IceCandidatePairConfigType::kAdded), "ADDED"}, - {static_cast(IceCandidatePairConfigType::kUpdated), "UPDATED"}, - {static_cast(IceCandidatePairConfigType::kDestroyed), - "DESTROYED"}, - {static_cast(IceCandidatePairConfigType::kSelected), - "SELECTED"}}); -} - -void EventLogAnalyzer::CreateIceConnectivityCheckGraph(Plot* plot) const { - constexpr int kEventTypeOffset = - static_cast(IceCandidatePairConfigType::kNumValues); - std::map checks_by_cp_id; - std::map candidate_pair_desc_by_id = - BuildCandidateIdLogDescriptionMap( - parsed_log_.ice_candidate_pair_configs()); - for (const auto& event : parsed_log_.ice_candidate_pair_events()) { - if (checks_by_cp_id.find(event.candidate_pair_id) == - checks_by_cp_id.end()) { - checks_by_cp_id[event.candidate_pair_id] = - TimeSeries("[" + std::to_string(event.candidate_pair_id) + "]" + - candidate_pair_desc_by_id[event.candidate_pair_id], - LineStyle::kNone, PointStyle::kHighlight); - } - float x = config_.GetCallTimeSec(event.log_time()); - float y = static_cast(event.type) + kEventTypeOffset; - checks_by_cp_id[event.candidate_pair_id].points.emplace_back(x, y); - } - - // TODO(qingsi): The same issue as in CreateIceCandidatePairConfigGraph. - for (auto& kv : checks_by_cp_id) { - plot->AppendTimeSeries(std::move(kv.second)); - } - - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 4, "Connectivity State", kBottomMargin, - kTopMargin); - plot->SetTitle("[IceEventLog] ICE connectivity checks"); - - plot->SetYAxisTickLabels( - {{static_cast(IceCandidatePairEventType::kCheckSent) + - kEventTypeOffset, - "CHECK SENT"}, - {static_cast(IceCandidatePairEventType::kCheckReceived) + - kEventTypeOffset, - "CHECK RECEIVED"}, - {static_cast(IceCandidatePairEventType::kCheckResponseSent) + - kEventTypeOffset, - "RESPONSE SENT"}, - {static_cast(IceCandidatePairEventType::kCheckResponseReceived) + - kEventTypeOffset, - "RESPONSE RECEIVED"}}); -} - -void EventLogAnalyzer::CreateDtlsTransportStateGraph(Plot* plot) const { - TimeSeries states("DTLS Transport State", LineStyle::kNone, - PointStyle::kHighlight); - for (const auto& event : parsed_log_.dtls_transport_states()) { - float x = config_.GetCallTimeSec(event.log_time()); - float y = static_cast(event.dtls_transport_state); - states.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(states)); - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, static_cast(DtlsTransportState::kNumValues), - "Transport State", kBottomMargin, kTopMargin); - plot->SetTitle("DTLS Transport State"); - plot->SetYAxisTickLabels( - {{static_cast(DtlsTransportState::kNew), "NEW"}, - {static_cast(DtlsTransportState::kConnecting), "CONNECTING"}, - {static_cast(DtlsTransportState::kConnected), "CONNECTED"}, - {static_cast(DtlsTransportState::kClosed), "CLOSED"}, - {static_cast(DtlsTransportState::kFailed), "FAILED"}}); -} - -void EventLogAnalyzer::CreateDtlsWritableStateGraph(Plot* plot) const { - TimeSeries writable("DTLS Writable", LineStyle::kNone, - PointStyle::kHighlight); - for (const auto& event : parsed_log_.dtls_writable_states()) { - float x = config_.GetCallTimeSec(event.log_time()); - float y = static_cast(event.writable); - writable.points.emplace_back(x, y); - } - plot->AppendTimeSeries(std::move(writable)); - plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), - "Time (s)", kLeftMargin, kRightMargin); - plot->SetSuggestedYAxis(0, 1, "Writable", kBottomMargin, kTopMargin); - plot->SetTitle("DTLS Writable State"); + webrtc::CreateSenderAndReceiverReportPlot(direction, fy, title, yaxis_label, + parsed_log_, config_, plot); } } // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.h b/rtc_tools/rtc_event_log_visualizer/analyzer.h index ca729b3cbcb..60d2244e0e7 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.h +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.h @@ -11,13 +11,13 @@ #ifndef RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZER_H_ #define RTC_TOOLS_RTC_EVENT_LOG_VISUALIZER_ANALYZER_H_ -#include #include #include +#include #include #include -#include "api/environment/environment.h" +#include "absl/strings/string_view.h" #include "api/function_view.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" @@ -27,18 +27,22 @@ namespace webrtc { +class LazyNetEqSimulator; + class EventLogAnalyzer { struct PlotDeclaration { - PlotDeclaration(const std::string& label, std::function f) + PlotDeclaration(const std::string& label, + std::function f) : label(label), plot_func(f) {} const std::string label; // TODO(terelius): Add a help text/explanation. - const std::function plot_func; + const std::function plot_func; }; class PlotMap { public: - void RegisterPlot(const std::string& label, std::function f) { + void RegisterPlot(const std::string& label, + std::function f) { for (const auto& plot : plots_) { RTC_DCHECK(plot.label != label) << "Can't use the same label for multiple plots"; @@ -46,6 +50,12 @@ class EventLogAnalyzer { plots_.push_back({label, f}); } + void RegisterPlot(const std::string& label, std::function f) { + RegisterPlot(label, [f, label](PlotCollection* collection) { + f(collection->AppendNewPlot(label)); + }); + } + std::vector::const_iterator begin() const { return plots_.begin(); } @@ -58,13 +68,12 @@ class EventLogAnalyzer { }; public: - // The EventLogAnalyzer keeps a reference to the ParsedRtcEventLogNew for the - // duration of its lifetime. The ParsedRtcEventLogNew must not be destroyed or + // The EventLogAnalyzer keeps a reference to the ParsedRtcEventLog for the + // duration of its lifetime. The ParsedRtcEventLog must not be destroyed or // modified while the EventLogAnalyzer is being used. EventLogAnalyzer(const ParsedRtcEventLog& log, bool normalize_time); - EventLogAnalyzer(const Environment& env, - const ParsedRtcEventLog& log, - const AnalyzerConfig& config); + EventLogAnalyzer(const ParsedRtcEventLog& log, const AnalyzerConfig& config); + ~EventLogAnalyzer(); void CreateGraphsByName(const std::vector& names, PlotCollection* collection) const; @@ -122,6 +131,7 @@ class EventLogAnalyzer { void CreateScreamRefWindowGraph(Plot* plot) const; void CreateScreamDelayEstimateGraph(Plot* plot) const; void CreateGoogCcSimulationGraph(Plot* plot) const; + void CreateScreamSimulationDelayGraph(Plot* plot) const; void CreateScreamSimulationBitrateGraph(Plot* plot) const; void CreateScreamSimulationRefWindowGraph(Plot* plot) const; void CreateScreamSimulationRatiosGraph(Plot* plot) const; @@ -148,23 +158,17 @@ class EventLogAnalyzer { void CreateTriageNotifications() const; void PrintNotifications(FILE* file) const; - private: - template - void CreateAccumulatedPacketsTimeSeries(Plot* plot, - const IterableType& packets, - const std::string& label) const; - void CreateEcnFeedbackGraph(Plot* plot, PacketDirection direction) const; + void SetNetEqReplacementFile(absl::string_view replacement_file_name, + int file_sample_rate_hz); - const Environment env_; + private: const ParsedRtcEventLog& parsed_log_; - // A list of SSRCs we are interested in analysing. - // If left empty, all SSRCs will be considered relevant. - std::vector desired_ssrc_; - AnalyzerConfig config_; PlotMap plots_; + + std::unique_ptr neteq_simulator_; }; } // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer_bindings.cc b/rtc_tools/rtc_event_log_visualizer/analyzer_bindings.cc index fa93031ff1f..4aed263a98a 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer_bindings.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyzer_bindings.cc @@ -64,16 +64,10 @@ void analyze_rtc_event_log(const char* log_contents, return; } - webrtc::AnalyzerConfig config; + webrtc::AnalyzerConfig config(webrtc::CreateEnvironment(), parsed_log, + /*normalize_time=*/true); config.window_duration_ = webrtc::TimeDelta::Millis(250); config.step_ = webrtc::TimeDelta::Millis(10); - if (!parsed_log.start_log_events().empty()) { - config.rtc_to_utc_offset_ = parsed_log.start_log_events()[0].utc_time() - - parsed_log.start_log_events()[0].log_time(); - } - config.normalize_time_ = true; - config.begin_time_ = parsed_log.first_timestamp(); - config.end_time_ = parsed_log.last_timestamp(); if (config.end_time_ < config.begin_time_) { std::cerr << "Log end time " << config.end_time_.ms() << " not after begin time " << config.begin_time_.ms() @@ -82,8 +76,7 @@ void analyze_rtc_event_log(const char* log_contents, return; } - webrtc::EventLogAnalyzer analyzer(webrtc::CreateEnvironment(), parsed_log, - config); + webrtc::EventLogAnalyzer analyzer(parsed_log, config); analyzer.InitializeMapOfNamedGraphs(/*show_detector_state=*/false, /*show_alr_state=*/false, /*show_link_capacity=*/false); diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc b/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc index c1cf7dc6b76..559ad78b4a5 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyzer_common.cc @@ -11,10 +11,14 @@ #include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" +#include #include #include +#include #include "logging/rtc_event_log/rtc_event_log_parser.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { @@ -86,4 +90,64 @@ std::string GetLayerName(LayerDescription layer) { return name.str(); } +std::string SsrcToString(uint32_t ssrc) { + StringBuilder ss; + ss << "SSRC " << ssrc; + return ss.Release(); +} + +// Checks whether an SSRC is contained in the list of desired SSRCs. +// Note that an empty SSRC list matches every SSRC. +bool MatchingSsrc(uint32_t ssrc, const std::vector& desired_ssrc) { + if (desired_ssrc.empty()) + return true; + return std::find(desired_ssrc.begin(), desired_ssrc.end(), ssrc) != + desired_ssrc.end(); +} + +// Computes the difference `later` - `earlier` where `later` and `earlier` +// are counters that wrap at `modulus`. The difference is chosen to have the +// least absolute value. For example if `modulus` is 8, then the difference will +// be chosen in the range [-3, 4]. If `modulus` is 9, then the difference will +// be in [-4, 4]. +int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) { + RTC_DCHECK_LE(1, modulus); + RTC_DCHECK_LT(later, modulus); + RTC_DCHECK_LT(earlier, modulus); + int64_t difference = + static_cast(later) - static_cast(earlier); + int64_t max_difference = modulus / 2; + int64_t min_difference = max_difference - modulus + 1; + if (difference > max_difference) { + difference -= modulus; + } + if (difference < min_difference) { + difference += modulus; + } + if (difference > max_difference / 2 || difference < min_difference / 2) { + RTC_LOG(LS_WARNING) << "Difference between" << later << " and " << earlier + << " expected to be in the range (" + << min_difference / 2 << "," << max_difference / 2 + << ") but is " << difference + << ". Correct unwrapping is uncertain."; + } + return difference; +} + +std::string GetDirectionAsString(PacketDirection direction) { + if (direction == kIncomingPacket) { + return "Incoming"; + } else { + return "Outgoing"; + } +} + +std::string GetDirectionAsShortString(PacketDirection direction) { + if (direction == kIncomingPacket) { + return "In"; + } else { + return "Out"; + } +} + } // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer_common.h b/rtc_tools/rtc_event_log_visualizer/analyzer_common.h index d2698f84b1a..4ef2e676c10 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer_common.h +++ b/rtc_tools/rtc_event_log_visualizer/analyzer_common.h @@ -15,7 +15,10 @@ #include #include #include +#include +#include "api/environment/environment.h" +#include "api/environment/environment_factory.h" #include "api/function_view.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -33,6 +36,20 @@ constexpr float kTopMargin = 0.05f; class AnalyzerConfig { public: + AnalyzerConfig() : normalize_time_(true), env_(CreateEnvironment()) {} + + AnalyzerConfig(const Environment& env, + const ParsedRtcEventLog& parsed_log, + bool normalize_time) + : normalize_time_(normalize_time), env_(env) { + begin_time_ = parsed_log.first_timestamp(); + end_time_ = parsed_log.last_timestamp(); + if (!parsed_log.start_log_events().empty()) { + rtc_to_utc_offset_ = parsed_log.start_log_events().front().utc_time() - + parsed_log.start_log_events().front().log_time(); + } + } + float GetCallTimeSec(Timestamp timestamp) const { Timestamp offset = normalize_time_ ? begin_time_ : Timestamp::Zero(); return static_cast((timestamp - offset).us()) / 1000000; @@ -66,6 +83,8 @@ class AnalyzerConfig { Timestamp end_time_ = Timestamp::MinusInfinity(); TimeDelta rtc_to_utc_offset_ = TimeDelta::Zero(); bool normalize_time_; + std::vector desired_ssrc_; + const Environment env_; }; struct LayerDescription { @@ -98,8 +117,19 @@ bool IsAudioSsrc(const ParsedRtcEventLog& parsed_log, std::string GetStreamName(const ParsedRtcEventLog& parsed_log, PacketDirection direction, uint32_t ssrc); +std::string SsrcToString(uint32_t ssrc); + std::string GetLayerName(LayerDescription layer); +std::string GetDirectionAsString(PacketDirection direction); +std::string GetDirectionAsShortString(PacketDirection direction); + +int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus); + +// Checks whether an SSRC is contained in the list of desired SSRCs. +// Note that an empty SSRC list matches every SSRC. +bool MatchingSsrc(uint32_t ssrc, const std::vector& desired_ssrc); + // For each element in data_view, use `f()` to extract a y-coordinate and // store the result in a TimeSeries. template diff --git a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc index 2797b0ce0c8..e52e1313951 100644 --- a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc +++ b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.cc @@ -32,10 +32,12 @@ namespace webrtc { LogScreamSimulation::LogScreamSimulation(const Config& config, const Environment& env) - : env_(env), send_rate_tracker_(config.rate_window) { // Scream is recreated if candidates change. scream_.emplace(env_); + scream_->SetTargetBitrateConstraints( + /*min=*/DataRate::Zero(), /*max=*/DataRate::PlusInfinity(), + /*start=*/DataRate::KilobitsPerSec(300)); } void LogScreamSimulation::ProcessUntil(Timestamp to_time) { @@ -69,7 +71,15 @@ void LogScreamSimulation::OnPacketSent(const LoggedPacketInfo& packet) { std::optional packet_info = transport_feedback_.ProcessSentPacket(sent_packet); if (packet_info.has_value()) { + send_window_usage_ = State::kBelowRefWindow; + if (packet_info->data_in_flight >= scream_->ref_window()) { + send_window_usage_ = State::kAboveRefWindow; + } + if (packet_info->data_in_flight > scream_->max_data_in_flight()) { + send_window_usage_ = State::kAboveScreamMax; + } scream_->OnPacketSent(packet_info->data_in_flight); + data_in_flight_ = packet_info->data_in_flight; } } @@ -112,7 +122,9 @@ void LogScreamSimulation::OnIceConfig( // Recreate Scream. This is inline with behaviour in // ScreamNetworkController::OnNetworkRouteChange. scream_.emplace(env_); - scream_->SetFirstTargetRate(DataRate::KilobitsPerSec(300)); + scream_->SetTargetBitrateConstraints( + /*min=*/DataRate::Zero() /*max=*/, DataRate::PlusInfinity(), + /*start=*/DataRate::KilobitsPerSec(300)); local_candidate_type_ = candidate.local_candidate_type; remote_candidate_type_ = candidate.remote_candidate_type; } @@ -156,16 +168,29 @@ void LogScreamSimulation::LogState(const TransportPacketsFeedback& msg) { send_rate_tracker_.Rate(msg.feedback_time).value_or(DataRate::Zero()), .ref_window = scream_->ref_window(), .ref_window_i = scream_->ref_window_i(), + .max_allowed_ref_window = scream_->max_allowed_ref_window(), .max_data_in_flight = scream_->max_data_in_flight(), - .data_in_flight = msg.data_in_flight, - .queue_delay_dev_norm = - scream_->delay_based_congestion_control().queue_delay_dev_norm(), + .data_in_flight = data_in_flight_, + .send_window_usage = send_window_usage_, + .smoothed_rtt = scream_->delay_based_congestion_control().rtt(), + .queue_delay = scream_->delay_based_congestion_control().queue_delay(), + .queue_delay_min_avg = + scream_->delay_based_congestion_control().queue_delay_min_avg(), + .latency_difference_avg = + scream_->delay_based_congestion_control().latency_difference_avg(), + .ref_window_scale_factor_due_to_avg_min_delay = + scream_->delay_based_congestion_control() + .ref_window_scale_factor_due_to_avg_min_delay(), + .ref_window_scale_factor_due_to_latency_difference = + scream_->delay_based_congestion_control() + .ref_window_scale_factor_due_to_latency_difference(), + .ref_window_scale_factor_close_to_ref_window_i = + scream_->ref_window_scale_factor_close_to_ref_window_i(), + .ref_window_combined_increase_scale_factor = + scream_->last_ref_window_increase_scale_factor(), .l4s_alpha = scream_->l4s_alpha(), .l4s_alpha_v = scream_->delay_based_congestion_control().l4s_alpha_v(), - .ref_window_delay_increase_scale = - scream_->delay_based_congestion_control().IsQueueDelayDetected() - ? 0.0 - : scream_->delay_based_congestion_control().scale_increase()}); + }); } } // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h index 102926df8d0..252880a83f4 100644 --- a/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h +++ b/rtc_tools/rtc_event_log_visualizer/log_scream_simulation.h @@ -32,7 +32,13 @@ namespace webrtc { class LogScreamSimulation { public: + // State of Scream at the time when a feedback report has been processed. struct State { + enum SendWindowUsage { + kBelowRefWindow, + kAboveRefWindow, + kAboveScreamMax, + }; Timestamp time; DataRate target_rate = DataRate::Zero(); @@ -41,14 +47,26 @@ class LogScreamSimulation { DataSize ref_window = DataSize::Zero(); DataSize ref_window_i = DataSize::Zero(); + DataSize max_allowed_ref_window = DataSize::Zero(); DataSize max_data_in_flight = DataSize::Zero(); + // Data in flight after last packet was sent before the state was captured. DataSize data_in_flight = DataSize::Zero(); - - double queue_delay_dev_norm; + // How the send window have been utilized. Based on data in flight when the + // last packet was sent before the state was captured. + SendWindowUsage send_window_usage = kBelowRefWindow; + + TimeDelta smoothed_rtt = TimeDelta::Zero(); + TimeDelta queue_delay = TimeDelta::Zero(); + TimeDelta queue_delay_min_avg = TimeDelta::Zero(); + TimeDelta latency_difference_avg = TimeDelta::Zero(); + + double queue_delay_dev_norm = 0; + double ref_window_scale_factor_due_to_avg_min_delay = 0.0; + double ref_window_scale_factor_due_to_latency_difference = 0.0; + double ref_window_scale_factor_close_to_ref_window_i = 0.0; + double ref_window_combined_increase_scale_factor = 0.0; double l4s_alpha = 0.0; double l4s_alpha_v = 0.0; - - double ref_window_delay_increase_scale = 0.0; }; struct Config { @@ -77,6 +95,8 @@ class LogScreamSimulation { Timestamp last_process_ = Timestamp::MinusInfinity(); TransportFeedbackAdapter transport_feedback_; BitrateTracker send_rate_tracker_; + State::SendWindowUsage send_window_usage_ = State::kBelowRefWindow; + DataSize data_in_flight_ = DataSize::Zero(); // With RFC 8888, transport sequence numbers are not stored per packet. // Instead, we generate one. diff --git a/rtc_tools/rtc_event_log_visualizer/main.cc b/rtc_tools/rtc_event_log_visualizer/main.cc index 03512b185bc..f299d9a318a 100644 --- a/rtc_tools/rtc_event_log_visualizer/main.cc +++ b/rtc_tools/rtc_event_log_visualizer/main.cc @@ -8,15 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ - #include #include #include #include #include -#include #include -#include #include #include "absl/algorithm/container.h" @@ -29,13 +26,11 @@ #include "api/environment/environment.h" #include "api/environment/environment_factory.h" #include "api/field_trials.h" -#include "api/neteq/neteq.h" #include "api/units/time_delta.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_tools/rtc_event_log_visualizer/alerts.h" -#include "rtc_tools/rtc_event_log_visualizer/analyze_audio.h" #include "rtc_tools/rtc_event_log_visualizer/analyzer.h" #include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h" #include "rtc_tools/rtc_event_log_visualizer/conversational_speech_en.h" @@ -192,17 +187,11 @@ int main(int argc, char* argv[]) { } } - webrtc::AnalyzerConfig config; + webrtc::AnalyzerConfig config(env, parsed_log, + absl::GetFlag(FLAGS_normalize_time)); config.window_duration_ = webrtc::TimeDelta::Millis(absl::GetFlag(FLAGS_averaging_window)); config.step_ = webrtc::TimeDelta::Millis(absl::GetFlag(FLAGS_averaging_step)); - if (!parsed_log.start_log_events().empty()) { - config.rtc_to_utc_offset_ = parsed_log.start_log_events()[0].utc_time() - - parsed_log.start_log_events()[0].log_time(); - } - config.normalize_time_ = absl::GetFlag(FLAGS_normalize_time); - config.begin_time_ = parsed_log.first_timestamp(); - config.end_time_ = parsed_log.last_timestamp(); if (config.end_time_ < config.begin_time_) { RTC_LOG(LS_WARNING) << "Log end time " << config.end_time_ << " not after begin time " << config.begin_time_ @@ -225,10 +214,11 @@ int main(int argc, char* argv[]) { has_generated_wav_file = true; } - webrtc::EventLogAnalyzer analyzer(env, parsed_log, config); + webrtc::EventLogAnalyzer analyzer(parsed_log, config); analyzer.InitializeMapOfNamedGraphs(absl::GetFlag(FLAGS_show_detector_state), absl::GetFlag(FLAGS_show_alr_state), absl::GetFlag(FLAGS_show_link_capacity)); + analyzer.SetNetEqReplacementFile(wav_path, 48000); // Flag replacements std::map> flag_aliases = { @@ -261,7 +251,7 @@ int main(int argc, char* argv[]) { {"incoming_bitrate", "outgoing_bitrate", "incoming_ecn_feedback", "outgoing_ecn_feedback"}}, {"scream", - {"scream_delay_estimates", "scream_ref_window", + {"scream_ref_window", "simulated_scream_delay", "simulated_scream_bitrates", "simulated_scream_ref_window", "simulated_scream_ratios", "network_delay_feedback", "pacer_delay"}}}; @@ -272,11 +262,6 @@ int main(int argc, char* argv[]) { // TODO(terelius): Also print a help text. std::cerr << " " << plot_name; } - // The following flags don't fit the model used for the other plots. - for (const auto& plot_name : flag_aliases["simulated_neteq_stats"]) { - std::cerr << " " << plot_name; - } - std::cerr << std::endl; std::cerr << "List of plot aliases (for use with the --plot flag):" << std::endl; @@ -301,13 +286,7 @@ int main(int argc, char* argv[]) { std::vector plot_flags = StrSplit(absl::GetFlag(FLAGS_plot), ","); std::vector plot_names; - const std::vector known_analyzer_plots = - analyzer.GetGraphNames(); - const std::vector known_neteq_plots = - flag_aliases["simulated_neteq_stats"]; - std::vector all_known_plots = known_analyzer_plots; - all_known_plots.insert(all_known_plots.end(), known_neteq_plots.begin(), - known_neteq_plots.end()); + const std::vector all_known_plots = analyzer.GetGraphNames(); for (const std::string& flag : plot_flags) { if (flag == "all") { plot_names = all_known_plots; @@ -335,115 +314,6 @@ int main(int argc, char* argv[]) { webrtc::PlotCollection collection; analyzer.CreateGraphsByName(plot_names, &collection); - // The simulated neteq charts are treated separately because they have a - // different behavior compared to all other plots. In particular, the neteq - // plots - // * cache the simulation results between different plots - // * open and read files - // * dont have a 1-to-1 mapping between IDs and charts. - std::optional neteq_stats; - if (absl::c_find(plot_names, "simulated_neteq_expand_rate") != - plot_names.end()) { - if (!neteq_stats) { - neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000, - field_trials); - } - webrtc::CreateNetEqNetworkStatsGraph( - parsed_log, config, *neteq_stats, - [](const webrtc::NetEqNetworkStatistics& stats) { - return stats.expand_rate / 16384.f; - }, - "Expand rate", collection.AppendNewPlot("simulated_neteq_expand_rate")); - } - if (absl::c_find(plot_names, "simulated_neteq_speech_expand_rate") != - plot_names.end()) { - if (!neteq_stats) { - neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000, - field_trials); - } - webrtc::CreateNetEqNetworkStatsGraph( - parsed_log, config, *neteq_stats, - [](const webrtc::NetEqNetworkStatistics& stats) { - return stats.speech_expand_rate / 16384.f; - }, - "Speech expand rate", - collection.AppendNewPlot("simulated_neteq_speech_expand_rate")); - } - if (absl::c_find(plot_names, "simulated_neteq_accelerate_rate") != - plot_names.end()) { - if (!neteq_stats) { - neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000, - field_trials); - } - webrtc::CreateNetEqNetworkStatsGraph( - parsed_log, config, *neteq_stats, - [](const webrtc::NetEqNetworkStatistics& stats) { - return stats.accelerate_rate / 16384.f; - }, - "Accelerate rate", - collection.AppendNewPlot("simulated_neteq_accelerate_rate")); - } - if (absl::c_find(plot_names, "simulated_neteq_preemptive_rate") != - plot_names.end()) { - if (!neteq_stats) { - neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000, - field_trials); - } - webrtc::CreateNetEqNetworkStatsGraph( - parsed_log, config, *neteq_stats, - [](const webrtc::NetEqNetworkStatistics& stats) { - return stats.preemptive_rate / 16384.f; - }, - "Preemptive rate", - collection.AppendNewPlot("simulated_neteq_preemptive_rate")); - } - if (absl::c_find(plot_names, "simulated_neteq_concealment_events") != - plot_names.end()) { - if (!neteq_stats) { - neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000, - field_trials); - } - webrtc::CreateNetEqLifetimeStatsGraph( - parsed_log, config, *neteq_stats, - [](const webrtc::NetEqLifetimeStatistics& stats) { - return static_cast(stats.concealment_events); - }, - "Concealment events", - collection.AppendNewPlot("simulated_neteq_concealment_events")); - } - if (absl::c_find(plot_names, "simulated_neteq_preferred_buffer_size") != - plot_names.end()) { - if (!neteq_stats) { - neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000, - field_trials); - } - webrtc::CreateNetEqNetworkStatsGraph( - parsed_log, config, *neteq_stats, - [](const webrtc::NetEqNetworkStatistics& stats) { - return stats.preferred_buffer_size_ms; - }, - "Preferred buffer size (ms)", - collection.AppendNewPlot("simulated_neteq_preferred_buffer_size")); - } - - // The model we use for registering plots assumes that the each plot label - // can be mapped to a lambda that will produce exactly one plot. The - // simulated_neteq_jitter_buffer_delay plot doesn't fit this model since it - // creates multiple plots, and would need some state kept between the lambda - // calls. - if (absl::c_find(plot_names, "simulated_neteq_jitter_buffer_delay") != - plot_names.end()) { - if (!neteq_stats) { - neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000, - field_trials); - } - for (auto it = neteq_stats->cbegin(); it != neteq_stats->cend(); ++it) { - webrtc::CreateAudioJitterBufferGraph( - parsed_log, config, it->first, it->second.get(), - collection.AppendNewPlot("simulated_neteq_jitter_buffer_delay")); - } - } - collection.SetCallTimeToUtcOffsetMs(config.CallTimeToUtcOffsetMs()); if (absl::GetFlag(FLAGS_protobuf_output)) { diff --git a/rtc_tools/rtc_event_log_visualizer/plot_base.h b/rtc_tools/rtc_event_log_visualizer/plot_base.h index df7a2cb23ad..99160d88a89 100644 --- a/rtc_tools/rtc_event_log_visualizer/plot_base.h +++ b/rtc_tools/rtc_event_log_visualizer/plot_base.h @@ -17,7 +17,6 @@ #include #include -#include "absl/base/attributes.h" #include "absl/strings/string_view.h" // Generated at build-time by the protobuf compiler. @@ -96,15 +95,9 @@ struct IntervalSeries { }; // A container that represents a general graph, with axes, title and one or -// more data series. A subclass should define the output format by overriding -// the Draw() method. +// more data series. class Plot { public: - virtual ~Plot() {} - - ABSL_DEPRECATED("Use PrintPythonCode() or ExportProtobuf() instead.") - virtual void Draw() {} - // Sets the lower x-axis limit to min_value (if left_margin == 0). // Sets the upper x-axis limit to max_value (if right_margin == 0). // The margins are measured as fractions of the interval @@ -167,12 +160,10 @@ class Plot { // Otherwise, the call has no effect and the timeseries is destroyed. void AppendTimeSeriesIfNotEmpty(TimeSeries&& time_series); - // Replaces PythonPlot::Draw() void PrintPythonCode( bool show_grid = false, absl::string_view figure_output_path = absl::string_view()) const; - // Replaces ProtobufPlot::Draw() void ExportProtobuf(analytics::Chart* chart) const; protected: @@ -191,26 +182,19 @@ class Plot { class PlotCollection { public: - virtual ~PlotCollection() {} - - ABSL_DEPRECATED("Use PrintPythonCode() or ExportProtobuf() instead.") - virtual void Draw() {} - - virtual Plot* AppendNewPlot(); + Plot* AppendNewPlot(); - virtual Plot* AppendNewPlot(absl::string_view); + Plot* AppendNewPlot(absl::string_view); void SetCallTimeToUtcOffsetMs(int64_t calltime_to_utc_ms) { calltime_to_utc_ms_ = calltime_to_utc_ms; } - // Replaces PythonPlotCollection::Draw() void PrintPythonCode( bool shared_xaxis, bool show_grid_on_all_plots, absl::string_view figure_output_path = absl::string_view()) const; - // Replaces ProtobufPlotCollections::Draw() void ExportProtobuf(analytics::ChartCollection* collection) const; protected: diff --git a/rtc_tools/unpack_aecdump/OWNERS b/rtc_tools/unpack_aecdump/OWNERS index 3ef412054e4..45953af31ea 100644 --- a/rtc_tools/unpack_aecdump/OWNERS +++ b/rtc_tools/unpack_aecdump/OWNERS @@ -1,3 +1,2 @@ -alessiob@webrtc.org peah@webrtc.org saza@webrtc.org diff --git a/rtc_tools/unpack_aecdump/unpack.cc b/rtc_tools/unpack_aecdump/unpack.cc index ede55a50c28..08e49e169c6 100644 --- a/rtc_tools/unpack_aecdump/unpack.cc +++ b/rtc_tools/unpack_aecdump/unpack.cc @@ -97,6 +97,12 @@ ABSL_FLAG(bool, fprintf(settings_file, " " #field_name ": %f\n", msg.field_name()); \ } +#define PRINT_CONFIG_STRING(field_name) \ + if (msg.has_##field_name()) { \ + fprintf(settings_file, " " #field_name ": %s\n", \ + msg.field_name().c_str()); \ + } + namespace webrtc { using audioproc::Event; @@ -507,9 +513,6 @@ int do_main(int argc, char* argv[]) { PRINT_CONFIG(aec_drift_compensation_enabled); PRINT_CONFIG(aec_extended_filter_enabled); PRINT_CONFIG(aec_suppression_level); - PRINT_CONFIG(aecm_enabled); - PRINT_CONFIG(aecm_comfort_noise_enabled); - PRINT_CONFIG(aecm_routing_mode); PRINT_CONFIG(agc_enabled); PRINT_CONFIG(agc_mode); PRINT_CONFIG(agc_limiter_enabled); @@ -520,11 +523,9 @@ int do_main(int argc, char* argv[]) { PRINT_CONFIG(transient_suppression_enabled); PRINT_CONFIG(pre_amplifier_enabled); PRINT_CONFIG_FLOAT(pre_amplifier_fixed_gain_factor); + PRINT_CONFIG_STRING(experiments_description); + PRINT_CONFIG_STRING(api_config_string); - if (msg.has_experiments_description()) { - fprintf(settings_file, " experiments_description: %s\n", - msg.experiments_description().c_str()); - } } else if (event_msg.type() == Event::INIT) { if (!event_msg.has_init()) { printf("Corrupt input file: Init missing.\n"); diff --git a/rtc_tools/video_encoder/encoded_image_file_writer.cc b/rtc_tools/video_encoder/encoded_image_file_writer.cc index 5b1b12f3b68..68ca11b03c8 100644 --- a/rtc_tools/video_encoder/encoded_image_file_writer.cc +++ b/rtc_tools/video_encoder/encoded_image_file_writer.cc @@ -14,7 +14,6 @@ #include #include "api/video/encoded_image.h" -#include "api/video/video_frame_type.h" #include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/video_codec.h" #include "modules/video_coding/svc/scalability_mode_util.h" @@ -22,7 +21,6 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" -#include "rtc_base/system/file_wrapper.h" namespace webrtc { namespace test { @@ -54,8 +52,7 @@ EncodedImageFileWriter::EncodedImageFileWriter( << j << ".ivf"; decode_target_writers_.emplace_back(std::make_pair( - IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(name.str()), 0), - name.str())); + IvfFileWriter::Wrap(name.str(), /*byte_limit=*/0), name.str())); } } } @@ -75,8 +72,7 @@ int EncodedImageFileWriter::Write(const EncodedImage& encoded_image) { RTC_CHECK_LT(temporal_index, temporal_layers_); if (spatial_index == 0) { - is_base_layer_key_frame = - (encoded_image._frameType == VideoFrameType::kVideoFrameKey); + is_base_layer_key_frame_ = encoded_image.IsKey(); } switch (inter_layer_pred_mode_) { @@ -117,7 +113,7 @@ int EncodedImageFileWriter::Write(const EncodedImage& encoded_image) { } // Write to higher spatial layers only if key frame. - if (!is_base_layer_key_frame) { + if (!is_base_layer_key_frame_) { break; } } diff --git a/rtc_tools/video_encoder/encoded_image_file_writer.h b/rtc_tools/video_encoder/encoded_image_file_writer.h index 927e679e935..0bfaa263dc1 100644 --- a/rtc_tools/video_encoder/encoded_image_file_writer.h +++ b/rtc_tools/video_encoder/encoded_image_file_writer.h @@ -42,7 +42,7 @@ class EncodedImageFileWriter final { int temporal_layers_ = 0; InterLayerPredMode inter_layer_pred_mode_ = InterLayerPredMode::kOff; - bool is_base_layer_key_frame = false; + bool is_base_layer_key_frame_ = false; std::vector decode_target_writers_; }; diff --git a/rtc_tools/video_encoder/video_encoder.cc b/rtc_tools/video_encoder/video_encoder.cc index bf14e1da6ac..bab90e522c6 100644 --- a/rtc_tools/video_encoder/video_encoder.cc +++ b/rtc_tools/video_encoder/video_encoder.cc @@ -114,7 +114,7 @@ std::string ToString(const EncodedImage& encoded_image) { char buffer[1024]; SimpleStringBuilder ss(buffer); - ss << VideoFrameTypeToString(encoded_image._frameType) + ss << VideoFrameTypeToString(encoded_image.frame_type()) << ", size=" << encoded_image.size() << ", qp=" << encoded_image.qp_ << ", timestamp=" << encoded_image.RtpTimestamp(); @@ -342,6 +342,10 @@ class BitstreamProcessor final : public EncodedImageCallback, return Result(Result::Error::OK); } + void OnFrameDropped(uint32_t /*rtp_timestamp*/, + int /*spatial_id*/, + bool /*is_end_of_temporal_unit*/) override {} + VideoCodec video_codec_setting_; int32_t frames_ = 0; diff --git a/rtc_tools/video_file_reader.cc b/rtc_tools/video_file_reader.cc index 1d2e87e7228..ce33bb25609 100644 --- a/rtc_tools/video_file_reader.cc +++ b/rtc_tools/video_file_reader.cc @@ -16,8 +16,10 @@ #include #include #include +#include #include +#include "absl/cleanup/cleanup.h" #include "absl/strings/match.h" #include "api/make_ref_counted.h" #include "api/scoped_refptr.h" @@ -33,6 +35,11 @@ namespace test { namespace { +// A limit to prevent infinite loops or OOM if a Y4M file has a malformed +// header. 256 bytes is plenty for any valid Y4M header but small enough +// to be safe. +constexpr int kMaxHeaderSize = 256; + bool ReadBytes(uint8_t* dst, size_t n, FILE* file) { return fread(reinterpret_cast(dst), /* size= */ 1, n, file) == n; } @@ -128,6 +135,8 @@ scoped_refptr